代码之家  ›  专栏  ›  技术社区  ›  Jordan H

在SwiftUI中通过UIViewRepresentable like Text调整UILabel的大小以换行

  •  0
  • Jordan H  · 技术社区  · 4 年前

    我们的目标是 UILabel UIViewRepresentable 大小相同 Text does-使用可用的宽度和换行符来填充所有文本,从而增加 HStack 它在里面,而不是无限扩大宽度。这和 this question 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()
        }
    }
    

    在HStack中使用两个文本可以得到所需的布局: Text and Text

    使用文本和标签视图会导致此不需要的布局: Text and LabelView

    如果你把 LabelView 在里面 GeometryReader width 标签视图 设置 preferredMaxLayoutWidth ,它是 0.0 因为某种原因。如果你移动 外面 卷轴视图 标签视图 HStack公司 .

    如果我指定 label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) 标签视图 文本 在2行。

    0 回复  |  直到 4 年前
        1
  •  5
  •   Asperi    4 年前

    问题出在这里 ScrollView

    注意:由于高度是动态计算的,因此只能在运行时使用,因此无法使用预览进行测试。

    demo

    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
                }
            }
        }
    }
    
        2
  •  0
  •   zrfrank    4 年前

    这不是一个具体的答案。

    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
        }
        
    }
    

    A paragraph

    A couple words


    此外,如果您不希望提供最大高度。你可以设置 preferredMaxLayoutWidth

        func updateUIView(_ uiView: UILabel, context: Context) {
            
            uiView.attributedText = attributedText
            
            uiView.preferredMaxLayoutWidth = 0.9 * UIScreen.main.bounds.width
        }
    

    Example, without max frame height.


    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.
        """