How to scale text to fit parent view with SwiftUI?

前端 未结 6 862
-上瘾入骨i
-上瘾入骨i 2021-02-02 09:41

I\'d like to create a text view inside a circle view. The font size should be automatically set to fit the size of the circle. How can this be done in SwiftUI? I tried scaledToF

6条回答
  •  南方客
    南方客 (楼主)
    2021-02-02 10:05

    Here's a solution that hides the text resizing code in a custom modifier which can be applied to any View, not just a Circle, and takes a parameter specifying the fraction of the View that the text should occupy.

    (I have to agree that while @szemian's solution is still not ideal, her method seems to be the best we can do with the current SwiftUI implementation because of issues inherent in the others. @simibac's answer requires fiddling to find a new magic number to replace 0.4 any time the text or its attributes--font, weight, etc.--are changed, and @giuseppe-sapienza's doesn't allow the size of the circle to be specified, only the font size of the text.)

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

    Using the modifier, the code becomes just this:

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

    Also, when a future version of Xcode offers SwiftUI improvements that obviate the .minimumScaleFactor hack, you can just update the modifier code to use it. :)

    If you want to see how the fraction parameter works, here's code to let you adjust it interactively with a slider:

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

    and here's what it looks like:

提交回复
热议问题