如何使用 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 的动态文本高度的主要内容,如果未能解决你的问题,请参考以下文章