はじめに
@State
で定義した値に変化が生じた際、それに応じて処理を挟み込むいくつかの方法です。
準備
デモ用に、文字列の末尾が3の倍数だったらアホになるExtensionを生やします。
extension String {
var needsAho: Bool {
self.last
.flatMap { Int("\($0)") }
.flatMap { $0 % 3 == 0 } ?? false
}
}
1. Computed Propertyを使う
- 一番シンプルな方法で、値に変化があると即時に反映されます
import SwiftUI
struct ContentView: View {
@State private var inputText: String = ""
private var displayText: String {
inputText.needsAho ? "🤪" : ""
}
var body: some View {
VStack {
TextField("title", text: $inputText)
Text(displayText)
}.padding()
}
}
2. Publishedを使う
- 1.の方法では、例えば「1秒間の間に連続されて入力された場合、まとめて1回だけ処理を行いたい」といった場合などに対応できません
-
ObservableObject
にPublished
プロパティを生やすことで、Publisherとして扱うことができるようになるので、Combineオペレーターを使えるようになります
import Combine
import SwiftUI
class ViewModel: ObservableObject {
@Published var inputText: String = ""
@Published var displayText: String = ""
private var cancellables: Set<AnyCancellable> = []
init() {
$inputText
.debounce(for: 1.0, scheduler: DispatchQueue.main)
.map { $0.needsAho ? "🤪" : "" }
.receive(on: DispatchQueue.main)
.assign(to: \.displayText, on: self)
.store(in: &self.cancellables)
}
deinit {
self.cancellables.forEach { $0.cancel() }
}
}
struct ContentView: View {
@ObservedObject private var viewModel = ViewModel()
var body: some View {
VStack {
TextField("title", text: $viewModel.inputText)
Text(viewModel.displayText)
}.padding()
}
}
3. CurrentValueSubjectを使う
- 2の方法では、いちいち
ObservableObject
を作らなくてはなりません -
CurrentValueSubject
を@State
プロパティで持っておけば、Viewだけで同様の挙動を実現できます
import Combine
import SwiftUI
struct ContentView: View {
@State private var inputTextSubject: CurrentValueSubject<String, Never> = .init("")
@State private var displayText: String = ""
@State private var cancellables: Set<AnyCancellable> = []
var body: some View {
VStack {
TextField("title", text: $inputTextSubject.value)
Text(displayText)
}
.padding()
.onAppear {
self.inputTextSubject
.debounce(for: 1.0, scheduler: DispatchQueue.main)
.map { $0.needsAho ? "🤪" : "" }
.receive(on: DispatchQueue.main)
.assign(to: \.displayText, on: self)
.store(in: &self.cancellables)
}
.onDisappear {
self.cancellables.forEach { $0.cancel() }
}
}
}