Text with custom font truncated - ios

I use the custom font "Zapfino" in my app, but the texts sometimes get truncated as shown in the picture below. Usually the text frame (colored red below) automatically expands with the contained text - but this does not seem to work for (some) custom fonts:
Text(name)
.font(Font.custom("Zapfino", size: 48, relativeTo: .title))
.foregroundColor(Color.white)
.baselineOffset(48)
.padding(10)
.background(Color.red)
Any ideas how to fix this? I tried to set the frame size explicitly, but it did not work.

I managed to adjust the SwiftUI related code from stackoverflow.com/a/64565207/6257435 to my needs. In order to enable automatic truncation of the text I defined a maximum intrinsic size that is considered accordingly:
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
size.width = min(size.width + clipExtension.width, maxIntrinsicContentSize.width)
size.height = min(size.height + clipExtension.height, maxIntrinsicContentSize.height)
return size
}
Otherwise the intrinsic size always extended to the text length and thus the text was never truncated.

Related

Keeping texts of different sizes aligned at top as the dynamic type size increases

I'm trying to align texts of different sizes at the top so that they stay aligned when the Dynamic Type size changes.
import SwiftUI
struct ContentView: View {
var body: some View {
HStack(alignment: .center, spacing: 0) {
Text("$")
.font(Font.custom("Helvetica", size: 24, relativeTo: .footnote))
.baselineOffset(7)
Text("123")
.font(Font.custom("Helvetica", size: 36, relativeTo: .footnote))
Text("45")
.font(Font.custom("Helvetica", size: 24, relativeTo: .footnote))
.baselineOffset(7)
}
}
}
This is aligned at top at
Regular Size
But it loses the top alignment as the Dynamic Type gets bigger: Dynamic Type Size ExtraExtraExtraLarge
Any ideas on how to keep them aligned at top even as the Dynamic Type size changes?
Any ideas on how to keep them aligned at top even as the Dynamic Type size changes?
I never needed this kind of configuration but your question arose my curiosity. 🤔
(I keep the same context you provided: 3 Text elements with the same text styles/font and two of them having the same font size)
How can I align two Text elements on top with different font sizes? 😵‍💫
I took a look at the font metrics to understand the returned values.
If these elements are aligned on top in a HStack view, it's the top edge that's concerned, not the letters nor the numbers inside.
So, I have to move vertically the biggest one to make its top content aligned to the smallest one.
To reach this goal, I used the alignmentGuide method that returns a view modified with respect to its horizontal alignment according to the computation performed in the method's closure by using the ascender and capHeight metrics difference.
How do I take into account the Dynamic Type feature in this implementation? 😰
The initial font sizes can be adapted to the user's preferences by using the dynamic property wrapper #ScaledMetrics that scales a numeric value.👍
I implement this information with Xcode 13.4 and iOS 15:🤓
import SwiftUI
import Foundation
struct ContentView: View {
#ScaledMetric(relativeTo: .footnote) var sizeMax: CGFloat=60
#ScaledMetric(relativeTo: .footnote) var sizeMin: CGFloat=24
var body: some View {
HStack(alignment: .top){
Text("$1&")
.font(Font.custom("Helvetica", size: 24,
relativeTo: .footnote))
.background(Color.blue)
Text("a2A")
.font(Font.custom("Helvetica", size: 60,
relativeTo: .footnote))
.alignmentGuide(VerticalAlignment.top){ d in
let customFontDescriptor = UIFontDescriptor.init(fontAttributes: [
UIFontDescriptor.AttributeName.family: "Helvetica",
UIFontDescriptor.AttributeName.textStyle:"footnote"
])
// Values for the font with min size.
let fontMin=UIFont(descriptor: customFontDescriptor,
size: sizeMin)
let ascMin = fontMin.ascender
let capHeightMin = fontMin.capHeight
// Values for the font with max size.
let fontMax=UIFont(descriptor: customFontDescriptor,
size: sizeMax)
let ascMax = fontMax.ascender
let capHeightMax = fontMax.capHeight
return abs((ascMax-capHeightMax)-(ascMin-capHeightMin))
}
.background(Color.gray)
Text("b3B")
.font(Font.custom("Helvetica", size: 24,
relativeTo: .footnote))
.background(Color.red)
}
}
}
Following this rationale, I get the following result to keep texts of different sizes aligned at top as the dynamic type size changes. 🎉🥳🎊
Don't hesitate to adapt this code snippet that fits the example you provided and that's definitely not generic. 😉

How to make text size relative to view in IOS?

