swiftuinsattributedstringuiviewcontrollerrepresentable

Custom UIViewController with UIViewcontrollerRepresentable that has a UITextView that crashes or is nil when called in SwiftUI


I have made a custom UIViewController called ViewControllerA and want to be able to use it so I made a UIViewControllerRepresentable called ViewControllerARepresentable as shown below, the problem though is that when I call ViewControllerARepresentable in my SwiftUI view and pass a value for stringToUpdateTextView, the ViewControllerA says the htmlTextView(UITextView) in ViewControllerA is nil and I'm not sure why.

ViewControllerARepresentable(stringToUpdateTextView: "<html>Send some Html Text as string here</html>")

ViewControllerARepresentable

public struct ViewControllerARepresentable: UIViewControllerRepresentable {
  var stringToUpdateTextView: String

  public func makeUIViewController(context: Context) -> ViewControllerA {
    let viewcontrollerA = ViewControllerA(testString: testingString)

    return viewcontrollerA
  }

  public func updateUIViewController(_ uiViewController: ViewControllerA, context: Context) {}
}

ViewControllerA

open class ViewControllerA: UIViewController {

  public var stringToUpdateTextView: String

  override open func viewDidLoad() {
    super.viewDidLoad()

htmlTextView.text = stringToUpdateTextView
  }

  @IBOutlet weak var htmlTextView: UITextView!

  public init(testString: String) {
    self.testString = testString
    super.init(nibName: nil, bundle: nil)
  }

  required public init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}

Crash occurs at htmlTextView.text = stringToUpdateTextView saying that htmlTextView.text is nil even though its an IBOutlet. Any Change made to the htmlTextView like background color ,etc, also causes a crash if called in viewDidAppear or viewDidLoad


Solution

  • When instantiating your view controller in makeUIViewController, the outlets haven't been initialised yet.

    The following code loads your view controller from the storyboard, and updates the properties in updateUIViewController:

    ViewController.swift

    import UIKit
    import SwiftUI
    
    class ViewController: UIViewController {
    
      @IBOutlet weak var htmlTextView: UITextView!
    
      override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
      }
    
    }
    
    struct ViewControllerWrapper: UIViewControllerRepresentable {
      typealias UIViewControllerType = ViewController
    
      @Binding var text: String
    
      func makeUIViewController(context: Context) -> ViewController {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        guard let viewController =  storyboard.instantiateViewController(
          identifier: "ViewController") as? ViewController else {
            fatalError("Cannot load from storyboard")
        }
        return viewController
      }
    
      func updateUIViewController(_ uiViewController: ViewController, context: Context) {
        uiViewController.htmlTextView.text = text
      }
    
    }
    
    
    struct ViewControllerPreview: PreviewProvider {
      static var previews: some View {
        ViewControllerWrapper(text: .constant("hello world!"))
      }
    }
    

    SwiftUIView.swift

    struct SwiftUIView: View {
      @State var text = "Text"
      var body: some View {
        HStack {
          TextField("Text:", text: $text)
          ViewControllerWrapper(text: $text)
        }
      }
    }