将 SwiftUI 数据源放在其他地方
Placing SwiftUI Data Sources Somewhere Else
我正在尝试在一个项目中使用 SwiftUI,但超出了每个教程中都可以找到的使用 @States 和 @Bindings 的最基本版本,所以我需要一些帮助来解决我的问题我这里做错了。
环境设置:
我有以下涉及此问题的文件:
- CustomTextField:这是一个 SwiftUI 视图,包含一个内部 TextField 以及其他一些东西(根据设计)
- CustomTextFieldConfiguration:包含我需要在自定义文本字段视图上配置的内容
- RootView:这是一个 SwiftUI 视图,它使用 CustomTextField 作为其子视图之一
- RootPresenter:这是 UI 逻辑和表示逻辑(在视图和业务逻辑之间)
- RootPresentationModel: 是Presenter可以修改视图状态的viewModel
- RootBuilder: 它包含使用构建器模式将组件连接在一起的构建器class
问题:
- textValue 属性 的 rootPresentationModel
中的 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
...
...
...
...
根视图:
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))
}
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,但超出了每个教程中都可以找到的使用 @States 和 @Bindings 的最基本版本,所以我需要一些帮助来解决我的问题我这里做错了。
环境设置: 我有以下涉及此问题的文件:
- CustomTextField:这是一个 SwiftUI 视图,包含一个内部 TextField 以及其他一些东西(根据设计)
- CustomTextFieldConfiguration:包含我需要在自定义文本字段视图上配置的内容
- RootView:这是一个 SwiftUI 视图,它使用 CustomTextField 作为其子视图之一
- RootPresenter:这是 UI 逻辑和表示逻辑(在视图和业务逻辑之间)
- RootPresentationModel: 是Presenter可以修改视图状态的viewModel
- RootBuilder: 它包含使用构建器模式将组件连接在一起的构建器class
问题:
- textValue 属性 的 rootPresentationModel 中的 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
...
...
...
...
根视图:
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))
}
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()
}
}