RxSwift by Examples #2 – Observable and the Bind

RxSwift by Examples 分成 4 部分激率。以下是 PART 2 的學習筆記和翻譯整理咳燕。原文在這里

binding 意思是連接 Observable 和 Subject乒躺。

釋義

我們已經(jīng)學習過 Observable 和 Observer招盲。

  • Subject - 可觀察的和觀察者。它可以觀察和被觀察聪蘸。
  • BehaviorSubject - 當你訂閱它宪肖,你將得到它已發(fā)出的最新的值表制,以及此后發(fā)出的值。
  • PublishSubject - 當你訂閱它控乾,你只能得到此后它發(fā)出的值么介。
  • ReplaySubject - 當你訂閱它,你將得到此后發(fā)出的值蜕衡,但也能得到此前發(fā)出的值壤短。可以得到多早以前發(fā)出的值呢慨仿?這取決于你所訂閱的 ReplaySubject 的緩存大芯酶(buffer size)。

舉例說:你正在舉行生日派對镰吆,你要打開你收到的禮物帘撰。

你打開了第一個、第二個万皿、第三個禮物摧找。你媽媽正在廚房里準備美味的食物,因此還沒來到派對現(xiàn)場牢硅。作為一個媽媽蹬耘,她想知道你得到了什么禮物。于是你將情況告訴她减余。在 Rx 的世界中综苔,你發(fā)送可觀察的序列 obserbable sequence(禮物)給觀察者 observer(你媽媽)。有意思的是位岔,她在你已經(jīng)發(fā)出了若干值之后開始觀察如筛,但是不管怎樣她得到了完整的信息。對她我們是一個 buffer = 3 的 ReplaySubject(我們保留 3 個最新的禮物發(fā)送給每一個新的訂閱者)赃承。

你繼續(xù)打開禮物妙黍。這時你看到你的兩個朋友 Jack 和 Andy 也來到派對。Jack 是你的好朋友瞧剖,所以他問你目前打開了些什么拭嫁。你對他的遲到有些生氣,所以你只告訴他你最后一次打開的禮物抓于。他并不知道還有其他禮物做粤,所以他很高興。在 Rx 的世界中捉撮,你只發(fā)送了最近的一個值給觀察者(Jack)怕品。他還將得到接下來的值當你發(fā)送的時候(接下來你將打開的禮物)。對他而言我們是一個 BehaviorSubject巾遭。

還有一個 Andy肉康,他只是一個普通朋友闯估,并不真的在意你已經(jīng)打開了什么禮物。所以他只是坐下來等待接下來的表演吼和。如你所料涨薪,對他而言我們只是一個 PublishSubject。他只得到在他訂閱之后發(fā)出的值炫乓。

還有一個概念叫 Variable刚夺。這是一個對 BehaviorSubject 的包裝。你只能提交 .onNext() 事件(當使用 BehaviorSubject 的時候你可以直接發(fā)送 .onError(), .onCompleted())末捣。Variable 自動發(fā)送 .onCompleted() 事件侠姑,當它被注銷的時候。

示例

我們將創(chuàng)建一個簡單的 app箩做,在視圖中連接球的顏色與位置莽红,我們還將連接視圖背景色與球體的顏色。

我們創(chuàng)建項目卒茬,并使用 Cocoapods 引入 RxSwift 和 RxCocoa船老,我們還將使用 Chameleon 來連接顏色咖熟。

Podfile

platform :ios, '9.0'
use_frameworks!
 
target 'ColourfulBall' do
 
pod 'RxSwift'
pod 'RxCocoa'
pod 'ChameleonFramework/Swift', :git => 'https://github.com/ViccAlexander/Chameleon.git'
 
end
 
post_install do |installer|
    installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
              config.build_settings['ENABLE_TESTABILITY'] = 'YES'
              config.build_settings['SWIFT_VERSION'] = '3.0'
        end
    end
end

在我們的 Controller 的 main view 中畫一個圓形圃酵。

import ChameleonFramework
import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
 
    var circleView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }
 
    func setup() {
        // Add circle view
        circleView = UIView(frame: CGRect(origin: view.center, size: CGSize(width: 100.0, height: 100.0)))
        circleView.layer.cornerRadius = circleView.frame.width / 2.0
        circleView.center = view.center
        circleView.backgroundColor = .green
        view.addSubview(circleView)
    }
}

下一步,添加 UIPanGestureRecognizer 并根據(jù)手勢改變球形的 frame

func setup() {
    // Add circle view
    circleView = UIView(frame: CGRect(origin: view.center, size: CGSize(width: 100.0, height: 100.0)))
    circleView.layer.cornerRadius = circleView.frame.width / 2.0
    circleView.center = view.center
    circleView.backgroundColor = .green
    view.addSubview(circleView)
        
    // Add gesture recognizer
    let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(circleMoved(_:)))
    circleView.addGestureRecognizer(gestureRecognizer)
}
 
