如何重新渲染 UI 以响应埋在嵌套 class 中的计算 属性?
How to re-render UI in response to computed property buried in a nested class?
我不清楚如何将以下计算的 属性 的输出合并到 UI。
var isComplete: Bool {
Set([.givenName, .familyName]).isSubset(of: elements)
}
我基本上希望在上述更改时更新用户界面。我将如何使用 Combine 来做到这一点?
响应式编程要求我现在向后思考,我在思考模型 <<< UI 而不是模型 >>> UI.
时遇到了麻烦
这是上下文中的代码。
struct EditPersonView: View {
let model: ViewModel
private var captionView: some View {
HStack {
/*
stuff
*/
if submitted && model.name.isComplete {
Spacer()
Text("select".localizedCapitalized) + Text(" ") + Text("save") + Text(" ") + Text("")
}
}
}
var body: some View {
/*
stuff - including captionView
*/
}
}
extension EditPersonView {
final class ViewModel {
let name: PersonName
init(person: Person) {
self.name = PersonName(for: person)
}
}
}
extension EditPersonView.ViewModel {
final class PersonName {
let person: Person
private let formatter = PersonNameComponentsFormatter()
init(for person: Person) {
self.person = person
}
var text: String {
get { person.name ?? "" }
set { person.name = newValue }
}
private var components: PersonNameComponents? {
formatter.personNameComponents(from: text)
}
var givenName: String? {
components?.givenName
}
var familyName: String? {
components?.familyName
}
private func isValid(component: String?) -> Bool {
if let name = component, name.count > 1 {
return true
}
return false
}
var elements: Set<Elements> {
var collection = Set<Elements>()
if isValid(component: givenName) { collection.insert(.givenName) }
if isValid(component: familyName) { collection.insert(.familyName) }
return collection
}
var isComplete: Bool {
Set([.givenName, .familyName]).isSubset(of: elements)
}
}
}
extension EditPersonView.ViewModel.PersonName {
enum Elements {
case givenName, familyName
}
}
下面是我想出的。
序列的根是 textPublisher
。这从发送到 text
.
的值开始序列
didSet
将文本发送到序列中,并像原始代码一样将其保存在人名中。
isComplete
成为发布者,根据组件是否有效发送 true
或 false
。 map
运算符链中的每个运算符都通过原始代码中的一个计算步骤获取值。你可以很容易地将它减少到一个 map
我想。 (或者将计算过滤到具有有意义名称的函数中,并将函数替换为闭包)
外部 Subscriber
可以订阅 isComplete
并在发出 true
值时响应。
final class PersonName {
var person: Person
private let formatter = PersonNameComponentsFormatter()
let textPublisher = PassthroughSubject<String, Never>()
var text: String {
get { person.name ?? "" }
set { textPublisher.send(newValue); person.name = newValue }
}
var isComplete : AnyPublisher<Bool, Never>!
init(for person: Person) {
self.person = person
isComplete = textPublisher
.map{ self.formatter.personNameComponents(from: [=10=]) }
.map{ (components: PersonNameComponents?) -> Set<Elements> in
var collection = Set<Elements>()
if let components = components {
if self.isValid(component: components.givenName) { collection.insert(.givenName) }
if self.isValid(component: components.familyName) { collection.insert(.familyName) }
}
return collection
}
.map { Set([Elements.givenName, Elements.familyName]).isSubset(of: [=10=]) }
.eraseToAnyPublisher()
}
private func isValid(component: String?) -> Bool {
if let name = component, name.count > 1 {
return true
}
return false
}
}
我不清楚如何将以下计算的 属性 的输出合并到 UI。
var isComplete: Bool {
Set([.givenName, .familyName]).isSubset(of: elements)
}
我基本上希望在上述更改时更新用户界面。我将如何使用 Combine 来做到这一点?
响应式编程要求我现在向后思考,我在思考模型 <<< UI 而不是模型 >>> UI.
时遇到了麻烦这是上下文中的代码。
struct EditPersonView: View {
let model: ViewModel
private var captionView: some View {
HStack {
/*
stuff
*/
if submitted && model.name.isComplete {
Spacer()
Text("select".localizedCapitalized) + Text(" ") + Text("save") + Text(" ") + Text("")
}
}
}
var body: some View {
/*
stuff - including captionView
*/
}
}
extension EditPersonView {
final class ViewModel {
let name: PersonName
init(person: Person) {
self.name = PersonName(for: person)
}
}
}
extension EditPersonView.ViewModel {
final class PersonName {
let person: Person
private let formatter = PersonNameComponentsFormatter()
init(for person: Person) {
self.person = person
}
var text: String {
get { person.name ?? "" }
set { person.name = newValue }
}
private var components: PersonNameComponents? {
formatter.personNameComponents(from: text)
}
var givenName: String? {
components?.givenName
}
var familyName: String? {
components?.familyName
}
private func isValid(component: String?) -> Bool {
if let name = component, name.count > 1 {
return true
}
return false
}
var elements: Set<Elements> {
var collection = Set<Elements>()
if isValid(component: givenName) { collection.insert(.givenName) }
if isValid(component: familyName) { collection.insert(.familyName) }
return collection
}
var isComplete: Bool {
Set([.givenName, .familyName]).isSubset(of: elements)
}
}
}
extension EditPersonView.ViewModel.PersonName {
enum Elements {
case givenName, familyName
}
}
下面是我想出的。
序列的根是 textPublisher
。这从发送到 text
.
didSet
将文本发送到序列中,并像原始代码一样将其保存在人名中。
isComplete
成为发布者,根据组件是否有效发送 true
或 false
。 map
运算符链中的每个运算符都通过原始代码中的一个计算步骤获取值。你可以很容易地将它减少到一个 map
我想。 (或者将计算过滤到具有有意义名称的函数中,并将函数替换为闭包)
外部 Subscriber
可以订阅 isComplete
并在发出 true
值时响应。
final class PersonName {
var person: Person
private let formatter = PersonNameComponentsFormatter()
let textPublisher = PassthroughSubject<String, Never>()
var text: String {
get { person.name ?? "" }
set { textPublisher.send(newValue); person.name = newValue }
}
var isComplete : AnyPublisher<Bool, Never>!
init(for person: Person) {
self.person = person
isComplete = textPublisher
.map{ self.formatter.personNameComponents(from: [=10=]) }
.map{ (components: PersonNameComponents?) -> Set<Elements> in
var collection = Set<Elements>()
if let components = components {
if self.isValid(component: components.givenName) { collection.insert(.givenName) }
if self.isValid(component: components.familyName) { collection.insert(.familyName) }
}
return collection
}
.map { Set([Elements.givenName, Elements.familyName]).isSubset(of: [=10=]) }
.eraseToAnyPublisher()
}
private func isValid(component: String?) -> Bool {
if let name = component, name.count > 1 {
return true
}
return false
}
}