在 SwiftUI 中,如何在调用 updateUIView 时从 UIViewRepresentable 访问属性?

问题描述 投票:0回答:1

我在尝试从 SwiftUI 中的 UIKit 组件访问

count
等属性时遇到问题,编译器发出紫色警告:

在视图更新期间修改状态,这将导致未定义的行为。

尽管将状态修改包含在

DispatchQueue.main.asyncAfter
内可以避免出现警告,但它被认为是次优解决方案。直接将 uiView 暴露给 SwiftUI 进行属性访问是另一种解决方法,但它也不太理想。

那么还有其他方法吗?

是否有更合适的方法来实现这一目标而不诉诸这些解决方法?

请注意,下面的示例经过简化以说明问题。虽然我知道

text.count
可以在 SwiftUI 中直接获取,但在某些情况下需要通过 UIKit 组件访问此类属性。

struct ContentView: View {
    @State private var text = ""
    @State private var count = 0
    var body: some View {
        VStack {
            UIKitTextField(text: $text, count: $count)
            Text("Word count: \(count)")
        }
    }
}
struct UIKitTextField: UIViewRepresentable {
    @Binding var text: String
    @Binding var count: Int
    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField(frame: .zero)
        textField.delegate = context.coordinator
        return textField
    }
    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = text
        count = uiView.text?.count ?? 0
//        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
//            count = uiView.text?.count ?? 0
//        }
    }
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: UIKitTextField
        init(_ textField: UIKitTextField) {
            self.parent = textField
        }
        func textFieldDidChangeSelection(_ textField: UITextField) {
            parent.text = textField.text ?? ""
        }
    }
}

更新另一个示例

struct UIKitImageView: UIViewRepresentable {
    let asset: Asset?
    let hdrOpen: Bool
    @Binding var isImageHDR: Bool
    func makeUIView(context: Context) -> UIImageView {
        let view = UIImageView()
        update(view)
        view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        view.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
        view.setContentHuggingPriority(.required, for: .horizontal)
        view.setContentHuggingPriority(.required, for: .vertical)
        return view
    }
    func updateUIView(_ view: UIImageView, context: Context) {
        update(view)
    }
    func update(_ view: UIImageView) {
        if let url = asset?.file {
            view.image = ImageReader.image(contentsOf: url)
        } else if let data = asset?.data {
            view.image = ImageReader.image(data: data)
        } else {
            view.image = UIImage()
        }
        if hdrOpen {
            view.preferredImageDynamicRange = .high
        } else {
            view.preferredImageDynamicRange = .standard
        }
        isImageHDR = view.image!.isHighDynamicRange
    }
}
swift swiftui uikit
1个回答
0
投票

您在可表示的实现中犯了一个错误,请像这样:

    func updateUIView(_ uiView: UITextField, context: Context) {
        context.coordinator.textDidChange = nil
        uiView.text = text
        //count = uiView.text?.count ?? 0 make this a computed property in the parent View
        context.coordinator.textDidChange = { newText in
            text = newText
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator() // can't pass self because this is a struct not an object 
    }

由于文本状态是你的事实来源,你必须通过任何其他方式的计算属性访问它的长度,并且它会不一致。

struct ContentView: View {
    @State private var text = ""
    var count: Int {
        text.count
    }
    var body: some View {
        VStack {
            UIKitTextField(text: $text)
            Text("Word count: \(count)")
        }
    }
}

第二个示例的固定代码:


   let url: URL

   func makeUIView(context: Context) -> UIImageView {
        let view = UIImageView()
        //update(view) updateUIView is already called after makeUIView so this is unnecessary
        view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        view.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
        view.setContentHuggingPriority(.required, for: .horizontal)
        view.setContentHuggingPriority(.required, for: .vertical)
        return view
    }
    func updateUIView(_ view: UIImageView, context: Context) {
        // need to check if url has changed
        if url != context.coordinator.url {
            update(view)
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.