func circleMoved(_ recognizer: UIPanGestureRecognizer) {
    let location = recognizer.location(in: view)
    UIView.animate(withDuration: 0.1) { 
        self.circleView.center = location
    }
}

Bind

下一步我們將做綁定馍管。連接球體位置與球體顏色郭赐。怎樣做呢?

首先我們將用 rx.observe() 觀察球體的中心位置确沸,然后用 bindTo() 綁定給一個變量 Variable捌锭。在我們的例子中,綁定做了什么事情呢罗捎?每一次一個我們的球體發(fā)送新的位置信息观谦,變量將收到關于它的新的信號。我們的變量是一個觀察者桨菜,因為它觀察位置豁状。

我們將在一個 ViewModel 中創(chuàng)建變量,我們用它來計算 UI 的東西倒得。在這個例子中泻红,每次變量得到一個新的地址,我們將為球體計算新的背景色霞掺。

我們只有兩個屬性:centerVariable 將是我們的 observer 和 observable - 我們保存數(shù)據(jù)給它谊路,并從它獲取數(shù)據(jù)。另一個是 backgroundColorObserable菩彬。它實際上不是一個變量缠劝,只是一個 obserable潮梯。

你可能會問為什么 centerVariable 是一個 Variable 而 backgroundColorObserable 是一個 Obserbable?

我們球體的 observable center 連接了 centerVariable惨恭,這意味著任何時候 center 改變酷麦,centerVAriable 將得到這個改變。因此這是一個觀察者 Observer喉恋。同時我們 ViewModel 中的 centerVariable 作為一個 Obserable沃饶,同時作為 observer 和 Observable 就是一個 Subject。

為什么是 Variable 而不是 PublishSubject 或者 ReplaySubject轻黑?因為我們想確保我們將得到訂閱時的最新的球體中心值糊肤。

backgroundColorObservable 只是一個 Observable,它從未被任何東西所約束氓鄙,所以它只是一個可觀察的 Observable馆揉。

ViewModel

我們的基礎 ViewModel 是這樣

import ChameleonFramework
import Foundation
import RxSwift
import RxCocoa

class CircleViewModel {
    
    var centerVariable = Variable<CGPoint?>(.zero) // Create one variable that will be changed and observed
    var backgroundColorObservable: Observable<UIColor>! // Create observable that will change backgroundColor based on center
    
    init() {
        setup()
    }
 
    func setup() {
    }
}

接著我們需要設置 backgroundColorObserable。我們希望它基于由 centerVariable 產(chǎn)生的新的 CGPoint 而改變抖拦。

func setup() {
    // When we get new center, emit new UIColor
    backgroundColorObservable = centerVariable.asObservable()
        .map { center in
            guard let center = center else { return UIColor.flatten(.black)() }
            
            let red: CGFloat = (center.x + center.y).truncatingRemainder(dividingBy: 255.0) / 255.0 // We just manipulate red, but you can do w/e
            let green: CGFloat = 0.0
            let blue: CGFloat = 0.0
            
            return UIColor.flatten(UIColor(red: red, green: green, blue: blue, alpha: 1.0))()
        }
}

分步講解:

  1. 轉換變量成 Observable - 因為 Variable 可以是 Observer 也可以是 Observable升酣,所以我們要決定它是哪一個。又因為我們想觀察它态罪,于是把它轉換成 Observable噩茄。
  2. Map 每個新的 CGPoint 到 UIColor。我們會得到 Observable 產(chǎn)生的新的中心位置复颈,經(jīng)過計算绩聘,得到新的 UIColor。
  3. 你可能注意到 Observable 是一個 optional 的 CGPoint耗啦。為什么凿菩?我們需要在得到 nil 的時候保護自己,返回默認值帜讲。

現(xiàn)在我們有了 Observable 的背景色衅谷。我們需要基于新的值更新球體。這非常簡單似将,我們將 subscribe() 這個 Observable获黔。

fileprivate var circleViewModel: CircleViewModel!
fileprivate let disposeBag = DisposeBag()

然后

circleViewModel = CircleViewModel()
// Subscribe to backgroundObservable to get new colors from the ViewModel.
circleViewModel.backgroundColorObservable
    .subscribe(onNext: { [weak self] backgroundColor in
        UIView.animateWithDuration(0.1) {
            self?.circleView.backgroundColor = backgroundColor
            // Try to get complementary color for given background color
            let viewBackgroundColor = UIColor(complementaryFlatColorOf: backgroundColor)
            // If it is different that the color
            if viewBackgroundColor != backgroundColor {
                // Assign it as a background color of the view
                // We only want different color to be able to see that circle in a view
                self?.view.backgroundColor = viewBackgroundColor
            }
        }
    })
    .addDisposableTo(disposeBag)

