如何使用 SwiftUI 获取 ScrollView 的动态文本高度

Posted

技术标签:

【中文标题】如何使用 SwiftUI 获取 ScrollView 的动态文本高度【英文标题】:How to get dynamic Text height for a ScrollView with SwiftUI 【发布时间】:2019-06-30 15:28:43 【问题描述】:

这给了我正确的动态文本高度

import SwiftUI
struct ContentView : View 
    var body: some View 
        List 
            Text("This is some very long text can we can see scrolls past two lines ").lineLimit(nil)   
        
    


#if DEBUG
struct ContentView_Previews : PreviewProvider 
    static var previews: some View 
        ContentView()
    

#endif

但以下内容会截断文本。如何通过以下方式获得动态高度?

import SwiftUI
struct ContentView : View 
    var body: some View 
        GeometryReader  reader in
            ScrollView 
                Text("This is some very long text can we can see scrolls past two lines ")
                    .lineLimit(nil)
                    .frame(width: reader.size.width)
            
        
    

#if DEBUG
struct ContentView_Previews : PreviewProvider 
    static var previews: some View 
        ContentView()
    

#endif

【问题讨论】:

这个 SO 帖子有一个很好的解决方法:***.com/questions/56593120/… 【参考方案1】:

您需要使用fixedSize 修饰符来防止截断。在你的情况下是这样的:

Text("This is some very long text can we can see scrolls past two lines ")
 .lineLimit(nil)
 .fixedSize(horizontal: false, vertical: true) //** It keeps 'width size' and expands 'height size'
 .frame(width: reader.size.width)

【讨论】:

【参考方案2】:
import SwiftUI

struct ContentView : View 

    let veryLongText = "Very long text..."
    let fontName = "System Font"
    let fontSize: Length = 12

    var body: some View 
        GeometryReader  geometry in
            ScrollView 
                Text(veryLongText)
                    .lineLimit(nil)
                    .font(.custom(fontName, size: fontSize))
                    .frame(width: geometry.size.width, height: veryLongText.textHeightFrom(width: geometry.size.width, fontName: fontName, fontSize: fontSize))
            
        
        .padding(.horizontal, 0.5 * fontSize)
    


extension String 

    func textHeightFrom(width: CGFloat, fontName: String = "System Font", fontSize: CGFloat = .systemFontSize) -> CGFloat 

        #if os(macOS) 

        typealias UXFont = NSFont
        let text: NSTextField = .init(string: self)
        text.font = NSFont.init(name: fontName, size: fontSize)

        #else

        typealias UXFont = UIFont
        let text: UILabel = .init()
        text.text = self
        text.numberOfLines = 0

        #endif

        text.font = UXFont.init(name: fontName, size: fontSize)
        text.lineBreakMode = .byWordWrapping
        return text.sizeThatFits(CGSize.init(width: width, height: .infinity)).height
    

【讨论】:

【参考方案3】:

可能是ScrollView 错误。老实说,我不确定为什么您的代码不起作用,但是您可以使用以下方法获得所需的结果:

struct ContentView : View 
    var body: some View 
        GeometryReader  reader in
            ScrollView(alwaysBounceVertical: true) 
                ZStack(alignment: .top) 
                    Color.clear.edgesIgnoringSafeArea(.all)
                    Text("This is some very long text can we can see scrolls past two lines ")
                        .lineLimit(nil)
                .frame(width: reader.size.width, height: reader.size.height)
            
        
    

我在滚动视图中添加了alwaysBounceVertical,这样您就可以看到视图滚动了。不过没必要。

【讨论】:

+ 用于解决问题。如果这不是一个错误,我会暂时搁置这个问题。因为我需要这个视图变得更复杂,我不确定解决方法是否可行【参考方案4】:

在我看来,这是一个错误,因为我找不到其行为的基本原理。

让我解释一下:

如果您没有 ScrollView,我们知道将 Text 视图的宽度强制为特定宽度并使用 lineLimit(nil) 将使文本视图垂直扩展以容纳文本。相反,如果您有 lineLimit(1),则省略号将显示以截断视图的内容。

现在,如果我们在 ScrollView 中有 Text(),并且我们将宽度强制为特定大小并设置 lineLimit(nil),则有两种可接受的结果:

    文本视图遵循新宽度并垂直扩展。 文本视图不考虑宽度,但允许您水平滚动。这根本不是一个好的选择,但它可能是由谁开发框架做出的决定。

然而,实际发生的是,文本视图尊重新的宽度大小(如省略号所示),但尽管有 lineLimit(nil),但它不会垂直扩展。没有理由这样做,因为这种行为已经可以通过使用 lineLimit(1) 来实现。这就是为什么我认为这绝对是一个错误。

您应该向 Apple 提交错误报告。

【讨论】:

还要注意,如果你强制高度,比如说 100 点,那么文本会延伸到超过 1 行。问题是你事先不知道你需要多少。 同意,这绝对是一个错误,而且很容易重现。只需创建一个新的 SwiftUI 文件,将现有的“Hello World”Text() 包装在 ScrollView 中,然后使用长字符串创建文本。我已经对此提交了反馈,并且真的鼓励大家也这样做。 我可以确认这个问题在 beta 5 中仍然存在。我有相同的用例,需要在滚动视图中包装长文本。看来 ScrollView 是非常错误的 atm。它还破坏了.sheet 修饰符。还为此提交了错误报告。【参考方案5】:

这似乎已在 ios 15.0 中得到解决。 但是如果你想在 iOS 14 上支持用户,那么你可以添加一个 hack 视图修饰符

@available(iOS 14.0, *)
fileprivate struct LineLimitHackForiOS14: ViewModifier 

    func body(content: Content) -> some View 
        if #available(iOS 15.0, *) 
            return AnyView(content)
        
        return AnyView(content.fixedSize(horizontal: false, vertical: true))
    


@available(iOS 14.0, *)
extension View 
    fileprivate func applyHackToPreventTextTruncationInScrollViews() -> some View 
        self.lineLimit(nil).modifier(LineLimitHackForiOS14())
    

代码可以用作的地方

Text(self.text).applyHackToPreventTextTruncationInScrollViews()

【讨论】:

以上是关于如何使用 SwiftUI 获取 ScrollView 的动态文本高度的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 swiftUI 获取 ComboBox

如何使用 SwiftUI 在动作关闭中获取按钮本身?

在使用 swiftui 的小部件中如何获取当前位置? [复制]

如何使用 SwiftUI 获取文本宽度?

如何使用 SwiftUI 获取文本宽度?

如何使用 SwiftUI 获取 ScrollView 的动态文本高度