@State更新视图,但@ObservedObject不更新视图

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了@State更新视图,但@ObservedObject不更新视图相关的知识,希望对你有一定的参考价值。

我有一个观点。

struct Form: View {
  @ObservedObject var model = FormInput()

  var body: some View {
    Form {
      TextArea("Details", text: $model.details.value)
        .validation(message: model.details.message)
    }
  }
}

哪儿 TextArea 是一个自定义视图和 .validation(message: model.details.message) 是一个自定义视图修改器。我的 FormInput 看起来如下。

final class FormInput: ObservableObject {
  @Published var details: TextInput

  // ... other code
}

TextInput 是一个自定义结构。

现在,当我运行上述代码时,验证从未触发,因为这个 Form 从不重新渲染,但如果我改变了 Form 到。

 struct MyForm: View {
      @State var details = TextInput()

      var body: some View {
        Form {
          TextArea("Details", text: $details.value)
            .validation(message: details.message)
        }
      }
    }

然后一切都能如期进行 为什么第二个版本的视图会渲染成 Form 但第一个就不行了?难道不应该是 Form 在第一种情况下,更新时 details 变化以来 details@PublishedForm 正在观察这些变化?

补充内容

以下是上述组件的附加代码

final class FormInput: ObservableObject {
  @Published var details: TextInput

  init(details: String = "") {
    self.details = TextInput(value: details, isValid: false, validations: [.length(12)])
  }

  // other code
}

struct TextInput {
  var value: String {
    didSet {
      self.validate()
    }
  }
  var validations: [TextValidation] // this is just an enum of different types of validations
  var isValid: Bool
  var message: String


  mutating func validate() {
    for validation in validations {
      // run validation
    }
  }
}

struct TextArea: View {
  @Binding var text: String
  @State var height: CGFloat = 12

  init(text: Binding<String>) {
    self._text = text
  }

  var body: some View {
    TextAreaField(text: $text)
  }
}

 struct TextAreaField: UIViewRepresentable {
   @Binding var text: String
   @Binding var height: CGFloat


   func makeUIView(context: Context) -> UITextView {
     let textField = UITextView()

     textField.isEditable = true
     textField.isSelectable = true
     textField.isUserInteractionEnabled = true
     textField.isScrollEnabled = false
     // ..other initializers removed for brevity

      textField.delegate = context.coordinator
      return textField
    }

   func updateUIView(_ uiView: UITextView, context: Context) {
     calculateHeight(uiView)
   }

   func calculateHeight(_ uiView: UIView) {
        let newSize = uiView.sizeThatFits(CGSize(width: uiView.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
        if self.height != newSize.height {
          DispatchQueue.main.async  {
            self.height = newSize.height
          }
        }
      }


   func makeCoordinator() -> Coordinator {
     return Coordinator(self)
   }


   final class Coordinator: NSObject, UITextViewDelegate {
     var parent: TextAreaField


     init(_ parent: TextAreaField) {
       self.parent = parent
     }

     func textViewDidChange(_ uiView: UITextView) {
       self.parent.text = uiView.text
       self.parent.calculateHeight(uiView)
      }

    }
}

struct Validation: ViewModifier {
  let message: String

  func body(content: Content) -> some View {
    let isValid = message == ""
    print(message)

    return VStack(alignment: .leading) {
      content

      if isValid == false {
        Text(message)
          .font(.footnote)
          .italic()
          .foregroundColor(Color.red)
          .frame(height: 24)
      }

    }
    .padding(.bottom, isValid ? 24 : 0)
  }
}


extension View {
  func validation(message: String) -> some View {
    self.modifier(Validation(message: message))
  }
}
答案

我想问题出在没有自定义组件的地方。因为下面简单的演示复制所提供的基础架构工作得很好。ObservableObject,其实和预期一样。

用Xcode 11.4 ios 13.4测试。与下面的演示进行比较,可能会有助于找到你的代码中的遗漏之处。

demo

struct TextInput {
    var value: String = "" {
        didSet {
            message = value    // just duplication for demo
        }
    }
    var message: String = ""
}

// simple validator highlighting text when too long
struct ValidationModifier: ViewModifier {
    var text: String

    func body(content: Content) -> some View {
        content.foregroundColor(text.count > 5 ? Color.red : Color.primary)
    }
}

extension View {   // replicated 
    func validation(message: String) -> some View {
        self.modifier(ValidationModifier(text: message))
    }
}

struct MyForm: View {    // avoid same names with standard views
  @ObservedObject var model = FormInput()

  var body: some View {
    Form {
      TextField("Details", text: $model.details.value) // used standard
        .validation(message: model.details.message)
    }
  }
}

final class FormInput: ObservableObject {
  @Published var details: TextInput = TextInput()
}

struct TestObservedInModifier: View {
    var body: some View {
        MyForm()
    }
}

以上是关于@State更新视图,但@ObservedObject不更新视图的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 更新@State 不会改变视图

StateObject 属性不会更新视图,但 ObservedObject 会

ReactNative state更新,视图不更新的问题

SwiftUI:如何在函数计算 @State 值时同时更新视图?

@state 变量更改后视图不更新

SwiftUI,为啥部分视图没有刷新? @State 变量未更新