如何在 SwiftUI ScrollView 中设置可编辑 UITextView 的宽度?

2023-11-21

我正在构建一个 SwiftUI 应用程序,我想将一个可编辑的 UITextView (包装在 UIViewRepresentable 中)放入 SwiftUI ScrollView 中。原因是我有其他 SwiftUI 内容想要放在文本上方,并且我希望它与文本一起滚动。我需要一个 TextView,因为我想要富文本和工具栏。

我想我可以禁用 UITextView 上的滚动并赋予它无限的高度,但这也赋予它无限的宽度,因此文本会水平滚动屏幕边缘,直到添加换行符。我尝试将文本视图的内容大小和框架大小设置为屏幕宽度,如帖子中建议的那样如何禁用 UITextView 中的垂直滚动?但我无法让它发挥作用。我可以成功更新 textview 内容大小,但更改似乎稍后会被覆盖(请参阅 textViewDidChange 中的打印语句)。

有什么办法可以实现这一点吗?即 SwiftUI ScrollView 内的可编辑 UITextView。提前致谢!


import SwiftUI


struct TextView: UIViewRepresentable {
    var attributedString: NSAttributedString
    var fixedWidth: CGFloat
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }
    
    
    class Coordinator: NSObject, UITextViewDelegate {
        var parent: TextView
        
        init(_ parent: TextView) {
            self.parent = parent
        }
        
        func textViewDidChange(_ textView: UITextView) {
            print("pre content size update: \(textView.contentSize)") // width is over limit
            let newSize = textView.sizeThatFits(CGSize(width: parent.fixedWidth, height: CGFloat.greatestFiniteMagnitude))
            textView.contentSize = CGSize(width: max(newSize.width, parent.fixedWidth), height: newSize.height)
            print("post content size update: \(textView.contentSize)") // width is updated
        }
    }
    
    
    
    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView(frame: .zero)
        textView.isEditable = true
        textView.isScrollEnabled = false
        textView.backgroundColor = .cyan // just to make it easier to see
        textView.delegate = context.coordinator
        return textView
    }
    
    func updateUIView(_ textView: UITextView, context: Context) {
        textView.attributedText = self.attributedString
        
    }
}





struct ContentView: View {
    var body: some View {
        GeometryReader { geo in
            ScrollView(.vertical) {
                VStack(alignment: .leading, spacing: /*@START_MENU_TOKEN@*/nil/*@END_MENU_TOKEN@*/){
                    //Other content
                    Image(systemName: "photo")
                        .font(.largeTitle)
                    Button("A button"){
                        print("i'm a button")
                    }
                    
                    //Editable textview
                    TextView(attributedString: NSAttributedString(string: "Here is a long string as you can see it is not wrapping but instead scrolling off the edge of the screen"), fixedWidth: geo.size.width)
                        .frame(width: geo.size.width)
                        .frame(maxHeight: .infinity)
                    
                }
                .padding(.horizontal)
            }
        }
        
    }
}

我尝试过的其他一些事情:

  • 只是使用普通的可滚动文本视图并将我的所有内容作为文本附件放入 NSAttributed 字符串中 -> 对我的内容限制太大并且难以检测用户交互
  • 在 updateUIView() 中的文本视图上设置锚点约束 -> 当文本为空时效果很好,但如果屏幕已经加载文本,则父视图的布局将完全重新排列
  • 将绑定从父级传递到文本视图的高度和宽度,如下所示如何在 SwiftUI 中将 NSAttributedString 与 ScrollView 一起使用?-> 不起作用。我认为这些问题并不像第一次出现那样相似 - 我想为可编辑文本设置固定大小,而这个问题需要为不可编辑文本设置可调整的内容大小

设置文本视图的宽度约束并设置GeometryReader在视图的顶部。

设定的理由GeometryReader在顶部,因为在滚动视图内它不允许完全滚动。

UIViewRepresentable Text View

struct TextView: UIViewRepresentable {
    var attributedString: NSAttributedString
    var fixedWidth: CGFloat
    
    let textView = UITextView(frame: .zero)
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }
    
    
    class Coordinator: NSObject, UITextViewDelegate {
        var parent: TextView
        init(_ parent: TextView) {
            self.parent = parent
        }
    }
    
    func makeUIView(context: Context) -> UITextView {
        textView.isEditable = true
        textView.isScrollEnabled = false
        textView.backgroundColor = .cyan // just to make it easier to see
        textView.delegate = context.coordinator
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.widthAnchor.constraint(equalToConstant: fixedWidth).isActive = true //<--- Here
        return textView
    }
    
    func updateUIView(_ textView: UITextView, context: Context) {
        DispatchQueue.main.async { //<--- Here
            textView.attributedText = self.attributedString
        }
    }
}

ContentView

struct ContentView: View {
    
    @State private var textViewSize: CGSize = CGSize()
    
    var body: some View {
        GeometryReader { geo in //<--- Here
            ScrollView(.vertical) {
                VStack(alignment: .leading, spacing: 0){
                    //Other content
                    Image(systemName: "photo")
                        .font(.largeTitle)
                    Button("A button"){
                        print("i'm a button")
                    }
                    
                    TextView(attributedString: NSAttributedString(string: "Here is a long string as you can see it is not wrapping but instead scrolling off the edge of the screenHere is a long string as you can see it is not wrapping but instead scrolling off the edge of the screenHere is a long string as you can see it is not wrapping but instead scrolling off the edge of the screenHere is a long string as you can see it is not wrapping but instead scrolling off the edge of the screenHere is a long string as you can see it is \n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new line\n1new lineinstead scrolling off the edge of the screenHere is a long string as you can see it is not wrapping but instead scrolling off the edge of the screenHere is a long string as you can see it is not wrapping but instead scrolling off the edge of the screen ------end"), fixedWidth: geo.size.width - 15 * 2) //<--- Here minus the padding for both size
                }.padding(.horizontal, 15)
            }
        }
    }
}

enter image description here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 SwiftUI ScrollView 中设置可编辑 UITextView 的宽度? 的相关文章

随机推荐