我有一个观点:
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
且@Published
观察到这些变化时,Form
发生变化时,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))
}
}
是否不应该更新?
附加上下文
以下是上述组件的其他代码
VideoView videoView = findViewById(R.id.videoView);
videoView.setVideoPath("android.resource://" + getPackageName() + "/" + R.raw.sample);
videoView.start();
答案 0 :(得分:1)
我认为问题出在缺少自定义组件。因为在下面的简单演示中,提供的基础结构的复制实际上可以按预期与ObservableObject
一起很好地工作。
通过Xcode 11.4 / iOS 13.4测试。与下面的演示进行比较可能有助于查找代码中缺少的内容。
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()
}
}