xcode SwiftUI -如何知道文本中的行数?

6rqinv9w  于 2023-03-24  发布在  Swift
关注(0)|答案(4)|浏览(171)

我有一个动态文本,它可以是小或大我什么默认情况下只显示3行和只有在需要添加“更多”按钮。当用户点击此按钮(“更多”)-我将显示所有测试。
我问,如何在SwiftUI中知道文本是否超过3行?

cwtwac6a

cwtwac6a1#

您可以使用GeometryReader来确定文本字段的宽度,然后将其与有关字体的信息一起使用,以计算显示整个文本所需的边界矩形的大小。如果该高度超过文本视图,则我们知道文本已被截断。

struct LongText: View {

    /* Indicates whether the user want to see all the text or not. */
    @State private var expanded: Bool = false

    /* Indicates whether the text has been truncated in its display. */
    @State private var truncated: Bool = false

    private var text: String

    init(_ text: String) {
        self.text = text
    }

    private func determineTruncation(_ geometry: GeometryProxy) {
        // Calculate the bounding box we'd need to render the
        // text given the width from the GeometryReader.
        let total = self.text.boundingRect(
            with: CGSize(
                width: geometry.size.width,
                height: .greatestFiniteMagnitude
            ),
            options: .usesLineFragmentOrigin,
            attributes: [.font: UIFont.systemFont(ofSize: 16)],
            context: nil
        )

        if total.size.height > geometry.size.height {
            self.truncated = true
        }
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            Text(self.text)
                .font(.system(size: 16))
                .lineLimit(self.expanded ? nil : 3)
                // see https://swiftui-lab.com/geometryreader-to-the-rescue/, 
                // and https://swiftui-lab.com/communicating-with-the-view-tree-part-1/
                .background(GeometryReader { geometry in
                    Color.clear.onAppear {
                        self.determineTruncation(geometry)
                    }
                })

            if self.truncated {
                self.toggleButton
            }
        }
    }

    var toggleButton: some View {
        Button(action: { self.expanded.toggle() }) {
            Text(self.expanded ? "Show less" : "Show more")
                .font(.caption)
        }
    }

}

这就是它在长文本和短文本中的样子:

希望这个有用。

gtlvzcf8

gtlvzcf82#

基于bhuemer的出色工作,这个版本尊重SwiftUI的本地字体,而不是要求硬编码的UIFont。而不是使用String布局阅读“完整”文本的大小,这将呈现文本三次:一次是真实的,一次有行限制,一次没有行限制。然后它使用两个GR来比较最后两个。

struct LongText: View {

    /* Indicates whether the user want to see all the text or not. */
    @State private var expanded: Bool = false

    /* Indicates whether the text has been truncated in its display. */
    @State private var truncated: Bool = false

    private var text: String

    var lineLimit = 3

    init(_ text: String) {
        self.text = text
    }

    var body: some View {
        VStack(alignment: .leading) {
            // Render the real text (which might or might not be limited)
            Text(text)
                .lineLimit(expanded ? nil : lineLimit)

                .background(

                    // Render the limited text and measure its size
                    Text(text).lineLimit(lineLimit)
                        .background(GeometryReader { displayedGeometry in

                            // Create a ZStack with unbounded height to allow the inner Text as much
                            // height as it likes, but no extra width.
                            ZStack {

                                // Render the text without restrictions and measure its size
                                Text(self.text)
                                    .background(GeometryReader { fullGeometry in

                                        // And compare the two
                                        Color.clear.onAppear {
                                            self.truncated = fullGeometry.size.height > displayedGeometry.size.height
                                        }
                                    })
                            }
                            .frame(height: .greatestFiniteMagnitude)
                        })
                        .hidden() // Hide the background
            )

            if truncated { toggleButton }
        }
    }

    var toggleButton: some View {
        Button(action: { self.expanded.toggle() }) {
            Text(self.expanded ? "Show less" : "Show more")
                .font(.caption)
        }
    }
}

下面显示了周围视图的行为。注意,这种方法支持LongText(...).font(.largeTitle),就像普通的Text一样。

struct ContentView: View {
    let longString = "This is very long text designed to create enough wrapping to force a More button to appear. Just a little more should push it over the edge and get us to one more line."

    var body: some View {
        VStack {
            Text("BEFORE TEXT")
            LongText(longString).font(.largeTitle)
            LongText(longString).font(.caption)
            Text("AFTER TEXT")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

laawzig2

laawzig23#

在iOS 16+中,可以通过使用ViewThatFits设置为行限制文本的隐藏背景来实现这一点。
此方法还考虑更改时的系统字体大小。

struct ExpandableText: View {
    let text: String
    let lineLimit: Int
    
    @State private var isExpanded: Bool = false
    @State private var isTruncated: Bool? = nil
    
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            Text(text)
                .lineLimit(isExpanded ? nil : lineLimit)
                .background(calculateTruncation(text: text))
            
            if isTruncated == true {
                button
            }
        }
        .multilineTextAlignment(.leading)
        // Re-calculate isTruncated for the new text
        .onChange(of: text, perform: { _ in isTruncated = nil })
    }
    
    func calculateTruncation(text: String) -> some View {
        // Select the view that fits in the background of the line-limited text.
        ViewThatFits(in: .vertical) {
            Text(text)
                .hidden()
                .onAppear {
                    // If the whole text fits, then isTruncated is set to false and no button is shown.
                    guard isTruncated == nil else { return }
                    isTruncated = false
                }
            Color.clear
                .hidden()
                .onAppear {
                    // If the whole text does not fit, Color.clear is selected, 
                    // isTruncated is set to true and button is shown.
                    guard isTruncated == nil else { return }
                    isTruncated = true
                }
        }
    }
    
    var button: some View {
        Button(isExpanded ? "Show less" : "Show more") {
            isExpanded.toggle()
        }
    }
}

hfyxw5xn

hfyxw5xn4#

下面是一个例子:

struct TextDemo: View {

    @State var moreText = true

    var body: some View {
        Group {
            Button(action: { self.moreText.toggle()} ) { Text("More") }
            Text("hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello")
                .frame(width: 300)
                .lineLimit( moreText ? 3: nil)
         }
     }
}

相关问题