如何缩放文本,以适应与 SwiftUI 的父视图?

我想创建一个文本视图内的圆形视图。字体大小应该自动设置为适合圆的大小。在 SwiftUI 中如何做到这一点?我试过 scaledTofill 和 scaledToFit 修饰符,但它们对 Text 视图没有影响:

struct ContentView : View {
var body: some View {
ZStack {
Circle().strokeBorder(Color.red, lineWidth: 30)
Text("Text").scaledToFill()
}
}
}
59961 次浏览

要做到这一点,你不需要 ZStack。你可以添加一个背景到 Text:

Text("Text text text?")
.padding()
.background(
Circle()
.strokeBorder(Color.red, lineWidth: 10)
.scaledToFill()
.foregroundColor(Color.white)
)

结果是: enter image description here

一种可能的“黑客技术”是使用大字体和小比例因子,这样它就会缩小自己:

ZStack {
Circle().strokeBorder(Color.red, lineWidth: 30)


Text("Text")
.padding(40)
.font(.system(size: 500))
.minimumScaleFactor(0.01)
}
}

人们可以使用 GeometryReader为了使它也工作在景观模式。

它首先检查宽度或高度是否较小,然后根据其中较小的值调整字体大小。

GeometryReader{g in
ZStack {
Circle().strokeBorder(Color.red, lineWidth: 30)
Text("Text")
.font(.system(size: g.size.height > g.size.width ? g.size.width * 0.4: g.size.height * 0.4))
}
}

enter image description here enter image description here

这里有一个解决方案,它将调整文本大小的代码隐藏在一个自定义修饰符中,这个修饰符可以应用于任何视图,而不仅仅是一个圆,并接受一个指定文本应该占据的视图分数的参数。

(我不得不承认,虽然@szemian 的解决方案仍然不理想,但由于其他方法固有的问题,她的方法似乎是我们对当前 SwiftUI 实现所能做的最好的解决方案。@ simibac 的回答需要找到一个新的神奇数字,在文本或其属性(字体、重量等)发生变化时替换0.4,而@giuseppe-sapienza 不允许指定圆圈的大小,只允许指定文本的字体大小。)

struct FitToWidth: ViewModifier {
var fraction: CGFloat = 1.0
func body(content: Content) -> some View {
GeometryReader { g in
content
.font(.system(size: 1000))
.minimumScaleFactor(0.005)
.lineLimit(1)
.frame(width: g.size.width*self.fraction)
}
}
}

使用这个修饰符,代码就变成了这样:

    var body: some View {
Circle().strokeBorder(Color.red, lineWidth: 30)
.aspectRatio(contentMode: .fit)
.overlay(Text("Text")
.modifier(FitToWidth(fraction: fraction)))
}

此外,当 Xcode 的未来版本提供了避免 .minimumScaleFactor黑客攻击的 SwiftUI 改进时,您可以只更新修饰符代码来使用它。:)

如果你想知道分数参数是如何工作的,下面的代码可以让你用滑块交互式地调整它:

struct ContentView: View {


@State var fraction: CGFloat = 0.5


var body: some View {
VStack {
Spacer()
Circle().strokeBorder(Color.red, lineWidth: 30)
.aspectRatio(contentMode: .fit)
.overlay(Text("Text")
.modifier(FitToWidth(fraction: fraction)))
Slider(value: $fraction, in:0.1...0.9, step: 0.1).padding()
Text("Fraction: \(fraction, specifier: "%.1f")")
Spacer()
}
}
}

看起来是这样的:

Code running on iPhone X

您希望允许您的文本:

  • 缩小到某一限度
  • 在一行(或多行)上

您选择此比例因子限制以满足您的需要。通常情况下,您不会缩小到超出可读性或超出限制,这将使您的设计看起来很糟糕

