SwiftUI 单行多行文本输入大合集TextField《寒食帖》

2,535 阅读2分钟

SwiftUI 单行多行文本输入大合集TextField《寒食帖》

效果

Jietu20200206-134350@2x.jpg

单行文本

单行文本

import SwiftUI

struct NormalTextFieldDemo: View {
     @State var oneText = ""
    var body: some View {
           VStack{
                    TextField("单行文本", text: $oneText)
                        .padding()
                    Spacer()
                }.frame(height:60)
                    .background(Color.yellow)
                    .cornerRadius(8)
                    .padding()
    }
}

struct NormalTextFieldDemo_Previews: PreviewProvider {
    static var previews: some View {
        NormalTextFieldDemo()
    }
}

自己封装UITextView实现TextView

SwiftUI目前还处于发展阶段,很多高级控件还没有原生实现。但是依托于Apple全家桶的好处,我们可以复用之前UIKit的控件。

自己封装UITextView实现TextView

import SwiftUI
import UIKit

struct TextView: UIViewRepresentable {
    @Binding var text: String
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> UITextView {
        
        let myTextView = UITextView()
        myTextView.delegate = context.coordinator
        
        myTextView.font = UIFont(name: "HelveticaNeue", size: 15)
        myTextView.isScrollEnabled = true
        myTextView.isEditable = true
        myTextView.isUserInteractionEnabled = true
        myTextView.backgroundColor = UIColor(white: 0.0, alpha: 0.05)
        
        return myTextView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        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) {
            print("text now: \(String(describing: textView.text!))")
            self.parent.text = textView.text
        }
    }
}

测试代码

import SwiftUI

struct TextViewDemo: View {
    @State var text = "此《寒食帖》为北宋元丰二年苏轼谪居黄州,于第三年(元丰五年)四月的寒食日。"
    
    var body: some View {
        //Text("  sd ")
        VStack {
        
        VStack {
            
            Text("输入框 TextView")
                .padding()
            TextView(
                text: $text
            )
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
        }.frame(height:300)
            .background(Color.orange)
            .cornerRadius(8)
            .padding()
        
       VStack {
                  Text("刚才输入的是:")
                      .padding()
                  Divider()
                  Text("\(text)")
                  Spacer()
              }.frame(height:300)
                  .background(Color.purple)
                  .cornerRadius(8)
                  .padding()
        }
    }
}

struct TextViewDemo_Previews: PreviewProvider {
    static var previews: some View {
        TextViewDemo()
    }
}

高级封装实现弹性文本框

import SwiftUI

import SwiftUI
import UIKit

fileprivate struct UITextViewWrapper: UIViewRepresentable {
    typealias UIViewType = UITextView

    @Binding var text: String
    @Binding var calculatedHeight: CGFloat
    var onDone: (() -> Void)?

    func makeUIView(context: UIViewRepresentableContext<UITextViewWrapper>) -> UITextView {
        let textField = UITextView()
        textField.delegate = context.coordinator

        textField.isEditable = true
        textField.font = UIFont.preferredFont(forTextStyle: .body)
        textField.isSelectable = true
        textField.isUserInteractionEnabled = true
        textField.isScrollEnabled = false
        if nil != onDone {
            textField.returnKeyType = .done
        }

        textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        return textField
    }

    func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<UITextViewWrapper>) {
        if uiView.text != self.text {
            uiView.text = self.text
        }
        if uiView.window != nil, !uiView.isFirstResponder {
            uiView.becomeFirstResponder()
        }
        UITextViewWrapper.recalculateHeight(view: uiView, result: $calculatedHeight)
    }

    fileprivate static func recalculateHeight(view: UIView, result: Binding<CGFloat>) {
        let newSize = view.sizeThatFits(CGSize(width: view.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
        if result.wrappedValue != newSize.height {
            DispatchQueue.main.async {
                result.wrappedValue = newSize.height // !! must be called asynchronously
            }
        }
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(text: $text, height: $calculatedHeight, onDone: onDone)
    }

    final class Coordinator: NSObject, UITextViewDelegate {
        var text: Binding<String>
        var calculatedHeight: Binding<CGFloat>
        var onDone: (() -> Void)?

        init(text: Binding<String>, height: Binding<CGFloat>, onDone: (() -> Void)? = nil) {
            self.text = text
            self.calculatedHeight = height
            self.onDone = onDone
        }

        func textViewDidChange(_ uiView: UITextView) {
            text.wrappedValue = uiView.text
            UITextViewWrapper.recalculateHeight(view: uiView, result: calculatedHeight)
        }

        func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
            if let onDone = self.onDone, text == "\n" {
                textView.resignFirstResponder()
                onDone()
                return false
            }
            return true
        }
    }

}

测试代码

import SwiftUI

struct MultilineTFDemo: View {
    @State var text5 =  demo_text
    var body: some View {
        VStack{
                       Text("多行自动扩充文本框")
                       MultilineTextField("Enter some text here", text: $text5, onCommit: {
                           print("Final text: \(self.text5)")
                       })
                           .overlay(RoundedRectangle(cornerRadius: 4).stroke(Color.black))
                       //Text("Something static here...")
                       Spacer()
                   }
                   .padding()
    }
}

完整demo代码

import SwiftUI


struct ContentView: View {
    @State var text = "此《寒食帖》为北宋元丰二年苏轼谪居黄州,于第三年(元丰五年)四月的寒食日,有感于季节更替、生活困乏以及仕途挫折,乃作寒食诗二首,并于其后书写成本卷,后人誉为苏轼存世最好的书迹,卷后并有黄庭坚题跋。此卷为墨迹素笺(行书十七行,一百二十九字)现藏于台北故宫博物院。"
    @State var oneText = ""
    
    var body: some View {
        VStack{
            VStack{
                TextField("单行文本", text: $oneText)
                .padding()
                Spacer()
            }.frame(height:60)
            .background(Color.yellow)
            .cornerRadius(8)
            .padding()
            
            VStack {
               
                Text("输入框")
                    .padding()
                TextView(
                    text: $text
                )
                    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
            }.frame(height:300)
                .background(Color.orange)
                .cornerRadius(8)
                .padding()
            VStack {
                Text("刚才输入的是:")
                    .padding()
                Divider()
                Text("\(text)")
                Spacer()
            }.frame(height:300)
                .background(Color.purple)
                .cornerRadius(8)
                .padding()
            
        }
        
    }
}

struct TextView: UIViewRepresentable {
    @Binding var text: String
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> UITextView {
        
        let myTextView = UITextView()
        myTextView.delegate = context.coordinator
        
        myTextView.font = UIFont(name: "HelveticaNeue", size: 15)
        myTextView.isScrollEnabled = true
        myTextView.isEditable = true
        myTextView.isUserInteractionEnabled = true
        myTextView.backgroundColor = UIColor(white: 0.0, alpha: 0.05)
        
        return myTextView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        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) {
            print("text now: \(String(describing: textView.text!))")
            self.parent.text = textView.text
        }
    }
}

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


参考文件

更多SwiftUI教程和代码关注专栏

QQ:3365059189 SwiftUI技术交流QQ群:518696470