Ios SwiftUI TextEditor初始内容大小错误
iOS 14.4+Xcode 12.4 我想在iOS上的SwiftUI中制作一个简单的清单,其中每个项目的文本都是Ios SwiftUI TextEditor初始内容大小错误,ios,swiftui,swiftui-texteditor,Ios,Swiftui,Swiftui Texteditor,iOS 14.4+Xcode 12.4 我想在iOS上的SwiftUI中制作一个简单的清单,其中每个项目的文本都是TextEditor 首先,我创建基本的应用程序结构,并用一些演示内容填充它: import SwiftUI @main struct TestApp: App { @State var alpha = "Alpha" @State var bravo = "Bravo is a really long one that should wra
TextEditor
首先,我创建基本的应用程序结构,并用一些演示内容填充它:
import SwiftUI
@main
struct TestApp: App {
@State var alpha = "Alpha"
@State var bravo = "Bravo is a really long one that should wrap to multiple lines."
@State var charlie = "Charlie"
init(){
//Remove the default background of the TextEditor/UITextView
UITextView.appearance().backgroundColor = .clear
}
var body: some Scene {
WindowGroup {
ScrollView{
VStack(spacing: 7){
TaskView(text: $alpha)
TaskView(text: $bravo)
TaskView(text: $charlie)
}
.padding(20)
}
.background(Color.gray)
}
}
}
然后,每个TaskView
表示列表中的一个任务(白色框):
struct TaskView:View{
@Binding var text:String
var body: some View{
HStack(alignment:.top, spacing:8){
Button(action: {
print("Test")
}){
Circle()
.strokeBorder(Color.gray,lineWidth: 1)
.background(Circle().foregroundColor(Color.white))
.frame(width:22, height: 22)
}
.buttonStyle(PlainButtonStyle())
FieldView(name: $text)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(EdgeInsets(top:10, leading:10, bottom: 10, trailing: 30))
.background(Color.white)
.cornerRadius(5)
}
}
最后,每个文本编辑器都位于字段视图中,如下所示:
struct FieldView: View{
@Binding var name: String
var body: some View{
ZStack{
Text(name)
.padding(EdgeInsets(top: -7, leading: -3, bottom: -5, trailing: -3))
.opacity(0)
TextEditor(text: $name)
.fixedSize(horizontal: false, vertical: true)
.padding(EdgeInsets(top: -7, leading: -3, bottom: -5, trailing: -3))
}
}
}
正如您在上面的屏幕截图中所看到的,文本编辑器的初始高度不会自动调整以适合文本。但我一输入它,它就会适当地调整大小。这里有一段视频显示:
如何使视图具有正确的初始高度?在我输入之前,TextEditor
会垂直滚动,因此它似乎具有错误的固有内容大小。注意:视图是半透明的,带有边框,因此您可以查看/调试正在进行的操作
首先,我使用首选项键
将“不可见”文本视图的高度传递回视图层次结构。然后,我用该值设置文本编辑器
框架的高度
请注意,视图现在已对齐到topLeading
——在最初的示例中,不可见文本是居中对齐的
我不喜欢的一件事是边缘插入的使用——这些感觉像是神奇的数字(嗯,它们是…),我宁愿有一个没有它们的解决方案,仍然保持文本和文本编辑器
完全对齐。但是,现在这是可行的
使用UIViewRepresentable和UITextView进行更新
这似乎可以避免滚动问题:
struct TaskView:View{
@Binding var text:String
@State private var textHeight : CGFloat = 40
var body: some View{
HStack(alignment:.top, spacing:8){
Button(action: {
print("Test")
}){
Circle()
.strokeBorder(Color.gray,lineWidth: 1)
.background(Circle().foregroundColor(Color.white))
.frame(width:22, height: 22)
}
.buttonStyle(PlainButtonStyle())
FieldView(text: $text, heightToTransmit: $textHeight)
.frame(height: textHeight)
.border(Color.red)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(EdgeInsets(top:10, leading:10, bottom: 10, trailing: 30))
.background(Color.white)
.cornerRadius(5)
}
}
struct FieldView : UIViewRepresentable {
@Binding var text : String
@Binding var heightToTransmit: CGFloat
func makeUIView(context: Context) -> UIView {
let view = UIView()
let textView = UITextView(frame: .zero, textContainer: nil)
textView.delegate = context.coordinator
textView.backgroundColor = .yellow // visual debugging
textView.isScrollEnabled = false // causes expanding height
context.coordinator.textView = textView
textView.text = text
view.addSubview(textView)
// Auto Layout
textView.translatesAutoresizingMaskIntoConstraints = false
let safeArea = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
textView.topAnchor.constraint(equalTo: safeArea.topAnchor),
textView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
textView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor)
])
return view
}
func updateUIView(_ view: UIView, context: Context) {
context.coordinator.heightBinding = $heightToTransmit
context.coordinator.textBinding = $text
DispatchQueue.main.async {
context.coordinator.runSizing()
}
}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
class Coordinator : NSObject, UITextViewDelegate {
var textBinding : Binding<String>?
var heightBinding : Binding<CGFloat>?
var textView : UITextView?
func runSizing() {
guard let textView = textView else { return }
textView.sizeToFit()
self.textBinding?.wrappedValue = textView.text
self.heightBinding?.wrappedValue = textView.frame.size.height
}
func textViewDidChange(_ textView: UITextView) {
runSizing()
}
}
}
结构任务视图:视图{
@绑定变量文本:字符串
@国家私有变量textHeight:CGFloat=40
var body:一些观点{
HStack(对齐:顶部,间距:8){
按钮(操作:{
打印(“测试”)
}){
圈()
.strokeBorder(颜色.灰色,线宽:1)
.background(圆圈().foregroundColor(颜色.白色))
.框架(宽度:22,高度:22)
}
.buttonStyle(PlainButtonStyle())
FieldView(文本:$text,heightToTransmit:$textHeight)
.frame(高度:textHeight)
.边框(颜色.红色)
}
.frame(最大宽度:。无穷大,对齐:。前导)
.填充(边集(顶部:10,前导:10,底部:10,尾随:30))
.背景(颜色.白色)
.转弯半径(5)
}
}
结构FieldView:UIViewRepresentable{
@绑定变量文本:字符串
@绑定var heightToTransmit:CGFloat
func makeUIView(上下文:context)->UIView{
let view=UIView()
让textView=UITextView(帧:.0,textContainer:nil)
textView.delegate=context.coordinator
textView.backgroundColor=.yellow//可视化调试
textView.IsCrollenabled=false//导致展开高度
context.coordinator.textView=textView
textView.text=文本
view.addSubview(文本视图)
//自动布局
textView.translatesAutoResizezingMaskintoConstraints=false
让safeArea=view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
textView.topAnchor.constraint(等式:safeArea.topAnchor),
textView.leadingAnchor.constraint(相等于:安全区域.leadingAnchor),
textView.trailingAnchor.constraint(等式:safeArea.trailingAnchor)
])
返回视图
}
func updateUIView(view:UIView,context:context){
context.coordinator.heightBinding=$heightToTransmit
context.coordinator.textBinding=$text
DispatchQueue.main.async{
context.coordinator.runSizing()文件
}
}
func makeCoordinator()->Coordinator{
返回协调员()
}
类协调器:NSObject、UITextViewDelegate{
var textBinding:绑定?
var-heightBinding:绑定?
var textView:UITextView?
func runSizing(){
guard let textView=textView else{return}
textView.sizeToFit()
self.textBinding?.wrappedValue=textView.text
self.heightBinding?.wrappedValue=textView.frame.size.height
}
func textViewDidChange(textView:UITextView){
运行规模()
}
}
}
这是一个非常巧妙的解决方案,谢谢。我看到的唯一遗留问题是,如果向上或向下滑动文本编辑器,它仍然会滚动。这里有一个来自模拟器的GIF显示了我的意思:这是使用文本编辑器
不可避免的副作用吗?我想是的,因为文本编辑器天生就是一个滚动视图,但我可能错了。如果它是UIKit,你可能会禁用滚动,除非它是当前的第一响应者。我曾尝试在UIExtView
(通过UIViewRepresentable
)上设置textView.IsScrolEnabled=false
),但它的行为与UIKit应用程序中的行为不同。我不知道如何使其表现为其边缘将自动布局约束固定到其父对象。请检查我的更新。为我工作。啊,太棒了!我试图做一些类似的事情,但在updateUIView
中,我不断得到context.coordinator.textView?.frame.size.height的0
。这种情况表明SwiftUI有点弱——在UIKit中,这种情况要容易得多。
struct TaskView:View{
@Binding var text:String
@State private var textHeight : CGFloat = 40
var body: some View{
HStack(alignment:.top, spacing:8){
Button(action: {
print("Test")
}){
Circle()
.strokeBorder(Color.gray,lineWidth: 1)
.background(Circle().foregroundColor(Color.white))
.frame(width:22, height: 22)
}
.buttonStyle(PlainButtonStyle())
FieldView(text: $text, heightToTransmit: $textHeight)
.frame(height: textHeight)
.border(Color.red)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(EdgeInsets(top:10, leading:10, bottom: 10, trailing: 30))
.background(Color.white)
.cornerRadius(5)
}
}
struct FieldView : UIViewRepresentable {
@Binding var text : String
@Binding var heightToTransmit: CGFloat
func makeUIView(context: Context) -> UIView {
let view = UIView()
let textView = UITextView(frame: .zero, textContainer: nil)
textView.delegate = context.coordinator
textView.backgroundColor = .yellow // visual debugging
textView.isScrollEnabled = false // causes expanding height
context.coordinator.textView = textView
textView.text = text
view.addSubview(textView)
// Auto Layout
textView.translatesAutoresizingMaskIntoConstraints = false
let safeArea = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
textView.topAnchor.constraint(equalTo: safeArea.topAnchor),
textView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
textView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor)
])
return view
}
func updateUIView(_ view: UIView, context: Context) {
context.coordinator.heightBinding = $heightToTransmit
context.coordinator.textBinding = $text
DispatchQueue.main.async {
context.coordinator.runSizing()
}
}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
class Coordinator : NSObject, UITextViewDelegate {
var textBinding : Binding<String>?
var heightBinding : Binding<CGFloat>?
var textView : UITextView?
func runSizing() {
guard let textView = textView else { return }
textView.sizeToFit()
self.textBinding?.wrappedValue = textView.text
self.heightBinding?.wrappedValue = textView.frame.size.height
}
func textViewDidChange(_ textView: UITextView) {
runSizing()
}
}
}