RxSwift中UIView與動畫的結(jié)合

試想一個(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ù)雜的動畫军浆。

2018-12-12 16-01-46.2018-12-12 16_02_05.gif

另外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)
2018-12-12 16-00-16.2018-12-12 16_00_29.gif

如果我們需要在移動之前先旋轉(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)
2018-12-12 15-59-13.2018-12-12 15_59_29.gif

為了讓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ù)用

參考

RxSwift and Animations in iOS

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撒遣,隨后出現(xiàn)的幾起案子断盛,更是在濱河造成了極大的恐慌,老刑警劉巖愉舔,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異伙菜,居然都是意外死亡轩缤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來火的,“玉大人壶愤,你說我怎么就攤上這事×蠛祝” “怎么了征椒?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長湃累。 經(jīng)常有香客問我勃救,道長,這世上最難降的妖魔是什么治力? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任蒙秒,我火速辦了婚禮,結(jié)果婚禮上宵统,老公的妹妹穿的比我還像新娘晕讲。我一直安慰自己,他們只是感情好马澈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布瓢省。 她就那樣靜靜地躺著,像睡著了一般痊班。 火紅的嫁衣襯著肌膚如雪勤婚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天辩块,我揣著相機(jī)與錄音蛔六,去河邊找鬼。 笑死废亭,一個(gè)胖子當(dāng)著我的面吹牛国章,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播豆村,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼液兽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了掌动?” 一聲冷哼從身側(cè)響起四啰,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎粗恢,沒想到半個(gè)月后柑晒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡眷射,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年匙赞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了佛掖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡涌庭,死狀恐怖芥被,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坐榆,我是刑警寧澤拴魄,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站席镀,受9級特大地震影響匹中,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜愉昆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一职员、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧跛溉,春花似錦焊切、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至堪侯,卻和暖如春嚎尤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伍宦。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工芽死, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人次洼。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓关贵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卖毁。 傳聞我的和親對象是個(gè)殘疾皇子揖曾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,982評論 3 119
  • 年復(fù)一年亥啦,終于把自己蹉跎成奔三微胖界的一員炭剪。并沒有說胖的慘不忍睹,直到有一天…… 某個(gè)陽光燦爛的日子翔脱,一行人打算出...
    唐來來lailai閱讀 143評論 0 0
  • 花如雪奴拦,盡無言,為君一笑嘆悲歡 櫻花落届吁,紅塵看粱坤,我再把君嘆 簫聲散隶糕,聽弦斷,再斷一曲聽悲歡 瀟雨寒站玄,笙歌散,寂寞已...
    聽聞千風(fēng)閱讀 301評論 0 2
  • 七絕·緣來(柿)你 秋隨雁去跡無蹤濒旦, 萬里晴空柿子紅株旷。 落盡繁華情意現(xiàn), 陪君一世笑寒風(fēng)尔邓。
    酉時(shí)七若閱讀 271評論 2 10
  • 在極限編程中有一個(gè)很有意思的實(shí)踐晾剖,就是隱喻。他是指在系統(tǒng)架構(gòu)梯嗽、設(shè)計(jì)和開發(fā)過程中將一些重要齿尽、關(guān)鍵、復(fù)雜的概念灯节,用隱喻...
    7in10閱讀 2,274評論 1 51