我們同時還把視圖背景色變成球體顏色的補色。同時檢查這個補色是否和球體顏色一樣(確保我們看得到球體)玩郊。我們可以把這段代碼放在 setup() 中

func setup() {
    // Add circle view
    circleView = UIView(frame: CGRect(origin: view.center, size: CGSize(width: 100.0, height: 100.0)))
    circleView.layer.cornerRadius = circleView.frame.width / 2.0
    circleView.center = view.center
    circleView.backgroundColor = .green
    view.addSubview(circleView)
    
    circleViewModel = CircleViewModel()
    // Bind the center point of the CircleView to the centerObservable
    circleView
        .rx.observe(CGPoint.self, "center")            
        .bindTo(circleViewModel.centerVariable)
        .addDisposableTo(disposeBag)
 
    // Subscribe to backgroundObservable to get new colors from the ViewModel.
    circleViewModel.backgroundColorObservable
        .subscribe(onNext: { [weak self] backgroundColor in
            UIView.animateWithDuration(0.1) {
                self?.circleView.backgroundColor = backgroundColor
                // Try to get complementary color for given background color
                let viewBackgroundColor = UIColor(complementaryFlatColorOf: backgroundColor)
                // If it is different that the color
                if viewBackgroundColor != backgroundColor {
                    // Assign it as a background color of the view
                    // We only want different color to be able to see that circle in a view
                    self?.view.backgroundColor = viewBackgroundColor
                }
            }
        })
        .addDisposableTo(disposeBag)
    
    let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(circleMoved(_:)))
    circleView.addGestureRecognizer(gestureRecognizer)
}

完成肢执。整個操縱顏色的任務沒有用到做類似事情時我們通常要用到的 delegate, notification。

也許你可以試著綁定中心位置和球體尺寸译红,試著基于 width 和 height 改變 cornerRadius预茄。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子耻陕,更是在濱河造成了極大的恐慌拙徽,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诗宣,死亡現(xiàn)場離奇詭異膘怕,居然都是意外死亡,警方通過查閱死者的電腦和手機召庞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門岛心,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人篮灼,你說我怎么就攤上這事忘古。” “怎么了诅诱?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵髓堪,是天一觀的道長。 經(jīng)常有香客問我娘荡,道長干旁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任炮沐,我火速辦了婚禮争群,結果婚禮上,老公的妹妹穿的比我還像新娘央拖。我一直安慰自己祭阀,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布鲜戒。 她就那樣靜靜地躺著,像睡著了一般抹凳。 火紅的嫁衣襯著肌膚如雪遏餐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天赢底,我揣著相機與錄音失都,去河邊找鬼。 笑死幸冻,一個胖子當著我的面吹牛粹庞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播洽损,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼庞溜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了碑定?” 一聲冷哼從身側響起流码,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤又官,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后漫试,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體六敬,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年驾荣,在試婚紗的時候發(fā)現(xiàn)自己被綠了外构。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡播掷,死狀恐怖典勇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叮趴,我是刑警寧澤割笙,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站眯亦,受9級特大地震影響伤溉,放射性物質發(fā)生泄漏。R本人自食惡果不足惜妻率,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一乱顾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宫静,春花似錦走净、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捌袜,卻和暖如春说搅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虏等。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工弄唧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人霍衫。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓候引,卻偏偏與公主長得像,于是被迫代替她去往敵國和親敦跌。 傳聞我的和親對象是個殘疾皇子澄干,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

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

  • 發(fā)現(xiàn) 關注 消息 RxSwift入坑解讀-你所需要知道的各種概念 沸沸騰關注 2016.11.27 19:11*字...
    楓葉1234閱讀 2,797評論 0 2
  • 最近在學習RxSwift相關的內(nèi)容,在這里記錄一些基本的知識點,以便今后查閱傻寂。 Observable 在RxSwi...
    L_Zephyr閱讀 1,755評論 1 4
  • 本文章內(nèi)部分圖片資源來自RayWenderlich.com 本文結合自己的理解來總結介紹一下RxSwift最基本的...
    FKSky閱讀 2,881評論 4 14
  • 投射心情舒暢開心快樂息尺,美噠噠 投射工作快快來,滿意自在舒適 投射寶寶開心快樂疾掰,健康成長 投射有一個愛我的和我愛的人...
    翟美麗閱讀 111評論 0 0
  • 首先要理清一下什么是“高下”搂誉,高,就是高雅静檬,指高尚雅致炭懊,表現(xiàn)受過良好教養(yǎng)的高尚舉止或情趣,比喻高超雅正拂檩,與“平庸邪...
    運劍閱讀 247評論 0 0