Skip to content

Instantly share code, notes, and snippets.

@mrackwitz
Created October 27, 2020 19:53
Show Gist options
  • Save mrackwitz/91395527a37c4cea1757d76e8e99f826 to your computer and use it in GitHub Desktop.
Save mrackwitz/91395527a37c4cea1757d76e8e99f826 to your computer and use it in GitHub Desktop.
A UILabel-based SwiftUI view which auto-expands to the width of the container, and only expand to render the full height of the label.
struct AutoHeightLabelView: View {
var attributedString: NSAttributedString
var body: some View {
HorizontalGeometryReader { width in
UILabelView(
attributedString: attributedString,
preferredMaxLayoutWidth: width
)
}
}
}
fileprivate struct UILabelView: UIViewRepresentable {
var attributedString: NSAttributedString
var preferredMaxLayoutWidth: CGFloat = .greatestFiniteMagnitude
public func makeUIView(context: UIViewRepresentableContext<UILabelView>) -> UILabel {
let label = UILabel(frame: .zero)
label.numberOfLines = 0
label.setContentHuggingPriority(.required, for: .vertical)
label.setContentCompressionResistancePriority(.required, for: .vertical)
label.setContentHuggingPriority(.defaultLow, for: .horizontal)
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
updateUIView(label, context: context)
return label
}
public func updateUIView(_ label: UILabel, context: UIViewRepresentableContext<UILabelView>) {
label.attributedText = attributedString
label.preferredMaxLayoutWidth = preferredMaxLayoutWidth
}
}
fileprivate struct HorizontalGeometryReader<Content: View> : View {
var content: (CGFloat) -> Content
@State private var width: CGFloat = 0
public init(@ViewBuilder content: @escaping (CGFloat) -> Content) {
self.content = content
}
public var body: some View {
content(width)
.frame(minWidth: 0, maxWidth: .infinity)
.background(
GeometryReader { geometry in
Color.clear
.preference(key: WidthPreferenceKey.self, value: geometry.size.width)
}
)
.onPreferenceChange(WidthPreferenceKey.self) { width in
self.width = width
}
}
}
fileprivate struct WidthPreferenceKey: PreferenceKey, Equatable {
static var defaultValue: CGFloat = 0
/// An empty reduce implementation takes the first value
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
}
}
@ANGOmarcello
Copy link

Hey @mrackwitz I found this very useful, thank you!

I was having issues on switching my tab bar and returning and also after changing dynamic type size during runtime, as the height was reset somehow.

I added a unique ID to the geometry reader to fix the issue as follows:

        .id(
            // Fixes height issues on tab change
            // Fixes height issues on dynamic type change
            UUID().uuidString
        )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment