Scaling down a text's font size to fit its length to another text in SwiftUI

Viewed 254

Consider the following code:

VStack{
    Text("A reference text")
    Text("(Adjust the length of this line to match the one above)")
}

I want the second Text() to be scaled down in font size to be exactly the length of the sentence above. I've already tried:

Text("(Adjust the length of this line to match the one above)")
     .scaledToFit()
     .minimumScaleFactor(0.01)

but it seems to not work. How can I achieve this?

3 Answers

Here is possible simple approach (tested with Xcode 13.4 / iOS 15.5)

Update: found even more simpler, based on the same concept (works with dynamic sizes as well)

Text("A reference text")
    .padding(.bottom)     // << spacing can be tuned 
    .overlay(
        Text("(Adjust the length of this line to match the one above)")
            .scaledToFit()
            .minimumScaleFactor(0.01)
    , alignment: .bottom)     // << important !!

Original:

VStack {
    let ref = "A reference text"
    let target = "(Adjust the length of this line to match the one above)"
    Text(ref)
    Text(ref).opacity(0)
        .overlay(Text(target)
            .scaledToFit()
            .minimumScaleFactor(0.01)
        )
}

demo

You need to get the size of the reference view

struct ScaledTextView: View {
    @State var textSize: CGSize =  CGSize.zero
    var body: some View {
        VStack{
            Text("A reference text")
                .viewSize(viewSize: $textSize) //Get initial size and any changes
            Text("(Adjust the length of this line to match the one above)")
            //If you want it to be a single line set the limit
            //.lineLimit(1)
            
            //Set the dependent's view frame, you can just set the width if you want, height is optional
                .frame(width: textSize.width, height: textSize.height)
                .scaledToFit()
                .minimumScaleFactor(0.01)
        }
    }
}
//Make it reusable and keep the view clean
extension View{
    func viewSize(viewSize: Binding<CGSize>) -> some View{
        return self.background(
            //Get the size of the View
            GeometryReader{geo in
                Text("")
                //detects changes such as landscape, etc
                    .onChange(of: geo.size, perform: {value in
                        //set the size
                        viewSize.wrappedValue = value
                    })
                //Initial size
                    .onAppear(perform: {
                        //set the size
                        viewSize.wrappedValue = geo.size
                    })
            }
        )
    }
}

This is a method which doesn't fix the smaller text's height.

struct ContentView: View {
    @State private var width: CGFloat?

    var body: some View {
        VStack {
            Text("A reference text")
                .background(
                    GeometryReader { geo in
                        Color.clear.onAppear { width = geo.size.width }
                    }
                )

            Text("(Adjust the length of this line to match the one above)")
                .scaledToFit()
                .minimumScaleFactor(0.01)
        }
        .frame(width: width)
        .border(Color.red)
    }
}

Result:

Result

Related