Swiftui如何根据textview内容管理Scrollview Height

Posted

技术标签:

【中文标题】Swiftui如何根据textview内容管理Scrollview Height【英文标题】:How to manage Scrollview Height according to textview content Swiftui 【发布时间】:2021-04-29 05:09:54 【问题描述】:

我正在尝试根据 textview 内容高度与其他视图管理滚动视图高度。

代码:

struct TextviewWithScrollview: View 
    @State private var textStyle = UIFont.TextStyle.body
    @State private var textForTextView = "fdgfhjdsgfdhsgfdsfg dsfg dsfgdsfh fh sf hdsjklf dhsjkfhsdjkfdhsjkfadhsfkds fdshfjkldsh fjkldsh fdshfdshfdsfhsfdsfh sf ewf g iuf herjkdsjkjvdhsvdshvdsv dshv ds vads vds hvsdvjkds vds hviuds  vhv disu ghdisuv g"
    var body: some View 
        ZStack 
            Color.red
                .ignoresSafeArea()
            ScrollView 
                VStack(alignment: .leading) 
                    TextView(isEditable: .constant(false), text:  .constant(textForTextView), textStyle:$textStyle, didStartEditing: .constant(false),placeHolderText:"")
                
                
                HStack 
                    Button("Top") 
                    
                    Button("Middle") 
                        
                    
                    Button("Bottom") 
                    
                
            
        
    


struct TextView: UIViewRepresentable 
    @Binding var isEditable:Bool
    @Binding var text: String
    @Binding var textStyle: UIFont.TextStyle
    @Binding var didStartEditing: Bool
    var placeHolderText: String = ""
    
    
    func makeUIView(context: Context) -> UITextView 
        let textView = UITextView()
        textView.delegate = context.coordinator
        textView.font = UIFont.preferredFont(forTextStyle: textStyle)
        textView.autocapitalizationType = .sentences
        textView.isSelectable = true
        textView.isUserInteractionEnabled = true
        textView.isScrollEnabled = true
        textView.dataDetectorTypes = .all
        textView.textColor = .white
        return textView
    
    
    func updateUIView(_ uiView: UITextView, context: Context) 
        uiView.text = text
        uiView.font = UIFont.preferredFont(forTextStyle: textStyle)
    
    
    func makeCoordinator() -> Coordinator 
        Coordinator($text)
    
    
    class Coordinator: NSObject, UITextViewDelegate 
        var text: Binding<String>
        
        init(_ text: Binding<String>) 
            self.text = text
        
        
        func textViewDidChange(_ textView: UITextView) 
            DispatchQueue.main.async 
                self.text.wrappedValue = textView.text
            
        
    

不带滚动视图的输出:

滚动视图输出:

有人可以向我解释如何根据 textview 内容和其他人的观点来管理 Scrollview 高度。我已经尝试按照上面的方法实现,但还没有结果。

任何帮助将不胜感激。

提前致谢。

【问题讨论】:

顺便说一句,如果你不需要支持 ios 13,你应该使用TextEditor 不太清楚你想要实现什么。如果文本超出屏幕怎么办?按钮应该始终在屏幕上吗?在中心?为什么要使用 UITextView,这是必需的限制吗?您能否详细说明目标。 @Asperi 我的目标是使用 textview 和其他视图进行全屏滚动。因为当我使用带有滚动视图的文本视图时,文本视图无法正常工作,但是当我不使用滚动视图时,文本视图工作正常,但其他视图滚动关闭。那么你能帮我如何用textview实现全屏滚动视图吗? @aheze 但在 TextEditor 中不支持突出显示 url。 是否可以管理滚动视图高度? @Asperi 【参考方案1】:

如果我对您的理解正确,这就是您想要实现的目标:

要获得此结果,请将 ScrollView 嵌入 GeometryReader 并使用框架调整 TextView 的大小。像这样:

struct TextviewWithScrollview: View 
@State private var textStyle = UIFont.TextStyle.body
@State private var textForTextView = "fdgfhjdsgfdhsgfdsfg dsfg dsfgdsfh fh sf hdsjklf dhsjkfhsdjkfdhsjkfadhsfkds fdshfjkldsh fjkldsh fdshfdshfdsfhsfdsfh sf ewf g iuf herjkdsjkjvdhsvdshvdsv dshv ds vads vds hvsdvjkds vds hviuds  vhv disu ghdisuv g"
var body: some View 
    ZStack 
        Color.red
            .ignoresSafeArea()
        GeometryReader  geo in
            ScrollView 
                VStack(alignment: .leading) 
                    TextView(isEditable: .constant(false), text:  .constant(textForTextView), textStyle:$textStyle, didStartEditing: .constant(false),placeHolderText:"")
                        .frame(width: geo.size.width, height: 300)
                
                HStack 
                    Button("Top") 
                    
                    Button("Middle") 
                    
                    Button("Bottom") 
                    
                
            
        
    

