試想一個(gè)實(shí)際場景许赃,先將視圖旋轉(zhuǎn)180度止喷,然后視圖暗淡消失,一般情況下我們可能會選擇在動畫完成的閉包中再添加暗淡動畫混聊,如下:
UIView.animate(withDuration: 0.5, animations: {
self.animatableView.transform = CGAffineTransform(rotationAngle: .pi / 2)
}, completion: { _ in
UIView.animate(withDuration: 0.5, animations: {
self.animatableView.alpha = 0
})
})
目前代碼看起來還OK弹谁,實(shí)現(xiàn)也很簡單,并不是很復(fù)雜,但是如果需要再插入一個(gè)動畫呢预愤,那么有可能變成這樣
UIView.animate(withDuration: 0.5, animations: {
self.animatableView.transform = CGAffineTransform(rotationAngle: .pi / 2)
}, completion: { _ in
UIView.animate(withDuration: 0.5, animations: {
self.animatableView.frame = self.animatableView.frame.offsetBy(dx: 50, dy: 0)
}, completion: { _ in
UIView.animate(withDuration: 0.5, animations: {
self.animatableView.alpha = 0
})
})
})
這樣看起來很不舒服沟于,有些笨重。如果還需要更多的動畫效果鳖粟,那么可讀性就相對差了社裆。當(dāng)然你可能會想到對于上面的代碼,我們其實(shí)可以使用關(guān)鍵字動畫來處理
UIView.animateKeyframes(withDuration: 1.5, delay: 0, options: [], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.33, animations: {
self.animatableView.transform = CGAffineTransform(rotationAngle: .pi / 2)
})
UIView.addKeyframe(withRelativeStartTime: 0.33, relativeDuration: 0.33, animations: {
self.animatableView.frame = self.animatableView.frame.offsetBy(dx: 50, dy: 0)
})
UIView.addKeyframe(withRelativeStartTime: 0.66, relativeDuration: 0.34, animations: {
self.animatableView.alpha = 0
})
})
雖然看起來直接向图、清晰一些泳秀,但是擴(kuò)展性不夠。接下來看看RxSwift的鏈?zhǔn)絼赢媽?shí)現(xiàn)
封裝每一個(gè)動畫成為一個(gè)函數(shù)榄攀,并返回一個(gè)Observable<Void>嗜傅,一旦動畫執(zhí)行完成被觀察者將發(fā)送元素告知,這樣很容易就實(shí)現(xiàn)動畫鏈?zhǔn)叫Ч?/p>
旋轉(zhuǎn)函數(shù)
func rotate(_ view: UIView, duration: TimeInterval) -> Observable<Void> {
return Observable.create { (observer) -> Disposable in
UIView.animate(withDuration: duration, animations: {
view.transform = CGAffineTransform(rotationAngle: .pi/2)
}, completion: { _ in
observer.onNext(())
observer.onCompleted()
})
return Disposables.create()
}
}
移動函數(shù)
func shift(_ view: UIView, duration: TimeInterval) -> Observable<Void> {
return Observable.create { (observer) -> Disposable in
UIView.animate(withDuration: duration, animations: {
view.frame = view.frame.offsetBy(dx: 50, dy: 0)
}, completion: { _ in
observer.onNext(())
observer.onCompleted()
})
return Disposables.create()
}
}
暗淡函數(shù)
func fade(_ view: UIView, duration: TimeInterval) -> Observable<Void> {
return Observable.create { (observer) -> Disposable in
UIView.animate(withDuration: duration, animations: {
view.alpha = 0
}, completion: { _ in
observer.onNext(())
observer.onCompleted()
})
return Disposables.create()
}
}
實(shí)現(xiàn)了上面的函數(shù)檩赢,現(xiàn)在就可以組合起來使用了
rotate(animatableView, duration: 0.5)
.flatMap { [unowned self] in
self.shift(self.animatableView, duration: 0.5)
}
.flatMap { [unowned self] in
self.fade(self.animatableView, duration: 0.5)
}
.subscribe()
.disposed(by: disposeBag)
雖然整體的代碼量比之前的實(shí)現(xiàn)方式還多吕嘀,但是動畫實(shí)現(xiàn)部分看起來很舒服,而且使用什么動畫以及使用多少個(gè)動畫都可以由自己來決定贞瞒,代碼的可擴(kuò)展和重用性都強(qiáng)偶房。而且函數(shù)封裝的動畫越多,越容易組合動畫效果實(shí)現(xiàn)復(fù)雜的動畫军浆。
另外RxSwift提供了很多操作符號棕洋,非常方便進(jìn)行各種功能操作,比如:concat
Observable.concat([
rotate(animatableView, duration: 0.5),
shift(animatableView, duration: 0.5),
fade(animatableView, duration: 0.5)
])
.subscribe()
.disposed(by: disposeBag)
使用該操作符能夠?qū)崿F(xiàn)相同的效果乒融,如果我們需要加入適當(dāng)?shù)难訒r(shí)操作掰盘,也是非常方便的
// 封裝延時(shí)函數(shù)
func delay(_ duration: TimeInterval) -> Observable<Void> {
return Observable.of(()).delay(duration, scheduler: MainScheduler.instance)
}
// 添加延時(shí)
Observable.concat([
rotate(animatableView, duration: 0.5),
delay(0.5),
shift(animatableView, duration: 0.5),
delay(1.0),
fade(animatableView, duration: 0.5)
])
.subscribe()
.disposed(by: disposeBag)
如果我們需要在移動之前先旋轉(zhuǎn)幾次,這也是很容易實(shí)現(xiàn)的赞季,先實(shí)現(xiàn)無限旋轉(zhuǎn)愧捕,然后只取前面需要的次數(shù)即可
func rotateEndlessly(_ view: UIView, duration: TimeInterval) -> Observable<Void> {
var disposed = false
return Observable.create { (observer) -> Disposable in
func animate() {
UIView.animate(withDuration: duration, animations: {
view.transform = view.transform.rotated(by: .pi/2)
}, completion: { (_) in
observer.onNext(())
if !disposed {
animate()
}
})
}
animate()
return Disposables.create {
disposed = true
}
}
}
// 實(shí)現(xiàn)旋轉(zhuǎn)5次
Observable.concat([
rotateEndlessly(animatableView, duration: 0.5).take(5),
shift(animatableView, duration: 0.5),
fade(animatableView, duration: 0.5)
])
.subscribe()
.disposed(by: disposeBag)
為了讓UIView類使用我們自己封裝的動畫效果,我們可以為Reactive添加擴(kuò)展
extension Reactive where Base == UIView {
func rotate(duration: TimeInterval) -> Observable<Void> {
return Observable.create { (observer) -> Disposable in
UIView.animate(withDuration: duration, animations: {
self.base.transform = CGAffineTransform(rotationAngle: .pi/2)
}, completion: { _ in
observer.onNext(())
observer.onCompleted()
})
return Disposables.create()
}
}
func shift(duration: TimeInterval) -> Observable<Void> {
return Observable.create { (observer) -> Disposable in
UIView.animate(withDuration: duration, animations: {
self.base.frame = self.base.frame.offsetBy(dx: 50, dy: 0)
}, completion: { (_) in
observer.onNext(())
observer.onCompleted()
})
return Disposables.create()
}
}
func fade(duration: TimeInterval) -> Observable<Void> {
return Observable.create { (observer) -> Disposable in
UIView.animate(withDuration: duration, animations: {
self.base.alpha = 0
}, completion: { (_) in
observer.onNext(())
observer.onCompleted()
})
return Disposables.create()
}
}
func rotateEndlessly(duration: TimeInterval) -> Observable<Void> {
var disposed = false
return Observable.create { (observer) -> Disposable in
func animate() {
UIView.animate(withDuration: duration, animations: {
self.base.transform = self.base.transform.rotated(by: .pi/2)
}, completion: { (_) in
observer.onNext(())
if !disposed {
animate()
}
})
}
animate()
return Disposables.create {
disposed = true
}
}
}
}
直接使用即可
Observable.concat([
animatableView.rx.rotateEndlessly(duration: 0.5).take(5),
animatableView.rx.shift(duration: 0.5),
animatableView.rx.fade(duration: 0.5)
])
.subscribe()
.disposed(by: disposeBag)
最后可以根據(jù)需求實(shí)現(xiàn)自己需要的動畫函數(shù)申钩,添加到擴(kuò)展中次绘,方便代碼功能的復(fù)用