[英]Size a UILabel in SwiftUI via UIViewRepresentable like Text to wrap multiple lines
目標是讓通過UIViewRepresentable
集成的UILabel
的大小與Text
的大小相同——使用可用寬度並換行到多行以適合所有文本,從而增加它所在的HStack
的高度,而不是無限擴展寬度。 這與這個問題非常相似,盡管接受的答案不適用於我正在使用的涉及ScrollView
、 VStack
和HStack
的布局。
struct ContentView: View {
var body: some View {
ScrollView {
VStack {
HStack {
Text("Hello, World")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempor justo quam, quis suscipit leo sollicitudin vel.")
//LabelView(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempor justo quam, quis suscipit leo sollicitudin vel.")
}
HStack {
Text("Hello, World")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempor justo quam, quis suscipit leo sollicitudin vel.")
}
Spacer()
}
}
}
}
struct LabelView: UIViewRepresentable {
var text: String
func makeUIView(context: UIViewRepresentableContext<LabelView>) -> UILabel {
let label = UILabel()
label.text = text
label.numberOfLines = 0
return label
}
func updateUIView(_ uiView: UILabel, context: UIViewRepresentableContext<LabelView>) {
uiView.text = text
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
使用 Text 和 LabelView 會導致這種不需要的布局:
如果您將LabelView
包裝在GeometryReader
中並將width
傳遞給LabelView
以設置preferredMaxLayoutWidth
,由於某種原因它是0.0
。 如果將GeometryReader
移到ScrollView
之外,您可以獲得一個寬度,但它是滾動視圖寬度,而不是 SwiftUI 為HStack
中的LabelView
建議的寬度。
相反,如果我指定label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
它工作得更好,但仍然不顯示所有文本,它奇怪地截斷了 3 行的LabelView
和 2 行的第二個Text
。
這里的問題在於ScrollView
需要確定的高度,但表示不提供它。 可能的解決方案是動態計算換行文本高度並明確指定它。
注意:由於高度是動態計算的,它僅在運行時可用,因此無法使用預覽進行測試。
用 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
}
}
}
}
這不是一個具體的答案。
我偶然發現固定大小修飾符可以幫助將 UILabel 自動調整為其內容大小。
struct ContentView: View {
let attributedString: NSAttributedString
var body: some View {
ScrollView {
LazyVStack(spacing: 10) {
RepresentedUILabelView(attributedText: attributedString)
.frame(maxHeight: 300)
.fixedSize(horizontal: false, vertical: true)
.background(Color.orange.opacity(0.5))
}
.padding()
}
}
}
struct RepresentedUILabelView: UIViewRepresentable {
typealias UIViewType = UILabel
var attributedText: NSAttributedString
func makeUIView(context: Context) -> UILabel {
let label = UILabel()
label.numberOfLines = 0
label.lineBreakMode = .byTruncatingTail
label.textAlignment = .justified
label.allowsDefaultTighteningForTruncation = true
// Compression resistance is important to enable auto resizing of this view,
// that base on the SwiftUI layout.
// Especially when the SwiftUI frame modifier applied to this view.
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
label.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
// Maybe this is not necessary.
label.clipsToBounds = true
return label
}
func updateUIView(_ uiView: UILabel, context: Context) {
print(#fileID, #function)
uiView.attributedText = attributedText
}
}
演示:
此外,如果您不想提供最大高度。 您可以將preferredMaxLayoutWidth
設置為您的屏幕寬度。 如果你把它放在 updateUIView 方法中,每當屏幕方向改變時,這個方法也會被調用。
func updateUIView(_ uiView: UILabel, context: Context) {
uiView.attributedText = attributedText
uiView.preferredMaxLayoutWidth = 0.9 * UIScreen.main.bounds.width
}
為了完整起見,這是我用來測試視圖的屬性文本設置。 但是,它不應該影響視圖調整大小的工作方式。
func makeAttributedString(fromString s: String) -> NSAttributedString {
let content = NSMutableAttributedString(string: s)
let paraStyle = NSMutableParagraphStyle()
paraStyle.alignment = .justified
paraStyle.lineHeightMultiple = 1.25
paraStyle.lineBreakMode = .byTruncatingTail
paraStyle.hyphenationFactor = 1.0
content.addAttribute(.paragraphStyle,
value: paraStyle,
range: NSMakeRange(0, s.count))
// First letter/word
content.addAttributes([
.font : UIFont.systemFont(ofSize: 40, weight: .bold),
.expansion : 0,
.kern : -0.2
], range: NSMakeRange(0, 1))
return content
}
let coupleWords = "Hello, world!"
let multilineEN = """
Went to the woods because I wished to live deliberately, to front only the essential facts of life, and see if I could not learn what it had to teach, and not, when I came to die, discover that I had not lived. I did not wish to live what was not life, living is so dear! Nor did I wish to practise resignation, unless it was quite necessary?"
I went to the woods because I wished to live deliberately, to front only the essential facts of life, and see if I could.
I went to the woods because I wished to live deliberately, to front only the essential facts of life, and see if I could not learn what it had to teach, and not.
"""
問題在於 label 的寬度限制。無需修改內容抗壓縮性或其他 label 設置。 只需設置preferredMaxLayoutWidth
值:
public struct AttributedText: UIViewRepresentable {
public func makeUIView(context: Context) -> UIViewType {
let label = UIViewType()
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.attributedText = attributedText
label.preferredMaxLayoutWidth = width
label.textAlignment = .left
return label
}
public func updateUIView(_ uiView: UIViewType, context: Context) {
}
public typealias UIViewType = UILabel
public let attributedText: String
public let width: CGFloat
public init(attributedText: String, width: CGFloat) {
self.attributedText = attributedText
self.width = width
}
}
用法:
AttributedLabel(
attributedText: someAttributedText,
width: UIScreen.main.bounds.width - 80
).padding(.bottom, -20)
您可以使用GeometryReader
或使用UIScreen.main.bounds.width
提供。 但是底部有一些額外的填充。 這就是為什么我使用了一些負底部填充:
當前答案中沒有一個解決方案與 SwiftUI 的本質足夠接近。 另一種思路,既然 SwiftUI 對 Text 來說已經足夠好了,為什么不做這樣的事情呢?
Text(yourstr)
.foregroundColor(.clear) <<<<<<<--------👈
.multilineTextAlignment(.leading)
.frame(maxWidth:.infinity,maxHeight: .infinity, alignment: .leading)
.overlay(
Your ActiveLabelStack() <<<<<<<--------👈
,alignment: .center
)
簡單地說,讓文字告訴你高度,是Label的底。 畢竟Text是SwiftUI的兒子。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.