You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

键盘弹出时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:58:51