Reputation: 858
Simplest example below. Works fine in preview (UITextView
text updates to "ouch"). But, run it in an app (ie add as rootView
by the sceneDelegate
), and the UITextView
doesn't update.
import SwiftUI
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
Upvotes: 10
Views: 3270
Reputation: 61
I noticed via print statements that makeUIView wasn't being called again after the first URL was successfully loaded. So upon the URL changing, my WebView was remaining on the first URL.
I modified my updateUIView method - which WAS being called when the URL was updated after the first successful load - to check if there was a difference between the active url and the new url. If there was a difference I updated the page with the correct url.
Here is my sample updateUIView method:
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)
}
}
}
Upvotes: 0
Reputation: 527
In my case I need to pass in an observable object into the uiViewRepresentable, so same case as mentioned by Rivera... When a @Published property of that observable object changes, updateUIView is called in the simulator but not on a real device... I'm using the latest Xcode 11.4 but admittedly on a device running iOS 13.3 (13.4.1 not installed yet, so I haven't checked if that bug has been eliminated or not). What solved my problem is the following: change the struct MyTextView into a final class (the final keyword is important), then add an initialiser. It is not even necessary to call .objectWillChange.send() on the observed object right before the change of the published var is triggered.
Upvotes: 0
Reputation: 19946
I had mixed results using the following. I'm pretty sure there is some kind bug with UIViewRepresentable
It worked for someviews, but then I pushed the exact same view again and the view model wouldn't update. Very strange...
Hopefully they release a SwiftUI TextView soon.
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
}
}
}
Upvotes: 0
Reputation: 2866
It looks like some kind of bug of SwiftUI, but there are two workarounds:
@Binding
to 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")
}
}
}
}
Upvotes: 2