struct ContentView : View {
var body: some View {
ZStack {
Circle().strokeBorder(Color.red, lineWidth: 30)
Text("Text")
.scaledToFill()
.minimumScaleFactor(0.5)
.lineLimit(1)
}
}

}

我把@Simibac 和@Anton 的答案混合在一起,结果被 iOS14.0打破了,所以我做了以下的修复工作。应该也可以在 SwiftUI 1.0上工作。

struct FitSystemFont: ViewModifier {
var lineLimit: Int
var minimumScaleFactor: CGFloat
var percentage: CGFloat


func body(content: Content) -> some View {
GeometryReader { geometry in
content
.font(.system(size: min(geometry.size.width, geometry.size.height) * percentage))
.lineLimit(self.lineLimit)
.minimumScaleFactor(self.minimumScaleFactor)
.position(x: geometry.frame(in: .local).midX, y: geometry.frame(in: .local).midY)
}
}
}

正如你所看到的,我使用了几何代理的 frame(in:)方法来获得本地坐标空间,然后使用 .midX.midY来正确地将其居中,因为正确的居中是我在 iOS 14上遇到的问题。

然后我在 View上设置了一个分机:

extension View {
func fitSystemFont(lineLimit: Int = 1, minimumScaleFactor: CGFloat = 0.01, percentage: CGFloat = 1) -> ModifiedContent<Self, FitSystemFont> {
return modifier(FitSystemFont(lineLimit: lineLimit, minimumScaleFactor: minimumScaleFactor, percentage: percentage))
}
}

所以用法是这样的:

Text("Your text")
.fitSystemFont()

基于@JaimeeAz 的回答。增加了一个指定最小字体的选项。

import SwiftUI


public struct FitSystemFont: ViewModifier {
public var lineLimit: Int?
public var fontSize: CGFloat?
public var minimumScaleFactor: CGFloat
public var percentage: CGFloat


public func body(content: Content) -> some View {
GeometryReader { geometry in
content
.font(.system(size: min(min(geometry.size.width, geometry.size.height) * percentage, fontSize ?? CGFloat.greatestFiniteMagnitude)))
.lineLimit(self.lineLimit)
.minimumScaleFactor(self.minimumScaleFactor)
.position(x: geometry.frame(in: .local).midX, y: geometry.frame(in: .local).midY)
}
}
}


public extension View {
func fitSystemFont(lineLimit: Int? = nil, fontSize: CGFloat? = nil, minimumScaleFactor: CGFloat = 0.01, percentage: CGFloat = 1) -> ModifiedContent<Self, FitSystemFont> {
return modifier(FitSystemFont(lineLimit: lineLimit, fontSize: fontSize, minimumScaleFactor: minimumScaleFactor, percentage: percentage))
}
}

我的定时器也有同样的问题。不幸的是,计时器每秒钟改变一次文本,所以文本一直在跳来跳去,不断地上升和下降。 我的方法是计算,什么是可以显示的最长的计时器-在我的情况下“-44:44:44”-和50点大小将导致227.7点大帧。227除以50(我以前用过的点大小) ,每个点的宽度为4.5(向下舍入)。 小心: 1 pt 的大小给了我一个5.3点大的框架-所以越大的字体越接近实际的文本大小没有框架它得到。

因为我正在使用一个几何阅读器无论如何,我可以简单地设置一个固定的文本大小,使用

Text("-44:44:44")
.font(.system(size: geometry.size.width / 4.5))

工作非常好,如果没有’-’或没有小时显示,我有一些空间的左边和右边,但文本不跳转。

这可以通过不同的比例因子进行细化,具体取决于所显示的数字数量,因此“-mm: ss”的另一个比例因子是。 当时间显示或隐藏时,这将导致“跳转”——但这很少发生在我需要的时候。

我有固定大小的按钮,这为我工作,以自动缩小长文本。

Text("This is a long label that will be scaled to fit:")
.lineLimit(1)
.minimumScaleFactor(0.5)

来源: 苹果