SwiftUI - Text minimumScaleFactor not scaling only when needed

Viewed 3819

I have a Text that I want to modify with a .minimumScaleFactor(0.1) when the length of its string extends outside the view. However, the scaling applies every time, even when the original font size would be perfectly fine.

My view is structured thusly:

        VStack(alignment: .center, spacing: 0) {
            HStack {
                Image("medal \(place)").resizable()
                    .foregroundColor(color)
                    .frame(width: 40, height: 40)
                Spacer()
                Text(username)
                    .font(.bold(16))
                    .lineLimit(1)
                    .minimumScaleFactor(0.1)
                    .frame(alignment: .trailing)
                    .foregroundColor(Color("mediumTextColor"))
            }
            Spacer()
            Text(score)
                .font(.extraBold(60))
                .foregroundColor(color)
                .lineLimit(1)
                .minimumScaleFactor(0.7)

            Spacer()
        }
        .frame(height: 96)
        .padding(10)
        .cornerRadius(16)
        .overlay(RoundedRectangle(cornerRadius: 16)
        .stroke(color, lineWidth: 2))
6 Answers

A continuation of answer above.

What is important is the order in which the modifiers are applied:

Text("This is a really long sentence.") 
   .minimumScaleFactor(0.5)                
   .font(.custom("OpenSans", size: 15))
   .lineLimit(1)
   .layoutPriority(1)

There isn't a need for the GeometryReader unless you need it for something else

Adding .lineLimit(1) to the Text will work well.

Xcode: 11.3.1

Important for that minimumScaleFactor() only does its job when it is needed, is the combination of .minimumScaleFactor AND .lineLimit(1); otherwise it will not work. layoutPriority(1) is not the key here.

Example - this works; Scale is only applied when needed:

Text("Something").minimumScaleFactor(0.5).lineLimit(1)

Adding a GeometryReader around the VStack that my two Text views were in solved this. As requested by @gohnjanotis:

GeometryReader { proxy in
    VStack(alignment: .center, spacing: 0) {
        HStack {
            Image("medal \(self.place)").resizable()
                .foregroundColor(self.color)
                .frame(width: 40, height: 40)

            Spacer()
            Text(self.username)
                .minimumScaleFactor(0.1)
                .font(.bold(16))
                .lineLimit(1)
                .frame(alignment: .trailing)
                .foregroundColor(Color("mediumTextColor"))
                .layoutPriority(1)

        }
        .padding(.top, 10)
        .padding(.horizontal, 10)
        Spacer()
        Text(self.score)
            .font(.extraBold(60))
            .foregroundColor(self.color)
            .lineLimit(1)
            .minimumScaleFactor(0.1)
            .layoutPriority(1)
            .padding(.horizontal, 10)
            .padding(.bottom, 10)
            .offset(y: -10)
    }
    .frame(width: proxy.size.width, height: proxy.size.height, alignment: .top)
}
.frame(maxHeight: 130)

Add .layoutPriority(1) modifier to your Text to make sure it takes the space if there is any.

After trying to figuring this out for a long while... playing with minimumScaleFactor, LineLimit, layoutPriority... I found out that using .frame() helps solve my problem.

My problem is that minimumScaleFactor applies to Text even if it is not needed.

Applying .frame() ad the last line solve my problem.

Something like this...

GeometryReader { geometry in
    VStack{
        
        Text("Hello")
            .foregroundColor(Color.Black)
            .font(.system(size: 50))
            .minimumScaleFactor(0.1)
            .lineLimit(1)
            .frame(height: geometry.size.height)
    }
}
Related