ios - 如何将`@Binding`重写为ViewModel
问题描述
我只想在下面做一个ViewModel
。CustomTextField
import SwiftUI
struct ContentView: View {
@State private var text = ""
var body: some View {
VStack {
CustomTextField(text: $text)
Text(text)
}
}
}
struct CustomTextField: View {
@Binding var text: String
var body: some View {
TextField("title", text: $text)
}
}
我尝试按如下方式重写它,但我不知道如何正确地重写它。
import SwiftUI
import Combine
struct ContentView: View {
@State private var text = ""
var body: some View {
VStack {
CustomTextField(text: $text)
Text(text)
}
}
}
struct CustomTextField: View {
@StateObject private var viewModel = CustomTextFieldViewModel()
// @Binding var text: String <-- How to replace ViewModel
var body: some View {
TextField("title", text: $text)
}
}
final class CustomTextFieldViewModel: ObservableObject {
private var cancellables: Set<AnyCancellable> = []
@Published var text = ""
}
如果我不使用@Binding
,我将无法以父母的观点进行通知。这CustomTextField
是模块化的,我希望它能够被任何父母使用,但我不知道如何编写它来模仿@Binding
使用 ViewModel。(换句话说,我想以某种方式链接ContentView's text
到ViewModel's text
。)
我这样做的方式有什么根本错误吗?
或者
Property Wrappers
是否可以通过使用另一个不是的来解决问题@Published
?
或者
有没有办法初始化 ViewModel 来解决这个问题?
解决方案
如果我理解正确,您希望@State
在父视图 ( ContentView
) 中使用 a ViewModel
,在子视图 ( CustomTextField
) 中使用 a。所以你必须@Binding
在你的 ViewModel 中声明:
final class CustomTextFieldViewModel: ObservableObject {
@Binding var text: String
init(text: Binding<String>) {
_text = text
}
}
struct CustomTextField: View {
@ObservedObject private var viewModel: CustomTextFieldViewModel
init(text: Binding<String>) {
viewModel = CustomTextFieldViewModel(text: text)
}
var body: some View {
TextField("title", text: $viewModel.text)
}
}
struct ContentView: View {
@State private var text = ""
var body: some View {
VStack {
CustomTextField(text: $text)
Text(text)
}
}
}
但是,您不应该使用此解决方案。@Binding
并且@State
只在View
s 中使用。
您可以使用单个 ViewModel,在父视图中实例化,并由两个视图共享(如在此答案中-编辑:或在@workingdog 的答案中)。
或者,如果您想使用两种不同的 ViewModel(一个用于管理ContentView
,另一个用于CustomTextField
),您可以使用 Combine :
struct ContentView: View {
@StateObject private var vm = ContentViewViewModel()
var body: some View {
VStack {
CustomTextField(viewModel: vm.customTextFieldVM)
TextField("", text: $vm.text)
Text(vm.text)
}
}
}
struct CustomTextField: View {
@ObservedObject var viewModel: CustomTextFieldViewModel
var body: some View {
TextField("title", text: $viewModel.text)
}
}
final class ContentViewViewModel: ObservableObject {
@Published var text: String {
didSet {
if text != self.customTextFieldVM.text {
self.customTextFieldVM.text = text
}
}
}
var customTextFieldVM: CustomTextFieldViewModel
var cancellables: Set<AnyCancellable> = []
init() {
text = ""
customTextFieldVM = CustomTextFieldViewModel()
customTextFieldVM.$text
.sink {
if $0 != self.text {
self.text = $0
}
}
.store(in: &cancellables)
}
}
final class CustomTextFieldViewModel: ObservableObject {
@Published var text: String
init() {
text = ""
}
}
推荐阅读
- r - 这个带有 ggrepel 的 ggplot 代码会导致其他人的 R 崩溃吗?
- html - 单个元素的外边距为零
- java - Razorpay 的 Payment 对象没有任何字段
- spring - zk-grails 和 spring 安全集成
- dart - 离开注册屏幕
- paypal - Django-Paypal:我如何收到买家的账单地址?
- java - Tomcat打开文件过多错误(Ubuntu 18.04)
- php - 部署到 Heroku 时安装依赖项时出错
- codenameone - 代号一android在firebase上注册推送通知失败,返回INVALID_PARAETERS错误字符串
- javascript - 如何访问子组件的方法?