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

单行文本

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的控件。

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
- 请关注我的专栏icloudend, SwiftUI教程与源码 www.jianshu.com/c/7b3e3b671…