iOS键盘扩展中屏幕左侧的动画延迟

Posted

技术标签:

【中文标题】iOS键盘扩展中屏幕左侧的动画延迟【英文标题】:Animation delay on left side of screen in iOS keyboard extension 【发布时间】:2016-05-12 19:50:01 【问题描述】:

我正在为 ios 开发键盘扩展。但是,我遇到了一些奇怪的问题,动画/图层没有立即出现在屏幕的最左侧。当用户按键时,我使用图层/动画来显示“工具提示”。对于除 A 和 Q 之外的所有键,工具提示都会立即显示,但是对于这两个键,在图层和动画出现之前似乎有一点延迟。这只会在触地时发生,如果我滑入 Q 或 A 命中区域,工具提示会立即呈现。我的调试显示代码对所有键的执行完全相同,但是对于这两个键,它没有立即生效。

关于屏幕左边缘是否有任何特殊情况可能导致此行为的任何想法?还是我在做一些愚蠢的事情,这可能是造成这种情况的原因?

这是触发工具提示渲染的触摸处理代码的一部分:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) 
    if(!shouldIgnoreTouches()) 
        for touch in touches 
            let location = (touch ).locationInView(self.inputView)

            // pass coordinates to offset service to find candidate keys
            let keyArray = keyOffsetService.getKeys(_keyboardLayout!, location: location)
            let primaryKey = keyArray[0]

            if primaryKey.alphaNumericKey != nil 
                let layers = findLayers(touch )

                if layers.keyLayer != nil 
                    graphicsService.animateKeyDown(layers.keyLayer as! CATextLayer, shieldLayer: layers.shieldLayer)
                    _shieldsUp.append((textLayer:layers.keyLayer, shieldLayer:layers.shieldLayer))
                
            
         
    

动画代码:

func animateKeyDown(layer:CATextLayer, shieldLayer:CALayer?) 
    if let sLayer = shieldLayer 
        keyDownShields(layer, shieldLayer: sLayer)
        CATransaction.begin()
        CATransaction.setDisableActions(true)

        let fontSizeAnim = CABasicAnimation(keyPath: "fontSize")
        fontSizeAnim.removedOnCompletion = true
        fontSizeAnim.fromValue = layer.fontSize
        fontSizeAnim.toValue = layer.fontSize * 0.9
        layer.fontSize = layer.fontSize * 0.9

        let animation  = CABasicAnimation(keyPath: "opacity")
        animation.removedOnCompletion = true
        animation.fromValue = layer.opacity
        animation.toValue = 0.3
        layer.opacity = 0.3

        let animGroup = CAAnimationGroup()
        animGroup.animations = [fontSizeAnim, animation]
        animGroup.duration = 0.01
        layer.addAnimation(animGroup, forKey: "down")

        CATransaction.commit()
    

取消隐藏工具提示层:

private func keyDownShields(layer:CATextLayer, shieldLayer:CALayer) 
    shieldLayer.hidden = false
    shieldLayer.setValue(true, forKey: "isUp")
    shieldLayer.zPosition = 1
    shieldLayer.removeAllAnimations()
    layer.setValue(true, forKey: "isUp")

【问题讨论】:

【参考方案1】:

这是由 iOS 9 中的一项功能引起的,该功能允许用户在向右滑动时强制按下屏幕的左边缘来切换应用程序。

您可以通过禁用 3D 触摸来关闭此功能,但这不是解决方案。

我不知道有任何 API 允许您覆盖此行为。

【讨论】:

谢谢,有道理。我猜 iOS10 中的行为发生了变化,因为问题已经消失了。 我的 iPhone 6s iOS 11 版本仍然存在这个问题,知道如何在我的应用程序中禁用 3D 触摸吗?? @Netero,据我所知 - 你不能。至少我们对此无能为力。 @Malakim 谢谢老兄!令人失望:/我已经在bugreport.apple.com 提交了一个雷达,表明Apple 提供了一个类似于能够设置的API:self.navigationController?.interactivePopGestureRecognizer.enabled = false【参考方案2】:

官方解决方案是覆盖您的UIInputViewControllerpreferredScreenEdgesDeferringSystemGestures

https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys

但是,它似乎至少不适用于 iOS 13。据我了解,这是因为preferredScreenEdgesDeferringSystemGesturesUIInputViewController 中被覆盖时无法正常工作,至少在iOS 13 上是这样。

当您在常规视图控制器中覆盖此属性时,它会按预期工作:

override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge 
    return [.left, .bottom, .right]

不过,UIInputViewController 并非如此。

UPD:看来,手势识别器仍会毫无延迟地获得.began 状态更新。因此,您可以添加自定义手势识别器来处理触摸事件,而不是遵循下面相当混乱的解决方案。

您可以快速测试这个添加UILongPressGestureRecognizerminimumPressDuration = 0 到您的控制视图。

另一种解决方案:

我最初的解决方法是在hitTest(_ point: CGPoint, with event: UIEvent?) -&gt; UIView? 中调用touch down 效果,即使视图的触摸延迟也会调用。

您必须忽略“真正的”触地事件,当它在大约 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:)

【讨论】:

以上是关于iOS键盘扩展中屏幕左侧的动画延迟的主要内容,如果未能解决你的问题,请参考以下文章

iOS 7 设备上的键盘动画问题

如何在键盘外观上为 Flutter 布局设置动画

如何使用 presentViewController 呈现 iOS 键盘扩展

在 iOS7 中边缘滑动时,使键盘与 UIView 同步动画

UITextField 的初始键盘动画的超慢滞后/延迟

printscreen键为何无法使用