Scaling down a text's font size to fit its length to another text in SwiftUI
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
})
}
)
}
}
How to scale text to fit parent view with SwiftUI?
One can use GeometryReader
in order to make it also work in landscape mode.
It first checks if the width or the height is smaller and then adjusts the font size according to the smaller of these.
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))
}
}
SwiftUI scale text to fit the width and height
on UIKit
minimumScaleFactor
and minimumFontSize
are ineffective unless you set adjustsFontSizeToFitWidth
.
Instead of creating a VStack
, you may want to place all the figures, separated by newlines, in a single text, and fit the text to the bounding rectangle.
With SwiftUI
the solution looks like:
struct aView: View {
let array = [4, 5, 6]
var body: some View {
HStack(spacing: 100) {
Text(array.map {String($0)}.joined(separator: "\n"))
.font(.system(size: 1000))
.minimumScaleFactor(0.01)
.frame(width: 60, height: 300)
.border(Color.primary)
Text(array.map {String($0)}.joined(separator: "\n"))
.font(.system(size: 1000))
.minimumScaleFactor(0.01)
.frame(width: 30, height: 150)
.border(Color.primary)
}
}
}
SwiftUI: Dynamically change font size based on a string length
There is a simple answer, however, I have found that it only works sometimes.
Text("Hello World!")
.minimumScaleFactor(0.4)
This should do what you want, only scale down the text if it will not fit. It will only scale down the text the amount required to fit in the text's frame.
I have found though that on some devices, swiftUI will scale down the text to the minimum factor no matter what. This seems to depend on the app. In some of my apps, it only works in the simulator while in others it just works fine on any device.
Hope this helps.
Automatic adjust font size in Text() of Swiftui?
Use minimumScaleFactor(_:)
https://developer.apple.com/documentation/swiftui/text/minimumscalefactor(_:)
struct ContentView: View {
var body: some View {
VStack{
Text("Test string")
.minimumScaleFactor(0.1) //<--Here
.frame(width: 15, height: 15)
}
}
}
SwiftUI - Text minimumScaleFactor not scaling only when needed
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)
SwiftUi how to prevent text to automatically truncate in order to fit
That is a bit more involved. It takes a lot of interplay with PreferenceKeys
. This code could probably be shortened, but it works...
struct InlineTextView: View {
@State var userName = "longtext username test"
@State var userNamePref: CGFloat = 0
@State var additionalTextPref: CGFloat = 0
@State var hStackPref: CGFloat = 0
@State var totalWidth: CGFloat = 0
@State var isTooWide: Bool = true
var body: some View {
GeometryReader {outerGeometry in
VStack(alignment: .leading, spacing: 20) {
Group {
if isTooWide {
Text(userName)
.background(GeometryReader { userGeometry in
Color.clear.preference(
key: UsernameWidthPreferenceKey.self,
value: userGeometry.size.width)
})
additionalInlineText
.onTapGesture {
print("tapped")
}
.background(GeometryReader { addGeometry in
Color.clear.preference(
key: AdditionalWidthPreferenceKey.self,
value: addGeometry.size.width)
})
} else {
HStack {
Text(userName)
.background(GeometryReader { geometry in
Color.clear.preference(
key: UsernameWidthPreferenceKey.self,
value: geometry.size.width)
})
additionalInlineTextView
.background(GeometryReader { geometry in
Color.clear.preference(
key: AdditionalWidthPreferenceKey.self,
value: geometry.size.width)
})
}
}
}
.onPreferenceChange(UsernameWidthPreferenceKey.self) {
userNamePref = max($0, userNamePref)
totalWidth = userNamePref + additionalTextPref
isTooWide = totalWidth > outerGeometry.size.width
print("userNamePref = \(userNamePref)")
print("additionalTextPref = \(additionalTextPref)")
print("hStackPref = \(hStackPref)")
print("totalWidth = \(totalWidth)")
print("outerGeo = \(outerGeometry.size.width)")
}
.onPreferenceChange(AdditionalWidthPreferenceKey.self) {
additionalTextPref = max($0, additionalTextPref)
totalWidth = userNamePref + additionalTextPref
isTooWide = totalWidth > outerGeometry.size.width
print("userNamePref = \(userNamePref)")
print("additionalTextPref = \(additionalTextPref)")
print("hStackPref = \(hStackPref)")
print("totalWidth = \(totalWidth)")
print("outerGeo = \(outerGeometry.size.width)")
}
Button {
if userName == "longtext username test" {
userNamePref = 0
userName = "userName"
} else {
userNamePref = 0
userName = "longtext username test"
}
} label: {
Text("Change name.")
}
}
}
}
var additionalInlineText: Text {
// Block 1
Text(Image(systemName: "network")) +
Text("12345") +
// Block 2
Text(Image(systemName: "network")) +
Text("2.0K") +
// Block 3
Text(Image(systemName: "network")) +
Text("2H") +
// Block 4
Text(Image(systemName: "network")) +
Text(Image(systemName: "network")) +
Text(Image(systemName: "network")) +
Text("1234")
}
}
private extension InlineTextView {
struct UsernameWidthPreferenceKey: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat,
nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}
struct AdditionalWidthPreferenceKey: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat,
nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}
}
Edit: made additionalInlineText tappable and changed the name to make it clear that it was a text.
And yes, the GeometryReader
determines the width of the screen, and the preference keys determine the width of the views. I start them in the VStack
so they read their max width. If you notice when userName
is changed, I reset the userNamePref
to get a new reading. This is a bit fragile, but it is the only way to do what you want.
Related Topics
Cllocation Distancefromlocation (In Swift)
Best Way to Handle Errors from Async Closures in Swift 2
Multidimensional Dictionaries Possible in Swift
Alamofire 3 Custom Encoding to Alamofire 4 Custom Encoding
Swift Objc_Getassociatedobject Always Nil
How to Check Whether an Object Is Kind of a Dynamic Class Type in Swift
Swift: Hashable Struct with Dictionary Property
Implementing Reconnection with Urlsession Publisher and Combine
Scenekit Won't Scale a Dynamic Body
Safari App Extension Crashes After a Few Seconds for Hello World Project
Can Not Conform to Protocol by Creating Extension with Where Clauses
Swift; Delegate Embedded View Controller and Parent
Implicitlyunwrappedoptional in Init VS Later
Swift Create Byte Buffer Holder for Nsstream
Prevent Error "Funk" Sound in Event Monitor Os X