【讨论】:

抱歉回复晚了。我检查了你的代码,它工作正常。但是如果我设置 textview 滚动关闭,我会停留在最后一点。因为在屏幕上启用了当前 2 滚动,一个 id scrollview 和第二个是 textview 滚动。你能帮我么。如何禁用 textview 滚动。【参考方案2】:

这是另一种解决方案,它根据显示的文本量添加 TextView 的自动调整大小:

下面是代码:

class myTextObject: ObservableObject 
    @Published var text: String = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem."


struct TextviewWithScrollview: View 
    @State private var textStyle = UIFont.TextStyle.body
    
    @StateObject var textForView = myTextObject()
    @State private var textViewSize: CGSize = CGSize(width: 300, height: 300)
    
    var body: some View 
        ZStack 
            Color.red
                .ignoresSafeArea()
            
            GeometryReader  geo in
                ScrollView 

                    VStack(alignment: .leading) 
                        TextView(
                            isEditable: .constant(false),
                            text: $textForView.text,
                            textStyle: $textStyle,
                            didStartEditing: .constant(false),
                            placeHolderText: "",
                            textViewSize: $textViewSize
                        )
                        .frame(minHeight: 30, idealHeight: min(textViewSize.height, geo.size.height), maxHeight: geo.size.height)
                    
                    
                    HStack 
                        Button("Top") 
                        
                        Button("Middle") 
                            self.$textForView.text.wrappedValue += "\n-- a new line --"
                        
                        Button("Bottom") 
                        
                    
                
            
        
    


struct TextView: UIViewRepresentable 
    @Binding var isEditable:Bool
    @Binding var text: String
    @Binding var textStyle: UIFont.TextStyle
    @Binding var didStartEditing: Bool
    var placeHolderText: String = ""
    
    @Binding var textViewSize: CGSize
    
    func makeUIView(context: Context) -> UITextView 
        let textView = UITextView()
        textView.delegate = context.coordinator
        textView.font = UIFont.preferredFont(forTextStyle: textStyle)
        textView.autocapitalizationType = .sentences
        textView.isSelectable = true
        textView.isUserInteractionEnabled = true
        textView.isScrollEnabled = true
        textView.dataDetectorTypes = .all
        textView.textColor = .gray
        DispatchQueue.main.async 
            textViewSize = textView.sizeThatFits(textView.textContainer.size)
        
        return textView
    
    
    func updateUIView(_ uiView: UITextView, context: Context) 
        uiView.text = text
        uiView.font = UIFont.preferredFont(forTextStyle: textStyle)
        
        DispatchQueue.main.async 
            let newContentSize = uiView.sizeThatFits(uiView.textContainer.size)
            context.coordinator.parent.$textViewSize.wrappedValue = newContentSize
        
    
    
    func makeCoordinator() -> Coordinator 
        Coordinator(parent: self)
    
    
    class Coordinator: NSObject, UITextViewDelegate 
        var parent: TextView
        
        init(parent: TextView)  
            self.parent = parent
        
        
        func textViewDidChange(_ textView: UITextView) 
            
            DispatchQueue.main.async 
                self.parent.$text.wrappedValue = textView.text
                
                let newContentSize = textView.sizeThatFits(textView.textContainer.size)
                self.parent.$textViewSize.wrappedValue = newContentSize
            
        
    


【讨论】:

抱歉回复晚了。我检查了你的代码,它工作正常。但是如果我设置 textview 滚动关闭,我会停留在最后一点。因为在屏幕上启用的当前 2 滚动第一个是滚动视图,第二个是文本视图滚动。你能帮我么。如何禁用 textview 滚动。 不确定您想要完成什么?在我发布的第二个实现中,文本视图是自动调整大小的,因此只要文本视图适合滚动视图的可见屏幕区域,文本视图中就不会发生任何滚动。在您放入更多文本的那一刻,文本视图会提供滚动行为。您可以通过更改 .frame 修饰符中的 geo.size.height 来更改文本视图不滚动的高度。顺便说一句,当我设置 textView.isScrollEnabled = false 时,ScrollView 的行为会有所不同。我还没有研究为什么会这样。【参考方案3】:

为文本视图设置宽度约束,并在视图顶部设置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)
            
        
    

【讨论】:

以上是关于Swiftui如何根据textview内容管理Scrollview Height的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 将数据从 Firebase 加载到 TextView

在 SwiftUI 中为 UITextView 设置初始视图偏移量

如何使用 SwiftUI 在 UICollectionViewCell 中填充内容

SwiftUI - 如何在运行时更改内部内容后获取 ScrollView 内容高度

如何根据 SwiftUI 中的日期将事件分成列表的不同部分?

swiftui,动画应用于父效果子动画