Xcode8.3.3中TableView自定义Cell被识别为UIView问题求助
为什么iOS10里自定义Cell会被识别为UIView?
在iOS 11之前,UITableView的内部视图层级和iOS11+有明显差异——你的MediaCell和gridTableViewCell其实被包裹在一个系统自带的UITableViewCellWrapperView容器里。当你遍历tableView.subviews时,拿到的是这个wrapper视图而非你的自定义Cell实例,所以isKind(of: MediaCell.self)自然匹配失败,看起来就像Cell被识别成了UIView(毕竟wrapper本身就是UIView的子类)。
iOS11之后苹果调整了UITableView的内部实现,移除了这个wrapper视图,所以你能直接在subviews里拿到自定义Cell,这就是你的代码在iOS11模拟器中能正常工作的原因。
你的代码存在的核心问题
直接操作tableView.subviews来移除Cell是完全违背UITableView设计规范的做法。UITableView有一套成熟的Cell复用机制,所有Cell的创建、复用、销毁都应该由系统自身管理。手动修改subviews会破坏复用池的状态,不仅容易引发布局错乱、崩溃等问题,还会导致代码依赖系统内部实现,一旦系统版本更新就失效(就像你遇到的iOS10和iOS11的差异)。
正确的解决方案
要实现「切换分段控制器时移除指定类型的Cell」,本质是让UITableView在刷新时不再显示这些类型的Cell,正确的做法是通过修改数据源来控制:
- 维护一个数据源数组,根据分段控制器的选中状态,填充对应类型的数据(或标记哪些类型的Cell需要显示)。
- 调用
tableView.reloadData(),在tableView(_:cellForRowAt:)方法中,根据当前数据源状态返回对应类型的Cell。
举个简单的实现示例:
// 定义枚举标记当前要显示的Cell类型 enum DisplayCellType { case media case grid case empty } var currentCellType: DisplayCellType = .media var dataSource: [Any] = [] // 根据currentCellType填充对应数据 // 分段控制器切换事件处理 @objc func segmentControlValueChanged(_ sender: UISegmentedControl) { switch sender.selectedSegmentIndex { case 0: currentCellType = .media dataSource = fetchMediaData() // 加载媒体类数据 case 1: currentCellType = .grid dataSource = fetchGridData() // 加载网格类数据 default: currentCellType = .empty dataSource = [] } tableView.reloadData() } // UITableView数据源方法 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dataSource.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { switch currentCellType { case .media: let cell = tableView.dequeueReusableCell(withIdentifier: "MediaCell", for: indexPath) as! MediaCell // 配置MediaCell的数据 return cell case .grid: let cell = tableView.dequeueReusableCell(withIdentifier: "gridTableViewCell", for: indexPath) as! gridTableViewCell // 配置gridTableViewCell的数据 return cell case .empty: let cell = tableView.dequeueReusableCell(withIdentifier: "empty", for: indexPath) as! emptyTableViewCell return cell } }
这种方式完全遵循UITableView的设计逻辑,既不会受系统版本变化影响,也能稳定管理Cell的显示与复用。如果只是需要临时隐藏某些Cell而非移除数据,也可以通过tableView(_:heightForRowAt:)返回0来实现,但同样要基于数据源状态判断,而非手动操作subviews。
内容的提问来源于stack exchange,提问作者user4812550

