はじめに
「比較して学ぶRxSwift入門」という書籍にあったテキストフィールドの文字数制限をCombineで実装してみました
完成形
実装
ContentView.swift
import SwiftUI
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
VStack {
TextField("名前", text: $viewModel.name)
.textFieldStyle(.roundedBorder)
Text(viewModel.nameLimitText)
.foregroundColor(viewModel.nameLimitTextColor)
.frame(maxWidth: .infinity, alignment: .trailing)
TextField("住所", text: $viewModel.address)
.textFieldStyle(.roundedBorder)
Text(viewModel.addressLimitText)
.foregroundColor(viewModel.addressLimitTextColor)
.frame(maxWidth: .infinity, alignment: .trailing)
}
.padding()
}
}
ViewModel.swift
import SwiftUI
import Combine
final class ViewModel: ObservableObject {
@Published var name: String = ""
@Published var address: String = ""
@Published var nameLimitText: String = ""
@Published var addressLimitText: String = ""
@Published var nameLimitTextColor: Color = .primary
@Published var addressLimitTextColor: Color = .primary
private let maxNameLength = 10
private let maxAddressLength = 20
private var cancellables = Set<AnyCancellable>()
init() {
$name
.map { $0.count }
.sink { [weak self] count in
guard let self else { return }
let length = self.maxNameLength - count
self.nameLimitText = length >= 0 ? "あと\(length)文字です" : "名前は\(self.maxNameLength)文字以内で設定してください"
self.nameLimitTextColor = length >= 0 ? Color.primary : Color.red
}
.store(in: &cancellables)
$address
.map { $0.count }
.sink { [weak self] count in
guard let self else { return }
let length = self.maxAddressLength - count
self.addressLimitText = length >= 0 ? "あと\(length)文字です" : "住所は\(self.maxAddressLength)文字以内で設定してください"
self.addressLimitTextColor = length >= 0 ? Color.primary : Color.red
}
.store(in: &cancellables)
}
}
おわり
View側がスッキリしていてコードが見やすくなりました