Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
674 views
in Technique[技术] by (71.8m points)

swift - How to use an NSAttributedString with a ScrollView in SwiftUI?

I've been able to render NSAttributedStrings via UIViewRepresentable which works great until I wrap the view in a ScrollView.

When placed inside the ScrollView, the NSAttributedString view stops rendering.

I've tried some other methods that replace an NSAttributedString with adding multiple Text() views together to get formatting which works inside the ScrollView and supports italics and monospace font. Unfortunately this doesn't work for links inside text blocks, which means I still need an NSAttributedString.

import SwiftUI

struct TextWithAttributedString: UIViewRepresentable {

    var attributedString: NSAttributedString

    init(_ attributedString: NSAttributedString) {
        self.attributedString = attributedString
    }

    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView(frame: .zero)
        textView.attributedText = self.attributedString
        textView.isEditable = false
        return textView
    }

    func updateUIView(_ textView: UITextView, context: Context) {
        textView.attributedText = self.attributedString
    }
}


let exampleText = """
Fugiat id blanditiis et est culpa voluptas. Vivamus aliquet enim eu blandit blandit. Sit eget praesentium maxime sit molestiae et alias aut.
"""

struct NSAttributedStringView: View {
    var body: some View {
// Note: when uncommented, the view breaks
//    ScrollView {
        TextWithAttributedString(NSAttributedString(string: exampleText))
//    }
    }
}

struct NSAttributedStringView_Previews: PreviewProvider {
    static var previews: some View {
        NSAttributedStringView()
            .previewLayout(.sizeThatFits)
    }
}

Edit: I tried using the wrapped UITextView with the text property set instead of the attributeText property, but this also fails to render in the ScrollView, so the issue seems to be the UITextView, not the NSAttributedString.

So the question is, how do we get the UITextView to work in a ScrollView?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The reason is that SwiftUI ScrollView requires defined content size, but used UITextView is itself a UIScrollView and detects content based on available space in parent view. Thus it happens cycle of undefined sizes.

Here is a simplified demo of possible approach how to solve this. The idea is to calculate content size of UITextView and pass it to SwiftUI...

struct TextWithAttributedString: UIViewRepresentable {
    @Binding var height: CGFloat
    var attributedString: NSAttributedString

    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView(frame: .zero)
        textView.isEditable = false
        return textView
    }

    func updateUIView(_ textView: UITextView, context: Context) {
        textView.attributedText = self.attributedString

        // calculate height based on main screen, but this might be 
        // improved for more generic cases
        DispatchQueue.main.async { // << fixed 
            height = textView.sizeThatFits(UIScreen.main.bounds.size).height
        }
    }
}


struct NSAttributedStringView: View {
    @State private var textHeight: CGFloat = .zero
    var body: some View {
        ScrollView {
            TextWithAttributedString(height: $textHeight, attributedString: NSAttributedString(string: exampleText))
                .frame(height: textHeight) // << specify height explicitly !
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...