键盘弹出时UIPopoverPresentationController的定位问题
解决UIPopoverPresentationController键盘弹出时的位置偏移问题
我之前也碰到过一模一样的坑!当从自定义UITableViewCell弹出带可编辑滚动视图的popover时,键盘弹出后位置乱跳的问题确实头疼,给你梳理几个能解决问题的步骤:
一、先修正初始弹出的锚点设置
你之前把sourceView设为父控制器的view,sourceRect用cell的frame,这其实是位置偏移的根源之一。因为cell的frame是相对于tableView的,当tableView滚动或者键盘弹出导致视图调整时,这个frame的参考系会乱掉。更稳妥的做法是:
- 把
sourceView直接设为被点击的单元格本身 sourceRect用cell的bounds(自身坐标系,不会随外部滚动变化)
代码示例:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let tappedCell = tableView.cellForRow(at: indexPath) else { return } // 初始化你的弹窗控制器 let popoverVC = YourEditableScrollPopoverVC() popoverVC.modalPresentationStyle = .popover if let presenter = popoverVC.popoverPresentationController { presenter.sourceView = tappedCell presenter.sourceRect = tappedCell.bounds presenter.permittedArrowDirections = .up // 按需设置箭头方向,比如指向单元格上方 presenter.delegate = self // 必须设置代理来处理位置重定位 } present(popoverVC, animated: true) }
二、通过代理方法处理键盘弹出时的位置调整
当键盘弹出时,系统会调用UIPopoverPresentationControllerDelegate的willRepositionPopoverToRect方法,这时候我们要手动修正popover的锚点位置——因为此时tableView可能因为键盘弹出自动调整了contentOffset,cell的实际位置已经变了。
实现代理方法:
extension YourParentVC: UIPopoverPresentationControllerDelegate { func popoverPresentationController(_ popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverToRect rect: UnsafeMutablePointer<CGRect>, in view: AutoreleasingUnsafeMutablePointer<UIView>) { // 先拿到当前选中的单元格 guard let selectedIndexPath = tableView.indexPathForSelectedRow, let currentCell = tableView.cellForRow(at: selectedIndexPath) else { return } // 如果你的sourceView是单元格,直接用bounds即可;如果必须用父view,就转成父view坐标系的frame if popoverPresentationController.sourceView === currentCell { rect.pointee = currentCell.bounds } else { let cellFrameInParent = tableView.convert(currentCell.frame, to: self.view) rect.pointee = cellFrameInParent } } }
三、给弹窗里的ScrollView做适配
别忘了给弹窗内的UIScrollView设置内容偏移调整,确保键盘弹出时滚动视图能自动适配:
class YourEditableScrollPopoverVC: UIViewController { @IBOutlet weak var contentScrollView: UIScrollView! override func viewDidLoad() { super.viewDidLoad() // 让scrollView自动适配键盘高度 contentScrollView.contentInsetAdjustmentBehavior = .always } }
额外的兜底方案(如果代理方法不够)
如果上面的方法还是有小偏移,可以监听键盘通知手动更新popover位置:
override func viewDidLoad() { super.viewDidLoad() // 注册键盘通知 NotificationCenter.default.addObserver(self, selector: #selector(updatePopoverPositionOnKeyboard), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(updatePopoverPositionOnKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil) } @objc private func updatePopoverPositionOnKeyboard(_ notification: Notification) { guard let popoverVC = presentedViewController as? YourEditableScrollPopoverVC, let presenter = popoverVC.popoverPresentationController, let selectedCell = tableView.cellForRow(at: tableView.indexPathForSelectedRow!) else { return } // 更新锚点位置 if presenter.sourceView === selectedCell { presenter.sourceRect = selectedCell.bounds } else { presenter.sourceRect = tableView.convert(selectedCell.frame, to: self.view) } // 强制刷新布局 popoverVC.view.layoutIfNeeded() } // 记得销毁时移除监听 deinit { NotificationCenter.default.removeObserver(self) }
内容的提问来源于stack exchange,提问作者Pat Long - Munkii Yebee
相关产品推荐
相关产品推荐

