如何使用SwiftUI缩放文本以适应父视图?

khbbv19g  于 2023-11-16  发布在  Swift
关注(0)|答案(9)|浏览(194)

我想在圆形视图中创建一个文本视图。字体大小应该自动设置为适合圆形的大小。如何在SwiftUI中完成此操作?我尝试了scaledToFill和scaledToFit修饰符,但它们对文本视图没有影响:

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

字符串

blmhpbnm

blmhpbnm1#

一个可能的“黑客”是使用一个大的字体大小和一个小的比例因子,所以它会缩小自己:

ZStack {
    Circle().strokeBorder(Color.red, lineWidth: 30)
                
    Text("Text")
        .padding(40)
        .font(.system(size: 500))
        .minimumScaleFactor(0.01)
     
}

字符串

dgtucam1

dgtucam12#

您希望允许文本:

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

您可以根据需要选择此比例因子限制。通常不会缩小到超出可读范围或超出会使设计看起来不好的限制

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

字符串
}

omqzjyyz

omqzjyyz3#

可以使用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))
    }
}

字符串
100d1x

的字符串

bxfogqkk

bxfogqkk4#

我有固定大小的按钮,这对我来说是自动缩小长文本。

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

字符串
Source: Apple

x8goxv8g

x8goxv8g5#

这里有一个解决方案,它在一个自定义修饰符中隐藏文本的隐藏代码,该修饰符可以应用于任何视图,而不仅仅是一个Circle,并接受一个参数,指定文本应该占据的视图部分。
(我不得不承认,虽然@szemian的解决方案仍然不理想,但她的方法似乎是我们能用当前SwiftUI实现的最好方法,因为其他方法存在固有的问题。@simibac的答案需要在文本或其属性(字体,粗细等)发生变化时,@giuseppe-sapienza's不允许指定圆圈的大小,只允许指定文本的字体大小。

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的未来版本提供SwiftUI改进来消除.minimumScaleFactor黑客攻击时,您只需更新修改器代码即可使用它。
如果你想看看分数参数是如何工作的,这里的代码可以让你用滑块交互地调整它:

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()
        }
    }
}


它看起来是这样的:
x1c 0d1x的数据

j8yoct9x

j8yoct9x6#

我做了一个@Simibac和@Anton的答案的混合,只有被iOS 14.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()

6qftjkof

6qftjkof7#

基于@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))
    }
}

字符串

wvt8vs2t

wvt8vs2t8#

要实现这一点,您不需要ZStack。您可以向Text添加背景:

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

字符串
结果如下:

20jt8wwn

20jt8wwn9#

我的计时器也遇到了同样的问题。不幸的是,计时器每秒会改变一次文本,所以文本总是跳来跳去,并不断地上下缩放。我的方法是计算,可以显示的最长计时器是多少-在我的例子中是“-44:44:44”-并且使用50 pt大小,这将导致227.7pt的大帧。227除以50(我以前用过的点大小)每点大小4. 5的宽度(四舍五入)。小心:用1点大小,它给了我一个5. 3点的大框架-所以字体越大,就越接近实际的文本大小,而不是它得到的框架。
由于我使用的是GeometryReader,我可以简单地设置一个固定的文本大小,使用

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

字符串
工作得很好,如果没有'-'或没有小时显示我有一些空间的左边和右边,但文本不跳来跳去。
这可以用不同的比例因子来细化,这取决于那里显示的数字数量--所以“-mm:ss”的另一个比例因子。这将导致在显示或隐藏小时时的“跳跃”--但这很少发生在我需要的时候。

相关问题