Skip to content

Instantly share code, notes, and snippets.

@yamazaki-sensei
Last active December 7, 2018 17:49
Show Gist options
  • Select an option

  • Save yamazaki-sensei/abb2d3485ec80a1bd58df2e54d62c768 to your computer and use it in GitHub Desktop.

Select an option

Save yamazaki-sensei/abb2d3485ec80a1bd58df2e54d62c768 to your computer and use it in GitHub Desktop.
ReSwiftを使うときに可能な限り再描画を避けたい話 ref: https://qiita.com/yamazaki_sensei/items/90a7c31f8f930a6c9986
import UIKit
protocol AvoidRerender: class {
associatedtype TargetState: Equatable
// この記事を書いている途中で、これがinternalなのはまずいことに気付いた。が、現状どうしようもない。
var state: TargetState? { get set }
func update(with state: TargetState?)
func customAction(state: TargetState?)
}
extension AvoidRerender where Self: UIViewController {
func update(with state: TargetState?) {
guard state != self.state else { return }
self.state = state
customAction(state: state)
self.view.setNeedsLayout()
}
}
extension AvoidRerender where Self: UIView {
func update(with state: TargetState?) {
guard state != self.state else { return }
self.state = state
customAction(state: state)
setNeedsLayout()
}
}
import ReSwift
struct CounterState: StateType {
var count = 0
}
extension CounterState: Equatable {
static func == (lhs: CounterState, rhs: CounterState) -> Bool {
return lhs.count == rhs.count
}
}
class View: UIView {
let viewModel = ViewModel()
init() {
super.init()
viewModel.cacheObservable.subscribe(onNext: {[weak self] value in
self.update(with: value)
}).disposed(by: disposeBag)
}
private func update(with value: Int) {
// 何かする
}
}
class View: UIView {
private var cache: Int? // 外部には絶対見せない
// storeにsubscribeしているViewControllerから呼んでもらうメソッド
func updateWithState(state: State) {
guard state.value != cache else { return }
update(with: state.value)
}
private func update(with value: Int) {
// 何かする
}
}
extension ViewController: StoreSubscriber, AvoidRerender {
typealias TargetState = CounterState
typealias StoreSubscriberStateType = CounterState
        // protocol extension内で StoreSubscriber を実装して、 newState もデフォルト実装してしまっていいかも
func newState(state: CounterState) {
update(with: state)
}
func customAction(state: CounterState?) {
print("customAction called")
guard let state = state else {
valueLabel.text = ""
return
}
valueLabel.text = "\(state.count)"
}
}
// Intの状態変化をViewに伝えたい場合
// 擬似コード。実際には動かない。
import RxSwift
class ViewModel {
private let cache = BehaviorRelay<Int>(value: 0)
var cacheObservable: Observable<Int> {
return cache.asObservable() // 外部から値を設定できないように、Observableだけ公開しておく
}
// storeにsubscribeしているViewControllerから呼んでもらうメソッド
func updateWithState(state: State) {
guard state.value != cache.value else { return } // これで再描画を避けられるはず
cache.accept(state.value)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment