【问题标题】:animateWithDuration lacks completion block, WatchOS 2animateWithDuration 缺少完成块,WatchOS 2
【发布时间】:2015-06-13 22:42:15
【问题描述】:

watchOS 2 在其 animateWithDuration 函数中没有任何类型的完成块。我正在开发一个需要在动画完成后运行代码的游戏。有解决办法吗?也许使用键值观察?另一种方法是使用与动画长度相匹配的计时器,但由于显而易见的原因,这并不理想。

【问题讨论】:

  • 我还没有测试过这个,但是使用NSOperation 听起来是处理这个问题的好方法。 WWDC '15 刚刚对它进行了精彩的讨论。将动画封装成NSOperation 并将完成的东西放在另一个NSOperation 中,然后添加一个依赖关系,使完成的东西依赖于动画的完成。这将作为一个完成块。
  • 我试试看,谢谢!你有没有机会记得谈话的名字,如果它被记录下来?
  • 标题:“高级 NSOperations”
  • 可悲的是似乎没有工作:(动画只是运行,完成块在动画开始后立即运行。
  • 这很奇怪。您可以使用NSOperations 发布您的代码吗

标签: watchkit apple-watch animatewithduration watchos watchos-2


【解决方案1】:

NSOperation 对我也不起作用。我现在只是使用以下解决方案,直到 Apple 正式添加完成块。不理想,但它有效。我打开了雷达。也许 Apple 会在 watchOS 2 的未来种子中添加一个完成块。

此扩展添加了一个采用完成块的 animateWithDuration 方法。并且在动画块的持续时间之后调用完成块。

extension WKInterfaceController {
    func animateWithDuration(duration: NSTimeInterval, animations: () -> Void, completion: (() -> Void)?) {
        animateWithDuration(duration, animations: animations)
        let completionDelay = dispatch_time(DISPATCH_TIME_NOW, Int64(duration * Double(NSEC_PER_SEC)))
        dispatch_after(completionDelay, dispatch_get_main_queue()) {
            completion?()
        }
    }
}

【讨论】:

  • 确实,这也是我得出的结论。这无论如何都不理想......但它有效:/
  • 我已经看到这在模拟器上运行良好,但在真实设备上却不行。 :(
【解决方案2】:

尝试使用 WKInterfaceController 中的完成块扩展 animateWithDuration 方法

Obj-C 版本(感谢@PunnetSethi):

- (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(void))completion{

    [self animateWithDuration:duration animations:animations];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), completion);

}

使用 WatchOS2 处理真正的 Watch

【讨论】:

    【解决方案3】:

    斯威夫特 4

    watchOS

    高级animate()方法:

    • 可重复
    • 可逆
    • 可停止
    • 带有完成块

    import Foundation
    import WatchKit
    
    
    
    protocol Animatable: class {
    
    func animate(forKey key: String,duration: TimeInterval, repeatCount count: CGFloat, autoreverses: Bool, animations: @escaping () -> Void, reverseAnimations: (() -> Void)?, completion: (() -> Void)?)
    func stopAnimations()
    }
    
    extension Animatable {
    
    func animate(forKey key: String, duration: TimeInterval, repeatCount count: CGFloat, autoreverses: Bool, animations: @escaping () -> Void, reverseAnimations: (() -> Void)?, completion: (() -> Void)?) {}
    
    func stopAnimations() {}
    }
    
    extension WKInterfaceController: Animatable {
    
    private struct AssociatedKeys {
        static var animsDesc = "_animsDesc"
        static var stopDesc = "_stopDesc"
    }
    
    var animations: [String: (() -> Void)?] {
        get {return objc_getAssociatedObject(self, &AssociatedKeys.animsDesc) as? [String: (() -> Void)?] ?? [:]}
        set {objc_setAssociatedObject(self, &AssociatedKeys.animsDesc, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)}
    }
    
    var animationStopStates: [String: Bool] {
        get {return objc_getAssociatedObject(self, &AssociatedKeys.stopDesc) as? [String: Bool] ?? [:]}
        set {objc_setAssociatedObject(self, &AssociatedKeys.stopDesc, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)}
    }
    
    func animate(forKey key: String, duration: TimeInterval, repeatCount count: CGFloat = 1.0, autoreverses: Bool = false, animations: @escaping () -> Void, reverseAnimations: (() -> Void)? = nil, completion: (() -> Void)? = nil) {
        let isStopped = self.animationStopStates[key] ?? false
        if isStopped {
            completion?()
            return
        }
        self.setAnimations(key, reverse: reverseAnimations)
        let count = count - 1
        let deadline = DispatchTime.now() + duration
        self.animate(withDuration: duration, animations: animations)
        DispatchQueue.main.asyncAfter(deadline: deadline) {
            var deadline2 = DispatchTime.now()
            if autoreverses, let rev = reverseAnimations {
                self.animate(withDuration: duration, animations: rev)
                deadline2 = DispatchTime.now() + duration
            }
            DispatchQueue.main.asyncAfter(deadline: deadline2, execute: {
                if !count.isEqual(to: .infinity) && count <= 0 {
                    completion?()
                } else {
                    self.animate(forKey: key, duration: duration, repeatCount: CGFloat(count), autoreverses: autoreverses, animations: animations, reverseAnimations: reverseAnimations, completion: completion)
                }
            })
        }
    }
    
    
    /// Stops all the currently playing animations
    func stopAnimations() {
        for key in self.animations.keys {
            guard let rev = self.animations[key] else {return}
            self.animationStopStates[key] = true
            self.animate(forKey: key, duration: 0, repeatCount: 0, autoreverses: false, animations: {}, reverseAnimations: rev)
        }
        self.animations.removeAll()
    }
    
    
    private func setAnimations(_ key: String, reverse: (() -> Void)?) {
        if self.animations[key] == nil {
            self.animations[key] = reverse
        }
    
    }
    }
    

    使用方法:

    self.animate(forKey: "custom_anim1", duration: 1.0, repeatCount: .infinity, autoreverses: true, animations: {[unowned self] in
            self.myButton.setHeight(100)
        }, reverseAnimations: { [unowned self] in
            self.myButton.setHeight(120)
        }) {
            print("Animation stopped!")
        }
    

    停止所有动画:

    self.stopAnimations()
    

    【讨论】:

      【解决方案4】:

      您可以使用此扩展程序。这是一个更好的解决方案,因为它不依赖于时间,例如在与动画延迟相同的时间后调度代码块。而且很优雅。

      extension WKInterfaceController {
          func animate(withDuration duration: TimeInterval,
                       animations: @escaping () -> Swift.Void,
                       completion: @escaping () -> Swift.Void) {
      
              let queue = DispatchGroup()
              queue.enter()
      
              let action = {
                  animations()
                  queue.leave()
              }
      
              self.animate(withDuration: duration, animations: action)
      
              queue.notify(queue: .main, execute: completion)
          }
      }
      

      您可以像这样轻松使用它:

      self.animate(withDuration: 0.2, animations: {
          //your animation code
      }, completion: {
          //to do after you animation 
      })
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-18
        • 2016-07-11
        相关资源
        最近更新 更多