swiftuiuikituiviewrepresentablehstack

UIViewRepresentable with fixedSize in HStack


I have a UIViewRepresentable wrapping an UIView that contains a UILabel.
I'm trying to position this representable view inside a HStack and I'd like it to take the width of the underlying label.
Here is the demo code:


import SwiftUI

struct MyView1: UIViewRepresentable {
    typealias UIViewType = UIView1

    func makeUIView(context: Context) -> UIView1 {
        return UIView1()
    }

    func updateUIView(_ nsNumber: UIView1, context: Context) {}
}

class UIView1: UIView {
    public var label: UILabel

    init() {
        self.label = UILabel()
        label.text = "Hello, World!"
        super.init(frame: .zero)
        addSubview(label)
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

struct ContentView: View {
    var body: some View {
        HStack {
            Text("Push")
                .background(.green)
            Spacer()
            MyView1()
                .fixedSize()
                .background(.blue)
        }
    }
}

#Preview { ContentView() }

The issue is that the view MyView1 doesn't appear in the stack at all. Any idea how it could be achieve? I just want my MyView1 to just take the space of the label.

enter image description here


Solution

  • You should override sizeThatFits in UIViewRepresentable. You should return the desired size of the view, given a ProposedViewSize.

    With fixedSize(), sizeThatFits will receive a ProposedViewSize with both width and height being nil.

    It's not clear what you want to happen when there is no fixedSize(). Let's say you want the label to expand horizontally as much as possible. An example implementation of that is:

    func sizeThatFits(_ proposal: ProposedViewSize, uiView: UIView1, context: Context) -> CGSize? {
        let size = uiView.label.intrinsicContentSize
        return CGSize(width: max(size.width, proposal.width ?? 0), height: size.height)
    }
    

    You should also set up some constraints between self.label and self in UIView1:

    init() {
        self.label = UILabel()
        label.text = "Hello, World!"
        label.numberOfLines = 0
        super.init(frame: .zero)
        addSubview(label)
        label.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            label.topAnchor.constraint(equalTo: topAnchor),
            label.bottomAnchor.constraint(equalTo: bottomAnchor),
            label.leftAnchor.constraint(equalTo: leftAnchor),
            label.rightAnchor.constraint(equalTo: rightAnchor),
        ])
    }