目 录CONTENT

文章目录

SwiftUI案例:尺寸自适应文本框

Dioxide-CN
2022-04-19 / 0 评论 / 4 点赞 / 37 阅读 / 2,796 字
温馨提示:
本文最后更新于 2022-04-22,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

SwiftUI案例:尺寸自适应文本框

效果

自适应

目标

  • 实现文本框可以单行、多行输入的功能并可以自使用文本内容的高度

思路突破

SwiftUI 并未提供可自适应高度的文本框组件,为实现自适应高度则需要继承 UITextField 进而自定义封装一个弹性的文本框组件。
通过更新函数,从该弹性文本框中获得文本内容的高度并将其赋值给组件的高度,即可实现“弹性”伸缩的效果。

视图实现

import SwiftUI

struct ContentView: View {
    var body: some View {
        Home()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct Home: View {
    @State var text = ""
    //自动更新的文本框高度
    @State var containHeight: CGFloat = 0
    var body: some View {
        //导航区视图控制
        NavigationView {
            //使用垂直布局
            VStack {
                //调用自定义组件AutoSizingTF
                AutoSizingTF(
                    hint: "请输入内容...",
                    text: $text,
                    containerHeight: $containHeight,
                    onEnd: {
                    //当键盘被关闭时调用该方法
                        UIApplication
                            .shared
                            .sendAction(
                                #selector(
                                    UIResponder.resignFirstResponder
                                ),
                                to: nil,
                                from: nil,
                                for: nil
                            )
                    }
                )
                .padding(.horizontal)
                .frame(height: containHeight < 120 ? containHeight : 120)
                .background(Color.white)
                .cornerRadius(10)
                .padding()
            }
            //导航区域的头部文本信息
            .navigationTitle("在输入框中输入文本")
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.primary.opacity(0.04).ignoresSafeArea())
        }
    }
}

//封装自适应文本框组件AutoSizingTF
struct AutoSizingTF: UIViewRepresentable {
    //参数列表
    var hint: String //Placeholder占位
    @Binding var text: String //文本
    @Binding var containerHeight: CGFloat //文本框高度
    var onEnd : () -> () //尾随闭包函数
    
    func makeCoordinator() -> Coordinator {
        return AutoSizingTF.Coordinator(parent:  self)
    }
    
    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView() //实例化文本框组件
        //原生组件样式控制
        textView.text = hint
        textView.textColor = .gray
        textView.backgroundColor = .clear
        textView.font = .systemFont(ofSize: 20)
        textView.delegate = context.coordinator
        //定义输入框附件toolbar(工具栏)并使用默认样式
        let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
        toolBar.barStyle = .default
        //使用另一个spacer作为间隔来使得done完成按钮布局在右侧
        let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        //定义done完成按钮
        let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: context.coordinator, action: #selector(context.coordinator.closeKeyBoard))
        
        toolBar.items = [spacer, doneButton]
        toolBar.sizeToFit()
        textView.inputAccessoryView = toolBar
        //返回这个UIViewRepresentable组件
        return textView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        //自适应文本高度函数
        DispatchQueue.main.async {
            if containerHeight == 0 {
                //将内容文本的高度赋值给弹性文本框的高度变量
                containerHeight = uiView.contentSize.height
            }
        }
    }
    
    
    class Coordinator: NSObject, UITextViewDelegate {
        //读取所有的父属性
        var  parent: AutoSizingTF
        init(parent: AutoSizingTF) {
            self.parent = parent
        }
        //键盘关闭时
        @objc func closeKeyBoard() {
            parent.onEnd()
        }
        
        func textViewDidBeginEditing(_ textView: UITextView) {
            if textView.text == parent.hint {
                textView.text = ""
                textView.textColor = UIColor(Color.primary)
            }
        }
        
        
        func textViewDidChange(_ textView: UITextView) {
            parent.text = textView.text
            parent.containerHeight = textView.contentSize.height
        }
        
        //检查文本框是否内容为空,如果为空则用hint的值覆盖
        func textViewDidEndEditing(_ textView: UITextView) {
            if textView.text == "" {
                //覆盖组件
                textView.text = parent.hint
                textView.textColor = .gray
            }
        }
        
    }
}

源码


4

评论区