自定义属性包装器无法准确反映我在 SwiftUI 中的 TextField 的状态,知道为啥吗?

Posted

技术标签:

【中文标题】自定义属性包装器无法准确反映我在 SwiftUI 中的 TextField 的状态,知道为啥吗?【英文标题】:Custom property wrapper not accurately reflecting the state of my TextField in SwiftUI, any idea why?自定义属性包装器无法准确反映我在 SwiftUI 中的 TextField 的状态,知道为什么吗? 【发布时间】:2020-08-18 00:32:56 【问题描述】:

我创建了一个属性包装器,我想在其中插入一些逻辑,并且“set”值正在做正确的事情,但文本字段并未使用所有大写文本进行更新。文本字段不应该显示所有大写文本还是我误解了它是如何工作的?

这也是一个人为的例子,我的最终目标是在属性包装器中插入更多的逻辑,我只是​​使用大写的例子来让它工作。我在整个互联网上进行了搜索,但没有找到可行的解决方案。

import SwiftUI
import Combine

struct ContentView: View 
    @StateObject var vm = FormDataViewModel()

    var body: some View 
        Form 
            TextField("Name", text: $vm.name)
        
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    


class FormDataViewModel: ObservableObject 
    @Capitalized var name: String = ""


@propertyWrapper
public class Capitalized 
    @Published var value: String

    public var wrappedValue: String 
        get  value 
        set  value = newValue.uppercased()  //Printing this shows all caps
    

    public var projectedValue: AnyPublisher<String, Never> 
        return $value
            .eraseToAnyPublisher()
    

    public init(wrappedValue: String) 
        value = wrappedValue
    

【问题讨论】:

【参考方案1】:

SwiftUI 监视 @StateObject@ObservedObject 中的 @Published 属性,并根据它们的变化触发 UI 更新。

但它并没有深入ObservableObject。您的 FormDataViewModel 没有任何 @Published 属性。


您可以做的一件事是模拟@Published 对值变化的作用。

class FormDataViewModel: ObservableObject 
    @Capitalized var name: String = ""
    private var nameObserver: AnyCancellable?

    init() 
        nameObserver = _name.$value.sink _ in
            self.objectWillChange.send()
        
    

请尝试。

【讨论】:

非常感谢!这行得通。我的印象是属性包装器可能是封装可重用逻辑的答案,但似乎我仍然需要使用支持变量来实现发布。【参考方案2】:

这可以使用标准的@Published 来完成,看起来更简单、更可靠。

这里有一个解决方案。使用 Xcode 12 / ios 14 测试。

class FormDataViewModel: ObservableObject 
    @Published var name: String = "" 
        didSet 
            let capitalized = name.uppercased()
            if name != capitalized 
                name = capitalized
                objectWillChange.send()
            
        
    

【讨论】:

以上是关于自定义属性包装器无法准确反映我在 SwiftUI 中的 TextField 的状态,知道为啥吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何定义协议以包含带有 @Published 属性包装器的属性

SwiftUI 接收自定义事件

带有选择器的 SwiftUI 列表:选择不适用于自定义类型(macOS)

深度解析:为何在 SwiftUI 视图的 init 初始化器里无法更改 @State 的值?

深度解析:为何在 SwiftUI 视图的 init 初始化器里无法更改 @State 的值?

在 SwiftUI 中使用 @State 属性包装器未更新视图