自 iOS11 以来的官方解决方案是覆盖preferredScreenEdgesDeferringSystemGestures
您的UIInputViewController
.
https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys
但是,它似乎至少不适用于 iOS 13。据我了解,这是由于preferredScreenEdgesDeferringSystemGestures
在内部被覆盖时无法正常工作UIInputViewController
,至少在 iOS 13 上是这样。
当您在常规视图控制器中覆盖此属性时,它会按预期工作:
override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
return [.left, .bottom, .right]
}
但是,情况并非如此UIInputViewController
。
UPD:看来,手势识别器仍然会.began
毫无延迟地获得状态更新。因此,您可以添加一个自定义手势识别器来处理触摸事件,而不是遵循下面相当混乱的解决方案。
您可以快速测试此添加UILongPressGestureRecognizer
到minimumPressDuration = 0
您的控制视图。
另一种解决方案:
我最初的解决方法是调用touch down
effects inside hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
,即使在视图的触摸延迟时也会调用它。
您必须忽略“真正的”触地事件,当它在大约 0.4 秒后触发或与touch up inside
事件同时触发时。此外,最好仅在测试点位于约 20pt 横向边距内的情况下应用此 hack。
例如,对于一个等于屏幕宽度的视图,实现可能如下所示:
let edgeProtectedZoneWidth: CGFloat = 20
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
guard result == self else {
return result
}
if point.x < edgeProtectedZoneWidth || point.x > bounds.width-edgeProtectedZoneWidth
{
if !alreadyTriggeredFocus {
isHighlighted = true
}
triggerFocus()
}
return result
}
private var alreadyTriggeredFocus: Bool = false
@objc override func triggerFocus() {
guard !alreadyTriggeredFocus else { return }
super.triggerFocus()
alreadyTriggeredFocus = true
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
alreadyTriggeredFocus = false
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
alreadyTriggeredFocus = false
}
...您在事件中triggerFocus()
调用的方法在哪里。touch down
或者,您可以覆盖touchesBegan(_:with:)
.