将 SwiftUI 数据源放在其他地方
Posted
技术标签:
【中文标题】将 SwiftUI 数据源放在其他地方【英文标题】:Placing SwiftUI Data Sources Somewhere Else 【发布时间】:2021-03-14 19:30:31 【问题描述】:我正在尝试在项目中使用 SwiftUI,但超出了在每个教程中都可以找到的使用 @States 和 @Bindings 的非常基本的版本,所以我需要一些帮助来解决我在这里做错的地方。
环境设置: 我有以下与此问题有关的文件:
CustomTextField:它是一个 SwiftUI 视图,包含一个内部 TextField 以及其他一些东西(根据设计) CustomTextFieldConfiguration:包含我需要在自定义文本字段视图上配置的内容 RootView:这是一个使用 CustomTextField 作为其子视图之一的 SwiftUI 视图 RootPresenter:这是 UI 逻辑和表示逻辑所在的位置(在视图和业务逻辑之间) RootPresentationModel:它是 ViewModel,Presenter 可以通过它修改视图的状态 RootBuilder:它包含使用构建器模式将组件连接在一起的构建器类问题:
rootPresentationModel 的 textValue 属性中的 textField 值未更新这是我所做的(部分)实现,但不知道我哪里出错了:
自定义文本字段:
struct CustomTextField: View
@Binding var config: CustomTextFieldConfiguration
var body: some View
ZStack
VStack
VStack
ZStack
HStack
TextField($config.placeHolder,
value: $config.textValue,
formatter: NumberFormatter(),
onEditingChanged: _ in ,
onCommit: )
.frame(height: 52.0)
.padding(EdgeInsets(top: 0, leading: 16 + ($config.detailActionImage != nil ? 44 : 0),
bottom: 0, trailing: 16 + ($config.contentAlignment == .center && $config.detailActionImage != nil ? 44 : 0)))
.background($config.backgroundColor)
.cornerRadius($config.cornerRedius)
.font($config.font)
...
...
...
...
CustomTextFieldConfiguration:
struct CustomTextFieldConfiguration
@Binding var textValue: String
...
...
...
...
RootView:
struct RootView: View
@State var configuration: CustomTextFieldConfiguration
var interactor: RootInteractorProtocol!
@Environment(\.colorScheme) private var colorScheme
var body: some View
HStack
Spacer(minLength: 40)
VStack(alignment: .trailing)
CustomTextField(config: $configuration)
Text("\(configuration.textValue)")
Spacer(minLength: 40)
RootPresenter:
class RootPresenter: BasePresenter
@ObservedObject var rootPresentationModel: RootPresentationModel
init(presentationModel: RootPresentationModel)
rootPresentationModel = presentationModel
...
...
...
RootPresentationModel:
class RootPresentationModel: ObservableObject
var textValue: String = ""
didSet
print(textValue)
RootBuilder:
class RootBuilder: BaseBuilder
class func build() -> (RootView, RootInteractor)
let interactor = RootInteractor()
let presenter = RootPresenter(presentationModel: RootPresentationModel())
let view: RootView = RootView(configuration: CustomTextFieldConfiguration.Presets.priceInput(textValue: presenter.$rootPresentationModel.textValue, placeholder: "", description: ""), interactor: interactor)
let router = RootRouter()
interactor.presenter = presenter
interactor.router = router
return (view, interactor)
(Presets 方法没有做任何重要的事情,只是为了确保它不会引发不相关的问题,这是实现):
static func priceInput(textValue: Binding<String>, placeholder: String, description: String) -> CustomTextFieldConfiguration
return CustomTextFieldConfiguration(textValue: textValue,
placeHolder: placeholder,
description: description,
defaultDescription: description,
textAlignment: .center,
descriptionAlignment: .center,
contentAlignment: .center,
font: CustomFont.headline1))
【问题讨论】:
我担心你有一个@Binding
持有另一个@Binding
。我从未见过这样做,如果它导致有趣的结果,我也不会感到惊讶。
我建议对 SwiftUI 数据源使用ObservableObject/ObservedObject
模式。
我也不太明白 RootPresentationModel
在做什么以及为什么它是 ObservableObject,但它的 textValue
属性不是 @Published
。好像如果你在某个地方观察它,你会想要它。
@jnpdx 很高兴您提到,将状态传递给它们之间有多个层的绑定的适当方法是什么?
我尝试了使用@Published 和不使用它。结果没有差异
【参考方案1】:
import SwiftUI
struct CustomTextField: View
@EnvironmentObject var config: CustomTextFieldConfiguration
@Binding var textValue: Double
var body: some View
ZStack
VStack
VStack
ZStack
HStack
//Number formatter forces the need for Double
TextField(config.placeHolder,
value: $textValue,
formatter: NumberFormatter(),
onEditingChanged: _ in ,
onCommit: )
.frame(height: 52.0)
//.padding(EdgeInsets(top: 0, leading: 16 + (Image(systemName: config.detailActionImageName) != nil ? 44 : 0),bottom: 0, trailing: 16 + (config.contentAlignment == .center && Image(systemName: config.detailActionImageName) != nil ? 44 : 0)))
.background(config.backgroundColor)
.cornerRadius(config.cornerRedius)
.font(config.font)
class CustomTextFieldConfiguration: ObservableObject
@Published var placeHolder: String = "place"
@Published var detailActionImageName: String = "checkmark"
@Published var contentAlignment: UnitPoint = .center
@Published var backgroundColor: Color = Color(UIColor.secondarySystemBackground)
@Published var font: Font = .body
@Published var cornerRedius: CGFloat = CGFloat(5)
@Published var description: String = ""
@Published var defaultDescription: String = ""
@Published var textAlignment: UnitPoint = .center
@Published var descriptionAlignment: UnitPoint = .center
init()
init(placeHolder: String, description: String, defaultDescription: String, textAlignment: UnitPoint,descriptionAlignment: UnitPoint,contentAlignment: UnitPoint, font:Font)
self.placeHolder = placeHolder
self.description = description
self.defaultDescription = defaultDescription
self.textAlignment = textAlignment
self.descriptionAlignment = descriptionAlignment
self.contentAlignment = contentAlignment
self.font = font
struct Presets
static func priceInput(placeholder: String, description: String) -> CustomTextFieldConfiguration
return CustomTextFieldConfiguration(placeHolder: placeholder, description: description,defaultDescription: description,textAlignment: .center,descriptionAlignment: .center,contentAlignment: .center, font:Font.headline)
struct RootView: View
@ObservedObject var configuration: CustomTextFieldConfiguration
//var interactor: RootInteractorProtocol!
@Environment(\.colorScheme) private var colorScheme
@Binding var textValue: Double
var body: some View
HStack
Spacer(minLength: 40)
VStack(alignment: .trailing)
CustomTextField(textValue: $textValue).environmentObject(configuration)
Text("\(textValue)")
Spacer(minLength: 40)
//RootPresenter is a class @ObservedObject only works properly in SwiftUI Views/struct
class RootPresenter//: BasePresenter
//Won't work can't chain ObservableObjects
// var rootPresentationModel: RootPresentationModel
//
// init(presentationModel: RootPresentationModel)
// rootPresentationModel = presentationModel
//
class RootPresentationModel: ObservableObject
@Published var textValue: Double = 12
didSet
print(textValue)
struct NewView: View
//Must be observed directly
@StateObject var vm: RootPresentationModel = RootPresentationModel()
//This cannot be Observed
let presenter: RootPresenter = RootPresenter()
var body: some View
RootView(configuration: CustomTextFieldConfiguration.Presets.priceInput(placeholder: "", description: ""), textValue: $vm.textValue//, interactor: interactor
)
struct NewView_Previews: PreviewProvider
static var previews: some View
NewView()
【讨论】:
谢谢,做了一些修改。以上是关于将 SwiftUI 数据源放在其他地方的主要内容,如果未能解决你的问题,请参考以下文章