如何正确将导航栏中的view.convert UIBarButtonItem框架转换为主窗口坐标

问题描述

我正在尝试为我的用户设置一个新的功能演练,为此,我想在相关的UI元素周围画一个圆。我正在尝试在NavigationBar右上角的UIBarButtonItem周围画一个圆,但是当我将框架坐标转换到主窗口时,数学中的内容不正确,红色圆圈向右偏移太远

在屏幕截图中,我试图在第二个按钮(弹出框指向的那个按钮)周围绘制一个红色圆圈,但是当我转换坐标时,它实际上在第三个按钮周围绘制。当我在第一个按钮(搜索放大镜)上运行相同的代码时,它工作得很好,并且正确绘制了红色圆圈。

Nav bar image with four buttons,third one with a red circle around it,and popover arrow pointing to second button

在我的代码中,我只是从弹出窗口中抓取barButtonItem,并且已经验证它指向正确的按钮。

if let buttonItem = self.popoverPresentationController?.barButtonItem {
        
    if let view = buttonItem.value(forKey: "view") as? UIView,let mainView = containingViewController?.navigationController?.view {

        let viewFrame = view.frame
        print("viewFrame: \(viewFrame)")
            
        // This should be translating coords to the main window.
        let windowRect = view.convert(viewFrame,to: nil)
 
        print("Window RECT \(windowRect)")

        let circlePath = UIBezierPath(ovalIn: windowRect)
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = circlePath.cgPath
                
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.linewidth = 3.0
        mainView.layer.addSublayer(shapeLayer)
     }
  }

我打印出view的框架,这些数字看似正确,但是当它不是导航栏中最左边的按钮时,翻译的windowRect关闭了。

对于搜索按钮(屏幕截图中最左边的一个):

viewFrame: (0.0,0.0,46.0,44.0)
Window RECT (617.0,27.0,44.0)

对于第二个按钮(屏幕快照中的弹出框所指向的那个),Window RECT偏移了108而不是预期的54:

viewFrame: (54.0,44.0)
Window RECT: (725.0,44.0)

每个UIBarButtonItem的宽度为46,我假设两者之间的间隔为8。这使第二组数字的起始x为54.0。但是当转换为“窗口”坐标时,两者之间的差异为108;所以它被整个按钮抵消了我尝试定位第3个和第4个按钮,并且始终以一个完整的按钮宽度关闭。如您所见,红色圆圈位于第三个按钮的中心,但这不是我要突出显示的那个。

在进行mainView时,我尝试转换为to: nil而不是view.convert,但这没什么区别。这些数字完全一样。关于我在做什么错的任何想法,或者这是一个(但是(已知或其他)已知的)错误,我将不得不在我的代码中做一些疯狂的事情才能使其正常工作?

已更新为添加:如果我删除翻译,然后将图层放到我发现的视图的超级视图中,就可以了:

let circlePath = UIBezierPath(ovalIn: viewFrame)
 // code to create shapelayer
view.superview?.layer.addSublayer(highlightItemLayer!)

然后它会显示在正确的位置。我希望它在视图层次结构中更高,以便可以在其他事物之上绘制它;因此仍然希望能够进行翻译。

解决方法

我想我已经解决了这个问题。我将转换行从let windowRect = view.convert(viewFrame,to: nil)更改为使用超级视图,而不仅仅是在视图上:

let windowRect = view.superview?.convert(viewFrame,to: nil)

现在红色圆圈显示在适当的位置(请注意,在创建路径之前,我还通过在windowRect上使用translatedFrame.insetBy(dx: -10,dy: -10)来增加了它的大小。

red circle on correct icon