我在尝试从 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
}
}
您在可表示的实现中犯了一个错误,请像这样:
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)
}
}