Xcode升级后UITableView单元格按钮点击无响应问题排查
嘿,针对你升级Xcode11后遇到的UITableView单元格点击/按钮无响应、仅部分区域变灰的问题,我帮你梳理几个最可能的原因和解决方案:
1. 重复给按钮绑定Target导致响应异常
你当前在cellForRowAt方法里,每次复用单元格时都会调用addTarget给按钮绑定事件。这种写法在Xcode旧版本可能没问题,但Xcode11之后UIKit的事件响应机制对重复绑定的处理更严格,会导致同一个按钮被绑定多份相同的target-action,最终引发点击事件无法正确触发,甚至完全无响应。
修复方案:
把按钮的事件绑定逻辑移到自定义单元格PostTableViewCell的初始化方法里,用代理/闭包把事件传递给ViewController,避免重复绑定:
首先在PostTableViewCell中添加代理协议:
import UIKit protocol PostCellDelegate: AnyObject { func didTapCommentButton(for post: Post) func didTapFavoriteButton(for post: Post) } class PostTableViewCell: UITableViewCell { weak var delegate: PostCellDelegate? private var currentPost: Post? @IBOutlet weak var btnComment: UIButton! @IBOutlet weak var favoritebutton: UIButton! override func awakeFromNib() { super.awakeFromNib() // 只在初始化时绑定一次事件 btnComment.addTarget(self, action: #selector(commentTapped), for: .touchUpInside) favoritebutton.addTarget(self, action: #selector(favoriteTapped), for: .touchUpInside) } func set(post: Post) { currentPost = post // 这里写你的UI配置逻辑,比如设置标题、内容等 } @objc private func commentTapped() { guard let post = currentPost else { return } delegate?.didTapCommentButton(for: post) } @objc private func favoriteTapped() { guard let post = currentPost else { return } delegate?.didTapFavoriteButton(for: post) } }
然后在HomeViewController中实现代理,并修改cellForRowAt:
extension HomeViewController: PostCellDelegate { func didTapCommentButton(for post: Post) { postKey = post._documentId performSegue(withIdentifier: "toCommentsList", sender: self) } func didTapFavoriteButton(for post: Post) { postKey = post._documentId // 这里写你原来的favupdate逻辑 let userMarkRef = Firestore.firestore().collection("users").document(Auth.auth().currentUser!.uid).collection("marked_posts").document(postKey) let postRef = Firestore.firestore().collection("posts").document(postKey) postRef.getDocument{(document, error) in // 你的原有逻辑... } } } // 修改cellForRowAt中的postCell部分 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.row == 0 && scores1.count == 1 { let cell = tableView.dequeueReusableCell(withIdentifier: "SmileMiles") as! ScoresCellInHomeScreen cell.set(scores: scores1[indexPath.row]) return cell } else if posts.count > (indexPath.row - 1 ) { let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell // 只设置代理,不再重复addTarget cell.delegate = self cell.set(post: posts[indexPath.row - 1 ]) return cell } else { let cell = UITableViewCell() cell.isUserInteractionEnabled = false return cell } }
2. 空单元格逻辑引发的布局/响应冲突
你在cellForRowAt的else分支返回了一个空的UITableViewCell(),虽然理论上numberOfRowsInSection返回的是scores1.count + posts.count,但异步加载数据时如果reload时机不对,可能出现行号超出范围的情况,这个空单元格会干扰tableView的事件响应链,导致部分区域变灰但无响应。
修复方案:
先严格控制行数,确保numberOfRowsInSection的返回值和单元格逻辑完全匹配:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // 只有scores1有数据时才显示顶部固定单元格 let fixedRowCount = scores1.isEmpty ? 0 : 1 return fixedRowCount + posts.count }
然后优化cellForRowAt的逻辑,避免出现超出范围的情况:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if !scores1.isEmpty && indexPath.row == 0 { let cell = tableView.dequeueReusableCell(withIdentifier: "SmileMiles") as! ScoresCellInHomeScreen cell.set(scores: scores1[0]) return cell } else { // 计算正确的post索引 let postIndex = scores1.isEmpty ? indexPath.row : indexPath.row - 1 guard postIndex < posts.count else { // 兜底返回不可交互的占位单元格 let cell = UITableViewCell() cell.isUserInteractionEnabled = false return cell } let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell cell.delegate = self cell.set(post: posts[postIndex]) return cell } }
3. 自动高度设置冲突导致点击区域异常
你在viewWillAppear中设置了tableView.rowHeight = UITableView.automaticDimension,同时又在heightForRowAt中给第一行设置固定高度,其他行返回自动高度。Xcode11之后UITableView的自动高度计算逻辑有调整,这种混合设置可能导致单元格的可点击区域计算错误,出现“一半变灰”的情况。
修复方案:
统一高度配置,保留heightForRowAt的逻辑,去掉viewWillAppear中的rowHeight设置:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tableView.estimatedRowHeight = 200 // 移除这行代码:tableView.rowHeight = UITableView.automaticDimension } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if !scores1.isEmpty && indexPath.row == 0 { return 150 } else { return UITableView.automaticDimension } }
同时确保自定义单元格PostTableViewCell的约束是完整的:从contentView的顶部到底部都有连续的约束,这样自动高度才能正确计算,点击区域也会覆盖整个单元格。
4. 清理无用代码避免潜在干扰
你在viewDidLoad中创建了一个完全无用的AViewController和MyTabBarItem,这段代码不仅没有作用,还可能不小心干扰视图层级或事件响应,建议直接删除:
// 删掉以下无用代码 var AViewController: UIViewController = UIViewController() var MyTabBarItem: UITabBarItem = UITabBarItem(title: nil, image: UIImage(named: "pencil")?.withRenderingMode(UIImage.RenderingMode.alwaysOriginal), selectedImage: UIImage(named: "pencil")) AViewController.tabBarItem = MyTabBarItem
内容的提问来源于stack exchange,提问作者user12008081

