如何获取navigationItem中rightBarButtonItems的视图位置坐标
基础说明
UIBarButtonItem本身并非UIView子类,没有直接暴露frame、center等坐标属性,它在导航栏上的实际渲染视图完全由UINavigationBar内部管理,不存在一步到位的公开属性可以直接拿到对应视图。
- 弹系统popover场景(官方推荐,完全不用算坐标)
做popover弹出根本不需要手动取CGPoint,苹果早就提供了原生适配接口:
给UIPopoverPresentationController直接指定需要锚定的barButtonItem对象,系统会自动匹配浮层位置、箭头方向,横竖屏切换、分屏模式变化时也会自动跟随按钮调整位置,完全不需要手动处理坐标适配逻辑。
参考实现代码:let popoverVC = UIViewController() popoverVC.modalPresentationStyle = .popover guard let popoverPC = popoverVC.popoverPresentationController else { return } // 直接绑定目标导航按钮作为锚点,无任何手动计算 popoverPC.barButtonItem = infoBarButtonItem popoverPC.permittedArrowDirections = .up present(popoverVC, animated: true) - 需要手动获取坐标的自定义场景
如果你要做自定义浮层、转场动画,确实需要拿到按钮在屏幕上的坐标,可以通过递归遍历导航栏视图层级,匹配对应按钮的承载交互视图,再通过UIKit原生坐标转换API拿到目标坐标系下的CGPoint值:
实现步骤:- 拿到当前导航控制器的
UINavigationBar视图实例 - 递归遍历导航栏的所有子视图,匹配对应barButton的交互承载视图
- 调用
convert(_:from:)方法将视图坐标转换到目标容器的坐标系下
参考实现代码:
func getBarItemCenter(for barItem: UIBarButtonItem, in coordinateView: UIView) -> CGPoint? { guard let navBar = navigationController?.navigationBar else { return nil } // 递归查找对应barButton的实际渲染视图 func findRenderView(in subviews: [UIView]) -> UIView? { for view in subviews { // 匹配承载barButton交互的UIControl实例 if let control = view as? UIControl, control.matches(barItem) { return control } if let matchedView = findRenderView(in: view.subviews) { return matchedView } } return nil } guard let renderView = findRenderView(in: navBar.subviews) else { return nil } // 把导航栏坐标系下的视图中心,转换到目标视图的坐标系 return coordinateView.convert(renderView.center, from: navBar) } - 拿到当前导航控制器的
注意事项
- 不要通过KVC直接访问带私有前缀的内部属性(比如
_view)来获取渲染视图,存在被App Store审核拒绝的风险- 必须等导航栏完成布局渲染后再获取视图坐标,否则会拿到frame为0的无效值
- 坐标转换时要明确指定源坐标系和目标坐标系,避免出现浮层位置偏移
内容的提问来源于stack exchange,提问作者achu
相关产品推荐
相关产品推荐

