复杂视图中的 SwiftUI 字体缩放

Posted

技术标签:

【中文标题】复杂视图中的 SwiftUI 字体缩放【英文标题】:SwiftUI Font scaling in a complex View 【发布时间】:2019-10-20 16:06:48 【问题描述】:

我的目标是确保容器中的文本能够根据其父级进行缩放。当容器只包含一个 Text 视图时效果很好,如下所示:

import SwiftUI

struct FontScalingExperiment: View 
    var body: some View 
        Text("Hello World ~!")
            .font(.system(size: 500))
            .minimumScaleFactor(0.01)
            .lineLimit(1)
            .padding()
            .background(
                RoundedRectangle(cornerRadius: 20)
                    .fill(Color.yellow)
                    .scaledToFill()  
        )
    


struct FontScalingExperiment_Previews: PreviewProvider 
    static var previews: some View 
        Group 
            FontScalingExperiment()
                .previewLayout(.fixed(width: 100, height: 100))
            FontScalingExperiment()
                .previewLayout(.fixed(width: 200, height: 200))
            FontScalingExperiment()
                .previewLayout(.fixed(width: 300, height: 300))
            FontScalingExperiment()
                .previewLayout(.fixed(width: 400, height: 400))
        

    

结果:

但是,当我们有更复杂的 View 时,我们不能使用相同的方法来根据其父大小自动缩放文本,例如:

import SwiftUI

struct IndicatorExperiment: View 
    var body: some View 
        VStack 
            HStack 
                Text("Line 1")
                Spacer()
            
            Spacer()
            VStack 
                Text("Line 2")
                Text("Line 3")
            
            Spacer()
            Text("Line 4")
        
        .padding()
        .background(
            RoundedRectangle(cornerRadius: 20)
                .fill(Color.yellow)
        )
            .aspectRatio(1, contentMode: .fit)
    


struct IndicatorExperiment_Previews: PreviewProvider 
    static var previews: some View 
        Group 
            IndicatorExperiment()
                .previewLayout(.fixed(width: 100, height: 100))
            IndicatorExperiment()
                .previewLayout(.fixed(width: 200, height: 200))
            IndicatorExperiment()
                .previewLayout(.fixed(width: 300, height: 300))
            IndicatorExperiment()
                .previewLayout(.fixed(width: 400, height: 400))
        
    

只需添加这 3 个修饰符:

.font(.system(size: 500)) .minimumScaleFactor(0.01) .lineLimit(1)

不会像第一个例子那样产生结果;文本放大超出框架。 我成功了,使用 GeometryReader 产生了我想要的结果,然后根据geometry.size.width 缩放字体大小。这是在 SwiftUI 中实现预期结果的唯一方法吗?

【问题讨论】:

我有您遇到的确切问题,但我无法在任何地方找到答案。在我将视图放入 HStack 之前,一切都按预期工作。您介意与 GeometryReader 分享您的解决方案吗? GeometryReader 为我们提供了框架的高度和宽度,使用这个大小我们可以相应地设置字体。例如:GeometryReader g in HStack ... .font(.system(size: g.size.width / ratio)) ratio 是任意数字,来调整大小。需要进行手动视觉验证以确保它为您的最小帧正确渲染。 您的解决方案运行良好。目视检查是关键。谢谢分享。 【参考方案1】:

您可以尝试使所有Texts 高度相同。为此,您需要明确设置填充和间距,因此这将缩放而不是固定的默认值。

此外,Spacer() 在这里没有多大意义 - 如果要求所有 Text 保持相同大小,Spacer 只会使 all 文本变小.对于Text 根据空间进行缩放,而Spacer 试图使用尽可能多的空间,这是一个矛盾。相反,我决定只在初始化程序中设置VStackspacing

工作代码:

struct IndicatorExperiment: View 
    private let size: CGFloat
    private let padding: CGFloat
    private let primarySpacing: CGFloat
    private let secondarySpacing: CGFloat
    private let textHeight: CGFloat

    init(size: CGFloat) 
        self.size = size
        padding = size / 10
        primarySpacing = size / 15
        secondarySpacing = size / 40

        let totalHeights = size - padding * 2 - primarySpacing * 2 - secondarySpacing
        textHeight = totalHeights / 4
    

    var body: some View 
        VStack(spacing: primarySpacing) 
            HStack 
                scaledText("Line 1")

                Spacer()
            
            .frame(height: textHeight)

            VStack(spacing: secondarySpacing) 
                scaledText("Line 2")

                scaledText("Line 3")
            
            .frame(height: textHeight * 2 + secondarySpacing)

            scaledText("Line 4")
        
        .padding(padding)
        .background(
            RoundedRectangle(cornerRadius: 20)
                .fill(Color.yellow)
        )
        .aspectRatio(1, contentMode: .fit)
        .frame(width: size, height: size)
    

    private func scaledText(_ content: String) -> some View 
        Text(content)
            .font(.system(size: 500))
            .minimumScaleFactor(0.01)
            .lineLimit(1)
            .frame(height: textHeight)
    

要测试的代码:

struct ContentView: View 
    var body: some View 
        ScrollView 
            VStack(spacing: 50) 
                IndicatorExperiment(size: 100)

                IndicatorExperiment(size: 200)

                IndicatorExperiment(size: 300)

                IndicatorExperiment(size: 400)
            
        
    

结果:

【讨论】:

【参考方案2】:

使用GeometryReader.minimumScaleFactor 修饰符可能是缩放视图中文本的唯一方法。要对大小进行更多控制,一种可能的方法是从父视图中提供 .frame 大小。

可缩放的文本视图

    GeometryReader  geo in
        Text("Foo")
            .font(
                .system(size: min(geo.size.height, geo.size.width) * 0.95))
            .minimumScaleFactor(0.05)
            .lineLimit(1)
    

使用可缩放文本视图的父视图

    GeometryReader  geo in
        ScaleableText()
            .frame(width: geo.size.width, height: geo.size.height)
    

【讨论】:

以上是关于复杂视图中的 SwiftUI 字体缩放的主要内容,如果未能解决你的问题,请参考以下文章

动态字体大小缩放

更改选项卡式视图栏颜色 SwiftUI

SwiftUI NavigationBar 后退按钮在使用自定义字体时抱怨图标缩放

SwiftUI/UIKit:水平滚动视图缩放视图

SwiftUI 缩放视图而不影响几何

如何为 UIPageViewController 中的所有 webViews 文本字体启用缩放