Swiftui — доступ к методам / свойствам UIKit из UIViewRepresentable

#swiftui #uikit #uiviewrepresentable

#swiftui #uikit #uiviewrepresentable

Вопрос:

Я создал текстовое представление SwiftUI на основе UITextView, используя UIViewRepresentable (см. код ниже). Отображение текста в Swiftui работает нормально.

Но теперь мне нужно получить доступ к внутренним функциям UITextView из моей модели. Как мне вызвать, например, UITextView.scrollRangeToVisible(_:) или получить доступ к таким свойствам, как UITextView.IsEditable?

Моя модель должна выполнять эти изменения на основе внутренних состояний модели.

Есть идеи? Спасибо

(ps Я знаю о TextEditor в SwiftUI, но мне нужна поддержка 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. Я знаю этот пост, но моя проблема, как получить доступ к функциям UIKit во время выполнения, там не рассматривается.

Ответ №1:

Вы можете использовать что-то вроде configurator шаблона обратного вызова, например

 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
}
 

и используйте его в своем представлении SwiftUI, например

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

Примечание: в зависимости от ваших сценариев могут потребоваться дополнительные условия, чтобы избежать циклического обновления, не уверен.