I have a custom Text Field in SwiftUI, that adjusts the number of lines it has according to the amount of content in the text field. In the app, the user can add text fields, and so I'm storing the heights, and content of the text fields in arrays, and appending to the arrays as more text fields are added.
Whenever I remove the code inside the updateUIView()
, the app runs smoothly but of course, the text fields don't appear, but whenever I include it as in the code below, the CPU spikes to 99%, and the app freezes (even when there's only one text field).
Does anyone know why this is happening, and any fixes to it?
struct CustomMultilineTF: UIViewRepresentable {
@Binding var text: String
@Binding var height: CGFloat
var placeholder: String
func makeCoordinator() -> Coordinator {
return CustomMultilineTF.Coordinator(par: self)
}
func makeUIView(context: Context) -> UITextView {
let view = UITextView()
view.isEditable = true
view.isScrollEnabled = true
view.text = placeholder
view.font = .systemFont(ofSize: 18)
view.textColor = UIColor.gray
view.delegate = context.coordinator
view.backgroundColor = UIColor.gray.withAlphaComponent(0.05)
return view
}
func updateUIView(_ uiView: UITextView, context: Context) {
DispatchQueue.main.async {
self.height = uiView.contentSize.height
}
}
class Coordinator: NSObject, UITextViewDelegate {
var parent: CustomMultilineTF
init(par: CustomMultilineTF) {
parent = par
}
func textViewDidBeginEditing(_ textView: UITextView) {
if self.parent.text == "" {
textView.text = ""
textView.textColor = .black
}
}
func textViewDidChange(_ textView: UITextView) {
DispatchQueue.main.async {
self.parent.height = textView.contentSize.height
self.parent.text = textView.text
}
}
}
}
In your updateUIView
, you're setting a value to self.height
, which is a Binding. My guess is that the @Binding is connected to a property (either another @Binding or a @State on your surrounding view). So, whenever you set a new value to that @Binding, that triggers a refresh of the parent view. That, in turn, ends up calling updateUIView
again, and you get into an infinite loop.
How to solve it probably depends on your architecture needs for the program. If you can get away with not having the parent know the height, you can probably solve it just by having the view update its own height.
You could also try only setting self.height
to a new value if it != the old one -- that would probably short circuit the loop. But, you might end up with other side effects.