Nico
Nico

Reputation: 2882

Content of UIViewRepresentable in a ScrollView is not rendered properly

I'm trying to integrate a UITextView inside a SwiftUI view (in order to render HTML content, similar to what's done here: Frame height problem with custom UIViewRepresentable UITextView in SwiftUI).

I can't get a sane behavior for the wrapped UITextView, text is either cut after 1st line or extended beyond the screen (if setContent* are omitted).

Here is a minimal code to reproduce the issue:

struct ContentView: View {
    var body: some View {
        ScrollView {
                Text(text)
                WrappedUITextView(text)
        }.padding()

    }
}

struct WrappedUITextView: UIViewRepresentable {
    private let textContent: String

    init(_ textContent: String) {
        self.textContent = textContent
    }

    func makeUIView(context: Context) -> UITextView {

        let uiTextView = UITextView()

        uiTextView.backgroundColor = .clear

        uiTextView.isEditable = false
        uiTextView.isScrollEnabled = false
        uiTextView.setContentHuggingPriority(.defaultLow, for: .vertical)
        uiTextView.setContentHuggingPriority(.defaultLow, for: .horizontal)
        uiTextView.setContentCompressionResistancePriority(.required, for: .vertical)
        uiTextView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

        return uiTextView
    }

    func updateUIView(_ uiTextView: UITextView, context: Context) {
        uiTextView.text = textContent
    }
}

let text = """
            Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
            """

This code render to this screen:

Obviously the intended behavior is having the text displayed completely as with the Text() SwiftUI Component.

Do you have any idea to improve this?

Upvotes: 1

Views: 989

Answers (2)

Nico
Nico

Reputation: 2882

For iOS 16 and up, Patrick's response works great.
For iOS 14 and up, I had success with the RepresentableKit library

import SwiftUI
import RepresentableKit

struct ContentView: View {
    var body: some View {
        ScrollView {
                Text(text)
                UIViewAdaptor {
                    let uiTextView = UITextView()
                    uiTextView.text = text
                    uiTextView.isEditable = false
                    uiTextView.isScrollEnabled = false
                    uiTextView.setContentHuggingPriority(.defaultLow, for: .vertical)
                    uiTextView.setContentHuggingPriority(.defaultLow, for: .horizontal)
                    uiTextView.setContentCompressionResistancePriority(.required, for: .vertical)
                    uiTextView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
                    return uiTextView
                }
        }.padding()
    }
}

Upvotes: 0

Patrick
Patrick

Reputation: 458

I faced the same problem, from iOS 16 and up, UIViewRepresentable has a new method sizeThatFits(_:uiView:context:) -> CGSize? that helps with this.

I implemented it as follows:

func sizeThatFits(_ proposal: ProposedViewSize, uiView: DropCapView, context: Context) -> CGSize? {
    let size = uiView.systemLayoutSizeFitting(
        CGSize(
            width: proposal.width ?? UIView.layoutFittingCompressedSize.width,
            height: UIView.layoutFittingCompressedSize.height
        ),
        withHorizontalFittingPriority: .required,
        verticalFittingPriority: .defaultLow
    )
    return proposal.replacingUnspecifiedDimensions(by: size)
}

Upvotes: 4

Related Questions