如何通过应用内购实现UITableView指定行的锁定与解锁?
实现UITableView部分行默认解锁、其余行锁定的方案
嘿,我完全懂你的需求——想要让列表里前两行默认开放,第三行及之后的内容(哪怕超过12行)默认锁定,只有用户完成内购后才解锁全部内容对吧?咱们来调整你的代码就能搞定,下面是具体的修改方案:
问题分析
你当前的代码逻辑是要么全锁、要么全解锁,没有区分哪些行是默认免费的。咱们需要添加对行索引的判断,来实现部分行免费开放的效果。
修改步骤&代码实现
1. 调整单元格锁定状态的显示逻辑
在cellForRowAt方法里,我们要区分免费行和付费行:
- 已购买用户:所有行都隐藏锁定图标
- 未购买用户:前两行隐藏锁定图标,其余行显示
2. 调整行点击的跳转逻辑
在didSelectRowAt方法里:
- 点击前两行:直接跳转到对应内容,无需判断购买状态
- 点击第三行及以后:检查购买状态,未购买则跳转到解锁页面,已购买则正常跳转
下面是修改后的完整代码:
import Foundation import UIKit class TrappingVC: UIViewController { @IBOutlet weak var buildingTableView: UITableView! @IBOutlet weak var settingsButtonItem: UIBarButtonItem! var trapping: [CellObject] = [] // 注意:如果列表行数超过segueIdentifiers的数量,这里要对应扩展数组,避免越界 var segueIdentifiers = ["a", "b", "c", "d"] // 示例:对应前四行的segue //VIEWDIDLOAD override func viewDidLoad() { super.viewDidLoad() //LOAD ARRARYS trapping = createBuildArray() buildingTableView.delegate = self buildingTableView.dataSource = self self.buildingTableView.rowHeight = 100.0 buildingTableView.tableFooterView = UIView() //CELL SEPARATORS buildingTableView.layoutMargins = UIEdgeInsets.zero buildingTableView.separatorInset = UIEdgeInsets.zero buildingTableView.separatorColor = UIColor.black buildingTableView.register(UINib.init(nibName: "TrappingCell", bundle: nil), forCellReuseIdentifier: "TrappingCell") settingsButtonItem.image = UIImage(named: "Settings") } @IBAction func showSettingsClicked(_ sender: Any) { performSegue(withIdentifier: "showSettings", sender: self) } //CREATE ARRAY OF BASIC LESSONS func createBuildArray() -> [CellObject]{ var tempTrapping: [CellObject] = [] let trapping1 = CellObject(image: #imageLiteral(resourceName: "Yellow"), title: "Below") let trapping2 = CellObject(image: #imageLiteral(resourceName: "Yellow"), title: "Side") let trapping3 = CellObject(image: #imageLiteral(resourceName: "Yellow"), title: "Above") // 可以继续添加更多行示例 let trapping4 = CellObject(image: #imageLiteral(resourceName: "Yellow"), title: "Advanced 1") let trapping5 = CellObject(image: #imageLiteral(resourceName: "Yellow"), title: "Advanced 2") tempTrapping.append(trapping1) tempTrapping.append(trapping2) tempTrapping.append(trapping3) tempTrapping.append(trapping4) tempTrapping.append(trapping5) return tempTrapping } } //TABLE extension TrappingVC: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return trapping.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let trappings = trapping[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "TrappingCell") as! TrappingCell cell.trappingTitle.text = trappings.title cell.trappingImage.image = trappings.image // 判断是否已购买 let isPurchased = UserDefaults.standard.bool(forKey: "payment") // 定义免费行的范围:前两行(index从0开始,所以row < 2) let isFreeRow = indexPath.row < 2 // 设置锁定图标显示状态 if isPurchased || isFreeRow { cell.lockedImage.isHidden = true } else { cell.lockedImage.isHidden = false } cell.layoutIfNeeded() cell.layoutMargins = UIEdgeInsets.zero return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let isPurchased = UserDefaults.standard.bool(forKey: "payment") let isFreeRow = indexPath.row < 2 if isFreeRow || isPurchased { // 免费行或已购买,正常跳转 // 注意:要确保segueIdentifiers数组的长度 >= 当前列表行数,避免数组越界 if indexPath.row < segueIdentifiers.count { performSegue(withIdentifier: segueIdentifiers[indexPath.row], sender: self) } } else { // 未购买且是付费行,跳转到解锁页面 let storyboard = UIStoryboard(name: "Main", bundle: nil) let controller = storyboard.instantiateViewController(withIdentifier: "UnlockContentVC") self.navigationController?.pushViewController(controller, animated: true) } tableView.deselectRow(at: indexPath, animated: true) } }
关键改动说明
- 简化购买状态判断:用
UserDefaults.standard.bool(forKey: "payment")替代原来的可选绑定,更简洁,默认值为false(如果key不存在的话)。 - 新增免费行判断:用
indexPath.row < 2明确前两行是免费的,你可以根据需求修改这个数值(比如想要前3行免费就改成row < 3)。 - 完善跳转逻辑:确保免费行不管购买状态都能正常跳转,付费行只有购买后才能访问。
- 数组越界防护:在跳转时判断
indexPath.row < segueIdentifiers.count,避免当列表行数超过segue数组长度时崩溃。
额外提醒
- 确保内购成功后,正确调用
UserDefaults.standard.set(true, forKey: "payment")并同步,这样购买状态才能被正确读取。 - 如果后续要调整免费行的数量,只需要修改
isFreeRow的判断条件即可,非常灵活。
内容的提问来源于stack exchange,提问作者belgrim
相关产品推荐
相关产品推荐