In the XCode storyboard, I have set up my ViewController as a bunch of stackviews and everything is relative -- view dimensions are expressed as fractions of other view dimensions... lots of constraints, etc.
This is to make sure it looks decent on all IOS devices (phones and Ipads, anyway).
It does look acceptable in different aspect ratios, but I've noticed that the font size of my UILabels and TextViews are NOT changing -- not getting LARGER along with their containing views.
So, for example, if I switch from an iPhone to an iPad preview, a UILabel size may increase drastically and yet the text that it contains stays the same... so it's tiny text in a big box.
SO... the question is:
Is there a way to express font/text sizes as relative to the view that contains the text?
Something like this:
text.height = 0.7 * container.height
text.width = maintain aspect ratio with height
Thanks.
The same problem i had when i was designing an application for both iPhone and iPad and i tried this solution which is working fine but took a little efforts to manage. You need to create a custom label class which will inherits from UILabel class. There in your awakeFromNib function you can check the device and you can multiply whatever the font size with a ratio you feel ok for iPhone and iPad. You can also add checking for different iPhone sizes. If you wish to use different ratios for different label, make a IBDesignable property named dynamicRatio in your custom class and take that value to increase font. You can play around this. The effort is assigning the class to all your labels and setting properties which i use to do parallel during designing.
Below are the set of code which am using.
import UIKit
class MyLabel: UILabel {
#IBInspectable var autoFont: Bool = false
#IBInspectable var fontSize: CGFloat = 0
override open func awakeFromNib() {
super.awakeFromNib()
let baseHeight: CGFloat = (((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad)) ?1024.0:568.0)
if autoFont == true {
if (isDevice() == DEVICES.iPhoneX) {
let size: CGFloat = 667.0 * (fontSize / baseHeight)
self.font = UIFont(name: self.font!.fontName, size: size)
} else {
let size: CGFloat = UIScreen.main.bounds.size.height * (fontSize / baseHeight)
self.font = UIFont(name: self.font!.fontName, size: size)
}
}
}
}
Hope this idea helps.
Increase text font size of UILabels or UITextFields as you can, then set MinimumFontScale or MinimumFontSize attribute for them in "Attributes Inspector" tab, now font size increases as the UITextFields or UILabels size increases.
UILabel
UITextField

Get the content height of a UITextView

I use this open source library called ReadMoreTextView to display a bunch of text. The library enables me to toggle between displaying an excerpt of the text and showing the full length content (the button is a normal UIButton added by me, not the library).
This works as expected.
My problem arises when I have to display a smaller amount of text. If I add some content that doesn't exceed the height of the UITextView, I want to hide the more/less toggle button. So I thought of taking the full content height and if only it's larger than the text view's height, show the toggle button.
In the above example, I aded a long couple of paragraphs that exceeds the text view bounds. The text view's height comes up as 128. But the content height also returns 128. There's a library specific method called boundingRectForCharacterRange which is supposed to return the content height also returns a wrong value (100).
print("TEXTVIEW HEIGHT: \(textView.bounds.height)") // 128
print("CONTENT HEIGHT: \(textView.contentSize.height)") // 128
let rect = textView.layoutManager.boundingRectForCharacterRange(range: NSRange(location: 0, length: textView.text.count), inTextContainer: textView.textContainer)
print("TEXT HEIGHT: \(rect.height)") // 100
I opened an issue at the library's Github page but the owner asked to ask it here.
Why does the content height return a wrong value?
Here is the project I'm using in the above example by the way.
You can simply use following method to get the content size:
let contentSize = self.textView.sizeThatFits(self.textView.bounds.size)
Then update the textview frame accordingly:
self.textView.frame = CGRect(width: contentSize.width, height: contentSize.height)

Get the intrinsic height of a custom control

How can I get the height of my custom control?
The idea is I will use it to dynamically set the height of some buttons inside the custom control. I've set the Placeholder height to 44 in the Xcode size inspector.
Working off Apple's Start Developing iOS Apps (Swift) tutorial, I am attempting to access frame.size.height and it gives a value of 1000 while the tutorial seems to suggest it should be 44.
class RatingControl: UIView {
...
override public var intrinsicContentSize: CGSize {
let buttonSize = Int(frame.size.height)
print(buttonSize) // prints 1000
let width = (buttonSize * starCount) + (spacing * (starCount - 1))
return CGSize(width: width, height: buttonSize)
}
...
You should never access frame inside intrinsicContentSize. intrinsicContentSize should return the size that perfectly fits the contents of the view, regardless of its current frame.
In your case, I think you can just use 44 for your buttonSize.
The placeholder intrinsic size is just that, placeholder, so that IB interpreter is has some value to work with and can layout the rest of the scene. But in your intrinsicContentSize getter, you implement the real size, which will be used in runtime by the AutoLayout engine. Since you return 1000 as the intrinsic content height, that's what you will see in runtime.

elipsis dots after UITextfield for secured text entry

I have multiple uitextfields setup and they are all connected with IBOutlets. I have one textfield that is a password and I have the 'Secure text Entry' selected. when I have this check I get this
Any ideas why this happens? If i deselect the secured entry the textfield rises fine depending on the size of the password with no ellipsis dots.
It does not matter how long the password is. Same thing.
If i don't have the security text selected it works fine
Any idea why? It can be a width issue because it does autosize. But why does the 'secure text entry' cause the issue?
I have faced the same problem. I think it is a bug of UITextfield. It calculates size for the text but not for the secure text(dots). I have the problem when the text includes slim characters like 1, l etc...
As workaround I have subclassed the UITextfield class and overridden intrinsicContentSize function. You might need to adjust letter spacing. I couldn't find how to get it dynamically depending on font.
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
if !self.isSecureTextEntry {
return size
}
var width = size.width
if let font = self.font,
let charCount = self.text?.count {
width = "•".size(withAttributes: [NSAttributedString.Key.font : font]).width * CGFloat(charCount)
width += (CGFloat(charCount)+1) * 4.5 // this magic number is for letter spacing
}
return CGSize(width: width, height: size.height)
}

Resources