在App开发过程中,经常会遇到类似下面的需求,随着输入文字数的增加,需要我们动态调整文本框的行数,以便可以把入力文字的都显示出来。所以我们必须要监听到输入框文字的输入的事件。这样的话,SwiftUI的输入框控件TextField是不合适的,因为SwiftUI的TextField是单行的,并且没法监听到文字输入的事件,所以我们这里采用了UIKit的UITextView。至少在目前阶段,SwiftUI是很难独立开发一款功能强大的App,很多情况下还是需要和UIkit一起合作,借助UIlkit完整的体系,二者相互嵌套形成混合开发,目前主要是在SwiftUI中使用UIkit。
1.要在SwiftUI中使用UITextView, 我们需要用到UIViewRepresentable, 对UITextView进行包装。
// 定义SwiftUI的ChatTextView,并且实现UIViewRepresentable struct ChatTextView: UIViewRepresentable { @Binding var myTextView: UITextView // UITextView高度行数 @Binding var contentHeight: CGFloat // 在makeUIView方法中,创建UITextView func makeUIView(context: Context) -> UITextView { let view = UITextView() // 是否可以滚动 view.isScrollEnabled = true // 是否可以编辑 view.isEditable = true // 输入文字颜色 view.textColor = .black // 输入文字字号 view.font = UIFont.systemFont(ofSize: 16); // 输入框圆角 view.layer.cornerRadius = 6 // 输入框背景颜色 view.backgroundColor = UIColor.init(red: 255 / 255, green: 255 / 255, blue: 255 / 255, alpha: 1) // 指定输入框各种事件的delegate代理 view.delegate = context.coordinator // 输入框弹出键盘类型 view.keyboardType = .default // 输入框边框宽度 view.layer.borderWidth = 1 // 输入框边框颜色 view.layer.borderColor = CGColor.init(srgbRed: 128 / 255, green: 128 / 255, blue: 128 / 255, alpha: 0.2) return view } // 没有需要的话,不用特别处理。 func updateUIView(_ uiView: UITextView, context: Context) { } func makeCoordinator() -> ChatTextView.Coordinator { Coordinator(self) } // 内部类,指定ChatTextView为Coordinator的成员变量,这样Coordinator成了ChatTextView和UITextView之间的桥梁,形成了类似桥接的效果。 class Coordinator: NSObject, UITextViewDelegate { // 指定ChatTextView为Coordinator的成员变量 var control: ChatTextView init(_ control: ChatTextView) { self.control = control } // 键盘弹出事件,在这里可以控制View视图的offset,避免视图被弹出键盘遮挡。 func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { self.control.myTextView = textView NotificationCenter.default.post(name: .editBegin, object: nil, userInfo: nil) return true } // 键盘关闭事件,在这里可以控制View视图的offset,键盘关闭时,复原视图位置。 func textViewShouldEndEditing(_ textView: UITextView) -> Bool { NotificationCenter.default.post(name: .editEnd, object: nil, userInfo: nil) textView.endEditing(true) return true } // ※键盘输入文字事件,在这里面可以获得UITextView当前的行高,我们要利用这个行高来改变ChatTextView的行高。 func textViewDidChange(_ textView: UITextView) { // 设定最小行高 if textView.contentSize.height == 36 { self.control.contentHeight = 45 // 限制最大行高 } else if textView.contentSize.height >= 74 { self.control.contentHeight = 74 } else { // 通过桥接可以把行高传递出去 self.control.contentHeight = textView.contentSize.height } } } }
2.在别的View中,可以使用我们上面定义好的ChatTextView控件, 并且根据输入文字数,动态调整显示的行数了。
import SwiftUI struct MessageHistoryView: View { @State var myTextView: UITextView = UITextView.init() @State var contentHeight: CGFloat = 45 var body: some View { // binding contentHeight值就是实时得到的行高 // 利用frame的height属性来调整ChatTextView的高度 ChatTextView(myTextView: self.$myTextView, contentHeight: self.$contentHeight).frame(width: UIScreen.main.bounds.width - 90, height: self.contentHeight) } }