3

I have created a SwiftUI TextView based on a UITextView using UIViewRepresentable (s. code below). Displaying text in Swiftui works OK.

But now I need to access internal functions of UITextView from my model. How do I call e.g. UITextView.scrollRangeToVisible(_:) or access properties like UITextView.isEditable ?

My model needs to do these modifications based on internal model states.

Any ideas ? Thanks

(p.s. I am aware of TextEditor in SwiftUI, but I need support for iOS 13!)

struct TextView: UIViewRepresentable {
  @ObservedObject var config: ConfigModel = .shared
  @Binding var text: String
  
  @State var isEditable: Bool
  var borderColor: UIColor
  var borderWidth: CGFloat
  
  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }
  
  func makeUIView(context: Context) -> UITextView {
    let myTextView = UITextView()
    myTextView.delegate = context.coordinator
    
    myTextView.isScrollEnabled = true
    myTextView.isEditable = isEditable
    myTextView.isUserInteractionEnabled = true
    myTextView.layer.borderColor = borderColor.cgColor
    myTextView.layer.borderWidth = borderWidth
    myTextView.layer.cornerRadius = 8
    return myTextView
  }
  
  func updateUIView(_ uiView: UITextView, context: Context) {
    uiView.font = uiView.font?.withSize(CGFloat(config.textsize))
    uiView.text = text
  }
  
  class Coordinator : NSObject, UITextViewDelegate {
    
    var parent: TextView
    
    init(_ uiTextView: TextView) {
      self.parent = uiTextView
    }
    
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
      return true
    }
    
    func textViewDidChange(_ textView: UITextView) {
      self.parent.text = textView.text
    }
  }
}
1
  • I know this post, but my problem how to access UIKit functions during run time is not addressed there. Commented Dec 7, 2020 at 12:06

1 Answer 1

2

You can use something like configurator callback pattern, like

struct TextView: UIViewRepresentable {
  @ObservedObject var config: ConfigModel = .shared
  @Binding var text: String
  
  @State var isEditable: Bool
  var borderColor: UIColor
  var borderWidth: CGFloat
  var configurator: ((UITextView) -> ())?     // << here !!

  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }
  
  func makeUIView(context: Context) -> UITextView {
    let myTextView = UITextView()
    myTextView.delegate = context.coordinator
    
    myTextView.isScrollEnabled = true
    myTextView.isEditable = isEditable
    myTextView.isUserInteractionEnabled = true
    myTextView.layer.borderColor = borderColor.cgColor
    myTextView.layer.borderWidth = borderWidth
    myTextView.layer.cornerRadius = 8
    return myTextView
  }

  func updateUIView(_ uiView: UITextView, context: Context) {
    uiView.font = uiView.font?.withSize(CGFloat(config.textsize))
    uiView.text = text

    // alternat is to call this function in makeUIView, which is called once,
    // and the store externally to send methods directly.
    configurator?(myTextView)                  // << here !!
  }

  // ... other code
}

and use it in your SwiftUI view like

TextView(...) { uiText in
   uiText.isEditing = some
}

Note: depending on your scenarios it might be additional conditions need to avoid update cycling, not sure.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.