animateWithDuration 缺少完成块,WatchOS 2
animateWithDuration lacks completion block, WatchOS 2
watchOS 2 在其 animateWithDuration 函数中没有任何类型的完成块。我正在开发一款在动画完成后需要 运行 代码的游戏。有什么解决方法吗?也许使用键值观察?另一种方法是使用与动画长度相匹配的计时器,但由于显而易见的原因,这并不理想。
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?()
}
}
}
尝试在 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
Swift 4
animate() watchOS 的高级方法:
- 可重复
- 可逆
- 可以停止
- 带有完成块
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()
您可以使用此扩展程序。这是一个更好的解决方案,因为它不依赖于时间,例如在与动画延迟相同的时间后分派代码块。而且很优雅。
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
})
watchOS 2 在其 animateWithDuration 函数中没有任何类型的完成块。我正在开发一款在动画完成后需要 运行 代码的游戏。有什么解决方法吗?也许使用键值观察?另一种方法是使用与动画长度相匹配的计时器,但由于显而易见的原因,这并不理想。
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?()
}
}
}
尝试在 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
Swift 4
animate() watchOS 的高级方法:
- 可重复
- 可逆
- 可以停止
- 带有完成块
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()
您可以使用此扩展程序。这是一个更好的解决方案,因为它不依赖于时间,例如在与动画延迟相同的时间后分派代码块。而且很优雅。
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
})