【问题标题】:How to get dynamic Text height for a ScrollView with SwiftUI如何使用 SwiftUI 获取 ScrollView 的动态文本高度
【发布时间】: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

【问题讨论】:

标签: swift swiftui


【解决方案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),则有两种可接受的结果:

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

      然而,实际发生的是,文本视图尊重新的宽度大小(如省略号所示),但尽管有 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()
      

      【讨论】: