MVC框架下,如果model有连续变化,通过delegate传递给view是可能出现不一致的,因为动画的执行过程不再主线程中完成,所以实际上他是不依次执行,2个动画叠加的结果可能造成M和V的不一致,为了解决这个问题就要对动画进行排队。
这是我在这个过程中找到的资料:
https://github.com/irace/BRYSerialAnimationQueue
http://guanjinke.com
首先感谢他们对我的帮助。
我的设想解决方案是直接使用
extension UIView
来扩展一个可以实现排队执行动画的方法。在学习了BRYSerialAnimationQueue以后遇到的主要问题是,扩展不允许使用存储属性,不过在http://guanjinke.com找到 了解决办法使用
objc_getAssociatedObject
以及objc_setAssociatedObject
!
下面附具体的代码及使用:
import UIKit
private var QUEUE_NUMBER_ID = UnsafePointer<Void>()
private var SEMAPHONE_NUMBER_ID = UnsafePointer<Void>()
extension UIView {
//添加一个存储属性,感谢http://guanjinke.com
class var queue:dispatch_queue_t{
get{
var result = objc_getAssociatedObject(self, &QUEUE_NUMBER_ID) as? dispatch_queue_t
if result == nil {
//log("shit,I queue have not be inited!")
//NSLog("shit,I queue have not be inited!", NSUTF8StringEncoding)
objc_setAssociatedObject(self, &QUEUE_NUMBER_ID, dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL), objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN))
//return dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL)
}
return objc_getAssociatedObject(self, &QUEUE_NUMBER_ID)! as dispatch_queue_t
}
set{
objc_setAssociatedObject(self, &QUEUE_NUMBER_ID, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN))
}
}
class var semaphore:dispatch_semaphore_t{
get{
var result = objc_getAssociatedObject(self, &SEMAPHONE_NUMBER_ID) as? dispatch_semaphore_t
if result == nil {
//NSLog("shit,I semaphore have not be inited!", NSUTF8StringEncoding)
objc_setAssociatedObject(self, &SEMAPHONE_NUMBER_ID, dispatch_semaphore_create(0), objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN))
//return dispatch_semaphore_create(0)
}
return objc_getAssociatedObject(self, &SEMAPHONE_NUMBER_ID)! as dispatch_semaphore_t
}
set{
objc_setAssociatedObject(self, &SEMAPHONE_NUMBER_ID, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN))
//NSLog("semaphore set", NSUTF8StringEncoding)
}
}
class func performAnimationsSerially(animation: ()->Void ) {
dispatch_async(self.queue, { ()->Void in
self.semaphore = dispatch_semaphore_create(0)
//dispatch_semaphore_signal(self.semaphore)
//dispatch_semaphore_signal(self.semaphore)
//dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER)
dispatch_async(dispatch_get_main_queue(), animation)
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER)
})
}
class func runCompletionBlock(completion:(Bool->Void)?,animationDidFinish finished:Bool) {
if let c = completion {
c(finished)
}
dispatch_semaphore_signal(self.semaphore);
}
class func queueAnimateWithDuration(duration:NSTimeInterval,delay:NSTimeInterval,options:UIViewAnimationOptions,animations:()->Void,completion:(Bool->Void)?){
self.performAnimationsSerially({ ()->Void in
UIView.animateWithDuration(duration,delay:delay,options:options, animations:animations,completion:{(finished:Bool)->Void in
self.runCompletionBlock(completion,animationDidFinish:finished)
})
})
}
class func queueAnimateWithDuration(duration:NSTimeInterval,animations:()->Void,completion:(Bool->Void)?){
self.performAnimationsSerially({ ()->Void in
UIView.animateWithDuration(duration,animations:animations,completion:{(finished:Bool)->Void in
self.runCompletionBlock(completion,animationDidFinish:finished)
})
})
}
class func queueAnimateWithDuration(duration:NSTimeInterval,animations:()->Void){
self.performAnimationsSerially({ ()->Void in
UIView.animateWithDuration(duration, animations:animations,completion:{(finished:Bool)->Void in
self.runCompletionBlock(nil,animationDidFinish:true)
})
})
}
class func animateWithDuration(duration: NSTimeInterval, delay: NSTimeInterval, usingSpringWithDamping dampingRatio: CGFloat, initialSpringVelocity velocity: CGFloat, options: UIViewAnimationOptions, animations: () -> Void, completion: ((Bool) -> Void)){
self.performAnimationsSerially({ ()->Void in
UIView.animateWithDuration(duration, delay: delay, usingSpringWithDamping: dampingRatio, initialSpringVelocity: velocity, options: options, animations: animations, completion: {(finished:Bool)->Void in
self.runCompletionBlock(completion,animationDidFinish:finished)
})
})
}
UIView.queueAnimateWithDuration(tileRefreshExpandTime, delay: tilePopDelay, options: UIViewAnimationOptions.TransitionNone,
animations: { () -> Void in
// Make the tile 'pop'
UIView.animateWithDuration(self.tileContractTime, animations: { () -> Void in
tile.layer.setAffineTransform(CGAffineTransformIdentity)
tile.value = value
})
tile.layer.setAffineTransform(CGAffineTransformMakeScale(self.tilePopMaxScale, self.tilePopMaxScale))
},
completion:{ (finished: Bool) -> Void in
// Shrink the tile after it 'pops'
UIView.animateWithDuration(self.tileContractTime, animations: { () -> Void in
tile.layer.setAffineTransform(CGAffineTransformIdentity)
tile.value = value
})
})