How can I get text to wrap in a UILabel (via UIViewRepresentable) without having a fixed width?
Possible solution is to declare the width as a variable on MyTextView:
struct MyTextView: UIViewRepresentable {
var width: CGFloat
func makeUIView(context: Context) -> UILabel {
let label = UILabel()
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
label.preferredMaxLayoutWidth = width
label.text = "Here's a lot of text for you to display. It won't fit on the screen."
return label
}
func updateUIView(_ view: UILabel, context: Context) {
}
}
and then use GeometryReader to findout how much space there is avaible and pass it into the intializer:
struct ExampleView: View {
var body: some View {
GeometryReader { geometry in
MyTextView(width: geometry.size.width)
}
}
}
Size a UILabel in SwiftUI via UIViewRepresentable like Text to wrap multiple lines
The problem here is in ScrollView
which requires definite height, but representable does not provide it. The possible solution is to dynamically calculate wrapped text height and specify it explicitly.
Note: as height is calculated dynamically it is available only in run-time, so cannot be tested with Preview.
Tested with Xcode 12 / iOS 14
struct LabelView: View {
var text: String
@State private var height: CGFloat = .zero
var body: some View {
InternalLabelView(text: text, dynamicHeight: $height)
.frame(minHeight: height)
}
struct InternalLabelView: UIViewRepresentable {
var text: String
@Binding var dynamicHeight: CGFloat
func makeUIView(context: Context) -> UILabel {
let label = UILabel()
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return label
}
func updateUIView(_ uiView: UILabel, context: Context) {
uiView.text = text
DispatchQueue.main.async {
dynamicHeight = uiView.sizeThatFits(CGSize(width: uiView.bounds.width, height: CGFloat.greatestFiniteMagnitude)).height
}
}
}
}
backup
Multiline label with UIViewRepresentable
There's a few things going on here.
InformationView
doesn't have anintrinsicContentSize
. SwiftUI relies on this property to determine aUIView
's ideal size.- No
fixedSize
modifier. This modifier forces a view to maintain its ideal size rather than grow to fill its parent.
You can't add an intrinsicContentSize
because the content size is dynamic; it's based on the number of lines in your label.
You can't add the fixedSize
modifier, because without an intrinsicContentSize
, this will set the size to (0,0).
/p>
One solution is to wrap InformationView
in a UIView
that measures the InformationView
size, and updates its own intrinsicContentSize
according to that measurement.
Your InformationView
should fill the width of the screen; it doesn't have an intrinsic width. On the other hand, the intrinsic height should be equal to the height of the compressed system layout size. This is "the optimal size of the view based on its constraints".
Here is the wrapper:
class IntrinsicHeightView<ContentView: UIView>: UIView {
var contentView: ContentView
init(contentView: ContentView) {
self.contentView = contentView
super.init(frame: .zero)
backgroundColor = .clear
addSubview(contentView)
}
@available(*, unavailable) required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private var contentHeight: CGFloat = .zero {
didSet { invalidateIntrinsicContentSize() }
}
override var intrinsicContentSize: CGSize {
.init(width: UIView.noIntrinsicMetric, height: contentHeight)
}
override var frame: CGRect {
didSet {
guard frame != oldValue else { return }
contentView.frame = self.bounds
contentView.layoutIfNeeded()
let targetSize = CGSize(width: frame.width, height: UIView.layoutFittingCompressedSize.height)
contentHeight = contentView.systemLayoutSizeFitting(
targetSize,
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel).height
}
}
}
This IntrinsicHeightView
is a generic UIView
that, when its frame changes, recalculates the height of its intrinsicContentSize
according to the compressed system layout height of its content view.
Here is the update to InformationRepresenter
:
struct InformationRepresenter: UIViewRepresentable {
func makeUIView(context: Context) -> IntrinsicHeightView<InformationView> {
return IntrinsicHeightView(contentView: InformationView())
}
func updateUIView(_ uiView: IntrinsicHeightView<InformationView>, context: Context) {
}
}
And finally, when using it in SwiftUI:
InformationRepresenter()
.padding()
.fixedSize(horizontal: false, vertical: true)
Using the fixedSize
modifier this way allows the UIView
to fill the parent, but use intrinsicContentSize.height
as its height. Here is the final result. Good luck!
Using UIViewRepresentable to wrap MarqueeLabel view
It is by-default expanded by intrinsic content to full width, so MarqueeLabel
just does not have what to scroll - everything is in frame.
In such cases we need to give ability to parent to shrink internal view to externally available space (by width in this case to screen)
so here is a fix - decrease resistance priority:
func makeUIView(context: UIViewRepresentableContext<UILabelView>) -> MarqueeLabel {
let label = MarqueeLabel()
label.text = text
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
// ^^^^^ << this one !!
return label
}
Tested with Xcode 13.4 / iOS 15.5
How does UIViewRepresentable size itself in relation to the UIKit control inside it?
Most simple and quick fix:
SwiftUIText(text: $helperText).fixedSize()
Update:
SwiftUIText(text: $helperText)
.fixedSize(horizontal: false, vertical: true)
SwiftUI Text scaledToFit and wrap text
try this, and similarly for the pink:
HStack {
Text(lorem)
.frame(width: 100, height: 100) // <--- here
.allowsTightening(true)
.lineLimit(2)
.scaledToFit()
.minimumScaleFactor(0.7)
}
.frame(width: 100, height: 100)
.background(Color.green)
Related Topics
Creating Custom Tableview Cells in Swift
Segue and Button Programmatically Swift
Append Text or Data to Text File in Swift
How to Generate a Random Number in Swift Without Repeating the Previous Random Number
How to Get the Count of a Swift Enum
Dyld: Library Not Loaded: @Rpath/Libswift_Stdlib_Core.Dylib
Swift Protocol With "Where Self" Clause
How to Get a Reference to the App Delegate in Swift
How to Save and Read Array of Array in Nsuserdefaults in Swift
Remove or Edit User Location Blue Pulsing Circle
How to Update the Constant Height Constraint of a Uiview Programmatically
How to Set Uicollectionviewcell Width and Height Programmatically