长按手势识别器位置(在:视图中)未正确计算位置
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 位置又回到了它应该在的位置。
我正在使用 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 位置又回到了它应该在的位置。