iOS UIPanGestureRecognizer:调整灵敏度?
iOS UIPanGestureRecognizer: adjust sensitivity?
我的问题:有没有办法调整UIPanGestureRecognizer的"sensitivity"使其开启'sooner',即在移动较少数量的'pixels'?
我有一个带有 UIImageView 的简单应用程序,以及与此相关的捏合和平移手势识别器,以便用户可以放大图像并手动在图像上绘制。工作正常。
但是,我注意到在用户的手势移动了大约 10 个像素之前,常用的 UIPanGestureRecognizer 不会 return 值 UIGestureRecognizerState.Changed。
示例:这是一张屏幕截图,显示了我尝试绘制的几条线越来越短,并且有一个明显的有限长度,低于该长度没有绘制任何线,因为平移手势识别器永远不会改变状态。
IllustrationOfProgressivelyShorterLines.png
...即,在黄线的右侧,我仍在尝试绘制,并且我的触摸被识别为 touchesMoved 事件,但是 UIPanGestureRecognizer 没有触发它自己的 "Moved" 事件因此什么也没有画出来。
(Note/clarification: 那张图片占据了我 iPad 的整个屏幕,所以即使在没有识别器发生状态变化。就捏合手势识别器生成的变换而言,我们只是 'zoomed in',因此一些 'pixels' 图像占据了大量屏幕。 )
这不是我想要的。关于如何解决它的任何想法?
也许 UIPanGestureRecognizer 的一些 'internal' 参数如果我对它进行子class编辑或类似的,我可以得到吗?我想我会尝试以诸如...
的方式对识别器进行子class
class BetterPanGestureRecognizer: UIPanGestureRecognizer {
var initialTouchLocation: CGPoint!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
initialTouchLocation = touches.first!.locationInView(view)
print("pan: touch begin detected")
print(self.state.hashValue) // this lets me check the state
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
print("pan: touch move detected")
print(self.state.hashValue) // this remains at the "began" value until you get beyond about 10 pixels
let some_criterion = (touches.first!.isEqual(something) && event.isEqual(somethingElse))
if (some_criterion) {
self.state = UIGestureRecognizerState.Changed
}
}
}
...但我不确定 some_criterion 等
使用什么
有什么建议吗?
.
其他可行但我不想做的替代方案:
- 我可以简单地将我的 UIPanGestureRecognizer 附加到某个父级,
非缩放视图,然后使用仿射变换等重新映射
平底锅的点接触到图像的各个部分。
那我为什么不这样做呢?因为代码是这样写的
许多其他对象挂在图像视图之外,它们都得到了
相同的手势识别器和......一切都很好,没有
我一直在跟踪任何事情(例如仿射变换),只有当你真的-真的放大时,问题才会出现。
- 我可以放弃 UIPanGestureRecognizer,实际上只需使用 touchesBegan 和 touchesMoved(这是一种
我在做什么),但是我喜欢 UIPanGestureRecognizer
以一种我不知道的方式将自己与捏事件区分开来
不得不担心自己编码。
- 我可以指定用户不能超过的最大缩放比例。这无法实现我的目标,即我想允许精细的操作级别。
谢谢。
[如果值得的话,我会选择你的答案而不是我的答案(即以下),所以我暂时不会 'accept' 这个答案。]
知道了。该解决方案的基本思想是在触摸移动时更改状态,但使用关于同时手势识别器的委托方法,以免 "lock" 出任何捏合(或旋转)手势。这将允许单指 and/or 多指平移,如您所愿,没有 'conflicts'.
这就是我的代码:
class BetterPanGestureRecognizer: UIPanGestureRecognizer, UIGestureRecognizerDelegate {
var initialTouchLocation: CGPoint!
override init(target: AnyObject?, action: Selector) {
super.init(target: target, action: action)
self.delegate = self
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
initialTouchLocation = touches.first!.locationInView(view)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
if UIGestureRecognizerState.Possible == self.state {
self.state = UIGestureRecognizerState.Changed
}
}
func gestureRecognizer(_: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool {
if !(shouldRecognizeSimultaneouslyWithGestureRecognizer is UIPanGestureRecognizer) {
return true
} else {
return false
}
}
}
通常将 "shouldRecognizeSimultaneouslyWithGestureRecognizer" 委托设置为 true always 是许多人可能想要的。如果另一个识别器是另一个 Pan,我将委托 return 设置为 false,只是因为我注意到没有那个逻辑(即,无论如何使委托 return 为真),它是 "passing through" Pan 对底层视图做手势,我不想要那样。无论如何,您可能只想让它 return 为真。干杯。
Swift 5 + 小改进
我有一个案例,当接受的解决方案与工具栏上的基本点击冲突时,工具栏上也有这个 betterPanGesture,所以我添加了最小水平偏移参数来触发状态更改为 .changed
class BetterPanGestureRecognizer: UIPanGestureRecognizer {
private var initialTouchLocation: CGPoint?
private let minHorizontalOffset: CGFloat = 5
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
self.initialTouchLocation = touches.first?.location(in: self.view)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if self.state == .possible,
abs((touches.first?.location(in: self.view).x ?? 0) - (self.initialTouchLocation?.x ?? 0)) >= self.minHorizontalOffset {
self.state = .changed
}
}
}
我的问题:有没有办法调整UIPanGestureRecognizer的"sensitivity"使其开启'sooner',即在移动较少数量的'pixels'?
我有一个带有 UIImageView 的简单应用程序,以及与此相关的捏合和平移手势识别器,以便用户可以放大图像并手动在图像上绘制。工作正常。
但是,我注意到在用户的手势移动了大约 10 个像素之前,常用的 UIPanGestureRecognizer 不会 return 值 UIGestureRecognizerState.Changed。
示例:这是一张屏幕截图,显示了我尝试绘制的几条线越来越短,并且有一个明显的有限长度,低于该长度没有绘制任何线,因为平移手势识别器永远不会改变状态。
IllustrationOfProgressivelyShorterLines.png
...即,在黄线的右侧,我仍在尝试绘制,并且我的触摸被识别为 touchesMoved 事件,但是 UIPanGestureRecognizer 没有触发它自己的 "Moved" 事件因此什么也没有画出来。
(Note/clarification: 那张图片占据了我 iPad 的整个屏幕,所以即使在没有识别器发生状态变化。就捏合手势识别器生成的变换而言,我们只是 'zoomed in',因此一些 'pixels' 图像占据了大量屏幕。 )
这不是我想要的。关于如何解决它的任何想法?
也许 UIPanGestureRecognizer 的一些 'internal' 参数如果我对它进行子class编辑或类似的,我可以得到吗?我想我会尝试以诸如...
的方式对识别器进行子classclass BetterPanGestureRecognizer: UIPanGestureRecognizer {
var initialTouchLocation: CGPoint!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
initialTouchLocation = touches.first!.locationInView(view)
print("pan: touch begin detected")
print(self.state.hashValue) // this lets me check the state
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
print("pan: touch move detected")
print(self.state.hashValue) // this remains at the "began" value until you get beyond about 10 pixels
let some_criterion = (touches.first!.isEqual(something) && event.isEqual(somethingElse))
if (some_criterion) {
self.state = UIGestureRecognizerState.Changed
}
}
}
...但我不确定 some_criterion 等
使用什么
有什么建议吗?
.
其他可行但我不想做的替代方案:
- 我可以简单地将我的 UIPanGestureRecognizer 附加到某个父级, 非缩放视图,然后使用仿射变换等重新映射 平底锅的点接触到图像的各个部分。 那我为什么不这样做呢?因为代码是这样写的 许多其他对象挂在图像视图之外,它们都得到了 相同的手势识别器和......一切都很好,没有 我一直在跟踪任何事情(例如仿射变换),只有当你真的-真的放大时,问题才会出现。
- 我可以放弃 UIPanGestureRecognizer,实际上只需使用 touchesBegan 和 touchesMoved(这是一种 我在做什么),但是我喜欢 UIPanGestureRecognizer 以一种我不知道的方式将自己与捏事件区分开来 不得不担心自己编码。
- 我可以指定用户不能超过的最大缩放比例。这无法实现我的目标,即我想允许精细的操作级别。
谢谢。
[如果值得的话,我会选择你的答案而不是我的答案(即以下),所以我暂时不会 'accept' 这个答案。]
知道了。该解决方案的基本思想是在触摸移动时更改状态,但使用关于同时手势识别器的委托方法,以免 "lock" 出任何捏合(或旋转)手势。这将允许单指 and/or 多指平移,如您所愿,没有 'conflicts'.
这就是我的代码:
class BetterPanGestureRecognizer: UIPanGestureRecognizer, UIGestureRecognizerDelegate {
var initialTouchLocation: CGPoint!
override init(target: AnyObject?, action: Selector) {
super.init(target: target, action: action)
self.delegate = self
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
initialTouchLocation = touches.first!.locationInView(view)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
if UIGestureRecognizerState.Possible == self.state {
self.state = UIGestureRecognizerState.Changed
}
}
func gestureRecognizer(_: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool {
if !(shouldRecognizeSimultaneouslyWithGestureRecognizer is UIPanGestureRecognizer) {
return true
} else {
return false
}
}
}
通常将 "shouldRecognizeSimultaneouslyWithGestureRecognizer" 委托设置为 true always 是许多人可能想要的。如果另一个识别器是另一个 Pan,我将委托 return 设置为 false,只是因为我注意到没有那个逻辑(即,无论如何使委托 return 为真),它是 "passing through" Pan 对底层视图做手势,我不想要那样。无论如何,您可能只想让它 return 为真。干杯。
Swift 5 + 小改进
我有一个案例,当接受的解决方案与工具栏上的基本点击冲突时,工具栏上也有这个 betterPanGesture,所以我添加了最小水平偏移参数来触发状态更改为 .changed
class BetterPanGestureRecognizer: UIPanGestureRecognizer {
private var initialTouchLocation: CGPoint?
private let minHorizontalOffset: CGFloat = 5
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
self.initialTouchLocation = touches.first?.location(in: self.view)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if self.state == .possible,
abs((touches.first?.location(in: self.view).x ?? 0) - (self.initialTouchLocation?.x ?? 0)) >= self.minHorizontalOffset {
self.state = .changed
}
}
}