Изменения в ObservedObject не обновляют UIViewRepresentable
Самый простой пример ниже. Прекрасно работает в предварительном просмотре (текст UITextView обновляется до "ой"). Но запустите его в приложении (т.е. добавьте как rootView с помощью sceneDelegate), и UITextView не обновится.
class ModelObject : ObservableObject{
@Published var text = "Model Text"
}
struct MyTextView : UIViewRepresentable {
@ObservedObject var modelObject : ModelObject
func makeUIView(context: Context) -> UITextView {
let result = UITextView()
result.isEditable = true
return result
}
func updateUIView(_ view: UITextView, context: Context) {
view.text = modelObject.text
}
}
struct BugDemoView : View{
@ObservedObject var modelObject : ModelObject
var body : some View{
VStack{
MyTextView(modelObject: modelObject)
Button(action: {
self.modelObject.text = "ouch"
}){
Text("Button")
}
}
}
}
#if DEBUG
var mo = ModelObject()
struct BugDemoView_Preview: PreviewProvider {
static var previews: some View {
BugDemoView(modelObject: mo)
}
}
#endif
4 ответа
Похоже на какую-то ошибку SwiftUI, но есть два обходных пути:
- Передать строку как
@Binding
кMyTextView
:
struct MyTextView : UIViewRepresentable {
@Binding var text: String
func makeUIView(context: Context) -> UITextView {
let result = UITextView()
result.isEditable = true
return result
}
func updateUIView(_ view: UITextView, context: Context) {
view.text = text
}
}
struct BugDemoView : View{
@ObservedObject var modelObject = ModelObject()
var body : some View{
VStack{
MyTextView(text: $modelObject.text)
Button(action: {
self.modelObject.text = "ouch"
}){
Text("Button")
}
}
}
}
- Передать строку в
MyTextView
:
struct MyTextView : UIViewRepresentable {
var text: String
func makeUIView(context: Context) -> UITextView {
let result = UITextView()
result.isEditable = true
return result
}
func updateUIView(_ view: UITextView, context: Context) {
view.text = text
}
}
struct BugDemoView: View{
@ObservedObject var modelObject = ModelObject()
var body: some View{
VStack{
MyTextView(text: modelObject.text)
Button(action: {
self.modelObject.text = "ouch"
}){
Text("Button")
}
}
}
}
С помощью операторов печати я заметил, что makeUIView больше не вызывается после успешной загрузки первого URL-адреса. Таким образом, после изменения URL-адреса мой WebView оставался на первом URL-адресе.
Я изменил свой метод updateUIView, который вызывался при обновлении URL-адреса после первой успешной загрузки, чтобы проверить, есть ли разница между активным URL-адресом и новым URL-адресом. Если была разница, я обновил страницу с правильным URL-адресом.
Вот мой пример метода updateUIView:
let request: URLRequest
func updateUIView(_ uiView: WKWebView, context: Context) {
if uiView.canGoBack, webViewStateModel.goBack {
uiView.goBack()
webViewStateModel.goBack = false
} else {
if(uiView.url?.absoluteString == request.url?.absoluteString){
print("The urls are equal")
} else {
print("The urls are NOT equal")
uiView.load(request)
}
}
}
У меня были смешанные результаты, используя следующее. Я почти уверен, что есть какая-то ошибка сUIViewRepresentable
Это сработало для некоторых представлений, но затем я снова нажал то же представление, и модель представления не обновлялась. Очень странный...
Надеюсь, они скоро выпустят SwiftUI TextView.
struct TextView: UIViewRepresentable {
@Binding var text: String
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> UITextView {
let myTextView = UITextView()
myTextView.delegate = context.coordinator
myTextView.font = UIFont(name: "HelveticaNeue", size: 15)
myTextView.isScrollEnabled = true
myTextView.isEditable = true
myTextView.isUserInteractionEnabled = true
myTextView.backgroundColor = UIColor(white: 0.0, alpha: 0.05)
return myTextView
}
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.text = text
}
class Coordinator: NSObject, UITextViewDelegate {
var parent: TextView
init(_ uiTextView: TextView) {
self.parent = uiTextView
}
func textViewDidChange(_ textView: UITextView) {
print("text now: \(String(describing: textView.text!))")
self.parent.text = textView.text
}
}
}
В моем случае мне нужно передать наблюдаемый объект в uiViewRepresentable, так же как и в случае, упомянутом Риверой... Когда свойство @Published этого наблюдаемого объекта изменяется, updateUIView вызывается в симуляторе, но не на реальном устройстве. Я использую последнюю версию Xcode 11.4, но, по общему признанию, на устройстве под управлением iOS 13.3 (13.4.1 еще не установлена, поэтому я не проверял, была ли устранена эта ошибка). Моя проблема решена следующим образом: преобразовать структуру MyTextView в последний класс (последнее ключевое слово важно), а затем добавить инициализатор. Нет необходимости даже вызывать.objectWillChange.send() для наблюдаемого объекта прямо перед тем, как сработает изменение опубликованной переменной.