6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

@Stateの値によって処理を制御する方法いくつか

Last updated at Posted at 2020-03-25

はじめに

@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()
    }
}
sample1.gif

2. Publishedを使う

  • 1.の方法では、例えば「1秒間の間に連続されて入力された場合、まとめて1回だけ処理を行いたい」といった場合などに対応できません
  • ObservableObjectPublished プロパティを生やすことで、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()
    }
}
sample2.gif

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() }
        }
    }
}
sample3.gif
6
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?