【问题标题】:How to scale text to fit parent view with SwiftUI?如何使用 SwiftUI 缩放文本以适应父视图?
【发布时间】:2019-11-23 22:05:12
【问题描述】:

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

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

【问题讨论】:

    标签: swift swiftui


    【解决方案1】:

    以@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))
        }
    }
    

    【讨论】:

      【解决方案2】:

      对我来说效果出奇的便宜的厚厚的

      struct ContentView: View {
      
      var scale = UIScreen.main.bounds.width*0.08
      
      var body: some View {
      Text("something").font(.system(size: scale))
      }}
      

      【讨论】:

        【解决方案3】:

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

        (我不得不同意,虽然@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 的未来版本提供 SwiftUI 改进以消除 .minimumScaleFactor hack 时,您只需更新修饰符代码即可使用它。 :)

        如果你想看看分数参数是如何工作的,下面的代码可以让你使用滑块进行交互调整:

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

        这是它的样子:

        【讨论】:

        • 这也适用于多行文本以适应其父级的高度。只需删除行限制并将最后一帧更改为'height:g.size.height'初始字体大小也可以设置为'g.size.height',因为字体点大小可能不需要更大如果这是您想要适应的身高,则高于父母的身高。
        【解决方案4】:

        我混合了@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()
        

        【讨论】:

        • 不错!谢谢。
        【解决方案5】:

        您希望允许您的文本:

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

        您可以根据需要选择此比例因子限制。通常,您不会缩小超出可读性或超出使您的设计看起来很糟糕的限制

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

        }

        【讨论】:

          【解决方案6】:

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

          【讨论】:

          • 实际上,这个解决方案比我之前标记为正确答案的“hack”更干净。唯一的缺点是,您必须为字体大小找到正确的比例因子。但是一旦你找到它,它就适用于所有屏幕尺寸和方向。谢谢!
          • min(g.size.height, g.size.width) * scale 呢?
          • 这是一个很好的固定文本解决方案。不幸的是,Text 在 iOS 13 上的渲染似乎比在 iOS 14 上略大。这意味着比例因子在不同的操作系统版本上不会产生相同的结果,并可能导致截断。
          • 你拯救了这一天!
          【解决方案7】:

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

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

          【讨论】:

          • 使用该解决方案进行更多测试后,我注意到文本无法在所有纵横比上精确缩放。这是因为文本视图不是方形的。我可以通过在 ZStack 视图中添加“aspectRatio”修饰符来解决这个问题:.aspectRatio(1, contentMode: .fit)
          • 自 XCode 11 Beta 6 起,如果文本不适合当前宽度,它会自动换行。因此,您需要将 .lineLimit(1) 添加到 Text 元素以使其再次工作。
          • 我正在使用这种技术,但是有一个错误,即在设备旋转时字体变成最小尺寸。当我单击更改 EnvironmentObject 的按钮时,视图会刷新并且字体大小再次变大。
          • @Ryan 我添加了一个也适用于横向的解决方案。
          【解决方案8】:

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

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

          结果是这样的:

          【讨论】:

          • 谢谢,但这并不是我所需要的。主要问题是自动增加字体大小以填充给定容器。您的示例只是将圆圈放在给定文本周围。知道如何解决这个问题吗?
          猜你喜欢
          • 2011-11-07
          • 2011-03-31
          • 1970-01-01
          • 1970-01-01
          • 2014-11-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-06-22
          相关资源
          最近更新 更多