长按手势识别器位置(在:视图中)未正确计算位置

Long Press Gesture Recognizer location(in: view) not correctly calculating location

我正在使用 UIScrollView 来显示顶部带有各种标记的图像。图像视图有一个 UILongPressGestureRecognizer 检测长按。当检测到长按事件时,我想在该位置创建一个新标记。

我遇到的问题是,当我放大或缩小时,手势识别器 location(in: view) 的位置似乎已关闭。这是我的实施片段:

let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.onLongPress(gesture:)))
self.hostingController.view.addGestureRecognizer(longPressGestureRecognizer)
@objc func onLongPress(gesture: UILongPressGestureRecognizer) {
    switch gesture.state {
        case .began:
            guard let view = gesture.view else { break }
            let location = gesture.location(in: view)
            let pinPointWidth = 32.0
            let pinPointHeight = 42.0
            let x = location.x - (pinPointWidth / 2)
            let y = location.y - pinPointHeight
            let finalLocation = CGPoint(x: x, y: y)
            self.onLongPress(finalLocation)
        default:
            break
    }
}

请注意,我使用的 UIViewControllerRepresentable 包含一个 UIViewController 和一个显示在我的 SwiftUI 视图中的 UIScrollView。也许这可能是造成它的原因。

这是 SwiftUI 代码:

var body: some View {
    UIScrollViewWrapper(scaleFactor: $scaleFactor, onLongPress: onInspectionCreated) {
        ZStack(alignment: .topLeading) {
            Image(uiImage: image)
            ForEach(filteredInspections, id: \.syncToken) { inspection in
               InspectionMarkerView(
                    scaleFactor: scaleFactor,
                    xLocation: CGFloat(inspection.xLocation),
                    yLocation: CGFloat(inspection.yLocation),
                    iconName: iconNameForInspection(inspectionMO: inspection),
                    label: inspection.readableIdPaddedOrNewInspection)
                    .onTapGesture {
                        selectedInspection = inspection
                }
            }
        }
    }
    .clipped()
}

这是一个 link 可重现的示例项目: https://github.com/Kukiwon/sample-project-zoom-long-press-location

这是问题的记录: Link to video

非常感谢任何帮助!

我不使用 SwiftUI,但我在 UIHostingController 实现中看到了一些古怪的东西,似乎有一个特定的怪癖正在打击你。

我将您的滚动视图插入 40 磅,并将主视图设置为红色背景,以便更容易看到发生了什么。

首先,在 sheet_hd 图片的边框周围添加一条 2 像素的蓝线,然后一直滚动到 bottom-left 角。它应该是这样的:

放大时,将滚动条保持在 bottom-left,它看起来像这样:

到目前为止一切顺利 -- 使用 long-press 添加标记按预期工作。

但是,一旦我们缩小到小于 1.0 缩放比例:

我们无法再看到图像的底部边缘。

放大后会更明显:

并且 long-press 位置不正确。

为了进一步说明,如果我们在滚动视图上设置 .clipsToBounds = false,并在图像视图上设置 .alpha = 0.5,我们会看到:

我们可以向上拖动视图以查看底部边缘,但是一旦我们松开触摸就会弹回滚动视图的框架下方。

应该解决这个问题的是使用这个扩展:

// extension to remove safe area from UIHostingController
//  source: 
extension UIHostingController {
    convenience public init(rootView: Content, ignoreSafeArea: Bool) {
        self.init(rootView: rootView)
        
        if ignoreSafeArea {
            disableSafeArea()
        }
    }
    
    func disableSafeArea() {
        guard let viewClass = object_getClass(view) else { return }
        
        let viewSubclassName = String(cString: class_getName(viewClass)).appending("_IgnoreSafeArea")
        if let viewSubclass = NSClassFromString(viewSubclassName) {
            object_setClass(view, viewSubclass)
        }
        else {
            guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return }
            guard let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0) else { return }
            
            if let method = class_getInstanceMethod(UIView.self, #selector(getter: UIView.safeAreaInsets)) {
                let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { _ in
                    return .zero
                }
                class_addMethod(viewSubclass, #selector(getter: UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), method_getTypeEncoding(method))
            }
            
            objc_registerClassPair(viewSubclass)
            object_setClass(view, viewSubclass)
        }
    }
}

然后,在 viewDidLoad() 在你的 UIScrollViewViewController:

override func viewDidLoad() {
    super.viewDidLoad()
    self.view.addSubview(self.scrollView)
    self.pinEdges(of: self.scrollView, to: self.view)
    
    // add this line
    self.hostingController.disableSafeArea()
    
    self.hostingController.willMove(toParent: self)
    self.scrollView.addSubview(self.hostingController.view)
    self.pinEdges(of: self.hostingController.view, to: self.scrollView)
    self.hostingController.didMove(toParent: self)
    self.hostingController.view.alpha = 0
}

快速测试(显然,您需要彻底测试)似乎不错...我们可以一直滚动到底部,long-press 位置又回到了它应该在的位置。