I need to calculate the upper bound for the maximum font size, at which the text with the given font fits on one line.
In order to calculate the upper bound for the font size picker, I need to calculate the width of the Text. And I am stuck here.
How can I calculate the width of the Text? Please help.
If there’s another solution to my problem, I would this appreciate as well.
Here’s my code:
extension Text {
var width: CGFloat {
return 0 // MARK: Calculate Text width somehow?
}
}
struct EditView: View {
@State private var string: String = "Hello, world!"
@State private var fontSize: CGFloat = 20
@State private var fontWeight: Font.Weight = .regular
@State private var fontWidth: Font.Width = .standard
@State private var fontDesign: Font.Design = .default
let squareWidth: CGFloat = 150
var font: Font {
Font.system(size: fontSize, weight: fontWeight, design: fontDesign).width(fontWidth)
}
var fontSizeRange: ClosedRange<CGFloat> {
let upperBound = maximumFontSizeThatFitsOnOneLineFor(string: string, with: font, in: squareWidth)
return 10...upperBound
}
func maximumFontSizeThatFitsOnOneLineFor(string: String, with font: Font, in maximumTextWidth: CGFloat) -> CGFloat {
var fontSize: CGFloat = 100
var text = Text(string).font(font)
var textWidth: CGFloat = text.width
while textWidth > maximumTextWidth {
fontSize -= 1
}
return fontSize
}
var body: some View {
VStack {
HStack {
Spacer()
AnotherView(string: string, font: font)
.frame(maxWidth: squareWidth, maxHeight: squareWidth)
.clipShape(RoundedRectangle(cornerRadius: 20))
Spacer()
}
.padding()
Form {
TextField("Enter text", text: $string)
Stepper("Font Size: (fontSize.formatted())", value: $fontSize, in: fontSizeRange)
Picker("Weight", selection: $fontWeight) {
Text("Ultra Light").tag(Font.Weight.ultraLight)
Text("Regular").tag(Font.Weight.regular)
Text("Heavy").tag(Font.Weight.heavy)
}
Picker("Width", selection: $fontWidth) {
Text("Compressed").tag(Font.Width.compressed)
Text("Standard").tag(Font.Width.standard)
Text("Expanded").tag(Font.Width.expanded)
}
Picker("Design", selection: $fontDesign) {
Text("Default").tag(Font.Design.default)
Text("Monospaced").tag(Font.Design.monospaced)
Text("Serif").tag(Font.Design.serif)
}
}
}
}
}
struct AnotherView: View {
var string: String
var font: Font
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(string)
.lineLimit(1)
.font(font)
Spacer(minLength: 0)
Text("🌍")
.font(.largeTitle)
}
Spacer(minLength: 0)
}
.foregroundColor(.white)
.padding(16)
.background { Color.red }
}
}
struct EditView_Previews: PreviewProvider {
static var previews: some View {
EditView()
}
}
2
Answers
You could try something like this. Use a
GeometryReader
on a clear background of the text. It is a bit hacky but works:You can just slap on a large font size, then let Swift scale it down to the largest size that fits: