elipsis dots after UITextfield for secured text entry - ios

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)
}

Related

How to scale the button text based on screen design swift?

In my project, i am using scaling for UI components. I am able to scale the text for UIlabel like below and it's working in all device:
1. Autoshrinks - minimum font scale set it to 0.5
2. No of lines - 0
3. Enable dynamic type in attribute inspector
4. adjustFontSizeToWidth to true
But when i am trying to adjust font for UI Button using beolow steps and i am not able to scale the text for UI button.
button.titleLabel?.numberOfLines = 1 // Tried with 0 also
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.lineBreakMode = // tried differenet linebreakmode
Could anyone have an idea of scaling UI Button text?
Are you sure it's not working?
Edit - After comments...
UIKit elements such as UILabel / UIButton / etc to not have a built-in "auto-adjust font height" property.
I don't work for Apple, so just guessing that is (at least in part) due to the fact that, in general...
Based on screen height, the UI is designed to either:
provide more or less information, e.g. more rows in a table, or
adjust vertical spacing between elements
That doesn't mean you can't or shouldn't adjust your font sizes... it just means you have to do it manually.
Couple options:
set the font-size at run-time, as suggested by Duncan
use a UIAppearance proxy to set the font-size, again at run-time
in either case, you could use a height-to-fontSize table or a "percentage" calculation.
Another option would be a custom class that sets the font-size based on the constrained button height.
Here's a simple example (note: for demonstration purposes only):
class AutoFontSizeButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
guard let fnt = titleLabel?.font else { return }
// default system type button has 6-pts top / bottom inset
// and font size is 15/18ths of that height
let h = ((bounds.height - 12.0) * (15.0 / 18.0)).rounded()
let fs = fnt.pointSize
if h != fs {
titleLabel?.font = UIFont(descriptor: fnt.fontDescriptor, size: h)
}
}
}
Result - the top three (yellow) buttons are 30, 40 and 50-points in height, with the default font-size of 15. The bottom three (green) buttons are again 30, 40 and 50-points in height, but the font-size is automatically set at run-time:
I don't think there is a way to get the font to auto-size. However, if you set the button's titleLabel.font to a specific font size the button will update to use the new font size, including resizing the button.
Use code like this:
let size: CGFloat = useLargeFont ? 50.0 : 17.0 //Change as needed
if let buttonFont = button.titleLabel?.font {
button.titleLabel?.font = buttonFont.withSize(size)
}

iOS 13: How can I tweak the leading / descend / line height of a custom font in UIKit / SwiftUI

I'm using a custom font and somehow the rendering screws up the line height, potentially because of misconfigured descent or leading (?), so that g's and j's are cut off in the last line of the rendered text. I think it might be a problem with this particular font, because Sketch is also exposing similar issues with the font in question, but I feel like I don't understand quite enough about typographic measurements or fonts. I found this Apple documentation page on Typographic Concepts quite insightful.
I looked into the font itself with the test version of FontLab, which I have used for the first time btw - so I've little clue really what I'm looking at. It does seem like the g is going below the configured descent, which seems to be what the last line is. (?) (See: Character view in FontLab, showing the descend of the g)
Via lineSpacing I could adjust the distance between just the lines itself to fix this in the first few lines. I know iOS 14 is going to bring a way to modify the leading of a Text in SwiftUI. But I need to target iOS 13, so that doesn't help.
I've also tried SwiftUI's Text, a normal UILabel.text and UILabel.attributedText with a customized paragraph style, but nothing I adjust there seems to mitigate the problem.
The view is not even clipping. Just adding padding to the frame does not help at all. It increases the distance, but the g's and j's are still cut.
What can I do? Subclass UILabel and overwrite the intrinsicContentSize to add some extra space, when there is a g and j in the last line? That feels a) dirty and b) given that padding didn't help, it might not fix the problem?
Is the font itself the problem here? Can I patch the font somehow without making it worse?
Is there any way to modify the leading or the descend height of the font, when I use lower level APIs? Seems like I could go down to CoreText, as CTFontCreateCopyWithAttributes(_:_:_:_:) is a candidate, if I could just modify via attributes the leading, line space or the descend? Can or monkey-patch / swizzle things without shooting myself in the knee? Should I just file a radar a feedback?
You need to use NSAttributedString instead of String to control the line spacing of UILabel. Here is sample code
let style = NSMutableParagraphStyle()
style.lineSpacing = 20
let string = NSMutableAttributedString(string: "The quick brown fox jumps over the lazy dog, The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog")
string.addAttribute(.paragraphStyle, value: style, range: NSMakeRange(0, string.length))
let label = UILabel(frame: CGRect(x: 20, y: 100, width: 300, height: 500))
label.attributedText = string
label.numberOfLines = 0
self.view.addSubview(label)
Out put
I know what you are asking as I have faced the same issues with custom fonts. I am going to offer two solutions. In my own project I went the way of your suggestion in overriding intrinsicContentSize and adding a padding multiplier for height and width. In my case the fonts were user facing so I had a struct that held all the relevant information. FYI Chalkduster is in the system and clips. I also believe that this is all due to the font file itself.
Solution 1:
Example:
struct UserFont{
var name : String
var displayName : String
var widthMultiplier : CGFloat
var heightMultiplier : CGFloat
}
Then in my UILabel I have it subclassed to use both of these metrics
#IBDesignable
class MultiplierUILabel: UILabel {
#IBInspectable var widthPaddingMultiplier : CGFloat = 1
#IBInspectable var heightPaddingMultiplier : CGFloat = 1
override var intrinsicContentSize: CGSize{
return CGSize(width: super.intrinsicContentSize.width * widthPaddingMultiplier, height: super.intrinsicContentSize.height * heightPaddingMultiplier)
}
}
This to me was the simplest implementation as I found the font and multiplier scale accordingly.
Solution 2:
You might be able to get the draw to occur slightly higher by measuring the glyph bounds and adjusting the origin y. For example this fixes the clipping on Chalkduster font that is included in the system.
#IBDesignable
class PaddingUILabel: UILabel {
override func drawText(in rect:CGRect) {
//hello
guard let labelText = text else { return super.drawText(in: rect) }
//just some breathing room
let info = boundsForAttrString(str: labelText, font: self.font!, kern: .leastNormalMagnitude)
let glyph = info.glyph
var newRect = rect
let glyphPadding = -(glyph.origin.y)
if glyphPadding - info.descent > 1 && info.descent != 0{
newRect.origin.y -= glyphPadding/2
}else{
if info.descent != 0{
newRect.origin.y += (info.descent - glyphPadding)/2
}
}
super.drawText(in: newRect)
}
func boundsForAttrString(str:String,font:UIFont,kern:CGFloat)->(glyph:CGRect,descent:CGFloat){
let attr = NSAttributedString(string: str, attributes: [.font:font,.kern:kern])
let line = CTLineCreateWithAttributedString(attr)
var ascent : CGFloat = 0
var descent : CGFloat = 0
var leading : CGFloat = 0
CTLineGetTypographicBounds(line, &ascent, &descent, &leading)
let glyph = CTLineGetBoundsWithOptions(line, .useGlyphPathBounds).integral
return (glyph,leading != 0 ? descent : 0)
}
}
Result of Solution 2:
System
PaddingUILabel using glyph bounds

Word Wrap Occurs Inconsistently in UILabel

I have a UILabel that is designed to expand in height when the width of the text's CGSize is greater than the width of the label. I accomplish that with this code:
func viewHeight(_ locationName: String) -> CGFloat {
let locationName = tappedLocation[0].name
var size = CGSize()
if let font = UIFont(name: ".SFUIText", size: 17.0) {
let fontAttributes = [NSAttributedStringKey.font: font]
size = (locationName as NSString).size(withAttributes: fontAttributes)
}
let normalCellHeight = horizontalStackViewHeightConstraint.constant
let extraLargeCellHeight = horizontalStackViewHeightConstraint.constant + 20.33
let textWidth = ceil(size.width)
let cellWidth = ceil(nameLabel.frame.width)
if textWidth > cellWidth {
return extraLargeCellHeight
} else {
return normalCellHeight
}
}
Lines = 0 and line break style = Word Wrap:
The label lives inside a vertical stackView, and is constrained to its top, leading and trailing edges and a stackView beneath it. The height of the label and the UIView properly expand in height when the CGSize width of the text is longer than the width of the label. All well and good.
However, the words do not wrap consistently. This behavior is intentional:
Bobby Mao's Chinese Kitchen & Bar:
XL cell. Width: 184.0,
Text width: 287.0
This behavior is not (why isn't "steak" on the prior line?):
Ruth's Chris Steak House:
XL cell. Width: 184.0,
Text width: 204.0
And neither is this (why didn't Gina wrap if it's over the label width parameter?):
Ristorante Mamma Gina:
XL cell. Width: 184.0,
Text width: 191.0
I have also set a temporary background color on my label to ensure that it does, in fact correspond to the intended width. The label in this example creates another line when the label's width is exceeded, but the text does not wrap:
I have read the other entries on Stack Overflow about word wrapping. I don't believe this is a duplicate. I do not have trouble creating two lines for my text. I don't have trouble with word wrapping occurring. I have trouble with how and when it is occurring.
I think the intent is clear... what am I missing?

how much pixels dose a character takes in iOS?

I'm trying to automatically layout text on a UILabel view.
The text (such as "abcdefghij") contains ten characters. I want to display it in one single line.
I turned off the Size Class and Auto Layout for convenience, and added following codes to layout the text on the UILabel. It should be ten characters in one line, and the width of the UILabel is equal to the width of the device.
let screenWidth = UIScreen.mainScreen().bounds.width
labelView.frame = CGRect(x: labelView.frame.origin.x, y: labelView.frame.origin.y, width: screenWidth, height: labelView.frame.height)
let string = "abcdefghij"
let stringLength = CGFloat(string.characters.count)
let characterSize = keyboardView.font.pointSize
let characterSpacing = (screenWidth - characterSize * stringLength) / stringLength
let content = NSAttributedString(string: string, attributes: [NSKernAttributeName: characterSpacing])
keyboardView.attributedText = content
But it turns out like this. The width of string is not equal to the screen
I think, the only could be wrong here is the pointSize. It equals to 13.8 while I set the font size to 17.
I don't understand it.
Give me some hints, please.
Thanks for your attention. 😄
By using sizeWithAttributes and boundingRectWithSize(_:options:context:), I finally figured out how it works. But my origin purpose is fitting the 10 characters in one line. The code should calculate the space between the characters, and all the space is same size. Could you give me some advices?
This is what I want to make
Each character occupies different amount of space depending on the character, font and size of the font.
Hence, you can use boundingRectWithSize(_:options:context:) to predict size of the string at runtime, and then take action according to your requirements.

Disable "..." truncating for a UITextField

I've been reviewing the documentation for UITextField and its options are much more limited than the UITextView.
I've run into a situation where I need to handle truncating the text by forcing the bounding box to be bigger so no truncation exists.
I cannot just use .adjustsFontSizeToFitWidth because this event is only allowed when the text box is at screen width. The truncation is happening when it's not full screen length.
Right now when the user types, I log each keystroke and make sure the UITextFields box expands to fit the text. However if I use a large font, the text is getting cut off still:
"THIS IS LARGE TEXT GETTING CUT O..."
Currently I log each keystroke and run this code to size it:
func adjustFrameWidthToFitText()
{
var size = sizeThatFits(CGSizeMake(CGFloat.max,height))
frame = CGRectMake(frame.origin.x, frame.origin.y, size.width + 7, frame.height)
}
However I still get the ... cut off in some places. Is there anyway to tell if the text is being truncated and override the behaviour causing said truncation?
(Solutions in Swift & Obj-c welcome!)
Based on the answer below I tried:
func adjustFrameWidthToFitText()
{
var fontSize = font.pointSize
var atr = [NSFontAttributeName:font]
var textSize = NSString(string: text).sizeWithAttributes(atr)
frame = CGRectMake(frame.origin.x, frame.origin.y, textSize.width, frame.height)
}
But there is still truncation
extension String {
func sizeWithAttributes(atr: NSDictionary) -> CGSize {
return NSString(string: self).sizeWithAttributes(atr)
}
}
let size = textView.text.sizeWithAttributes([NSFontAttribute:textView.font])
This will return the exact size for the string including '\n' characters.
The you can use the size however you want.
ADDED
Also when I was using CATextLayer I had to add this to the attributes to get the rows, havent tested on UITextView or field though:
let style = NSMutableParagraphStyle()
style.lineHeightMultiple = 1.05
..., NSParagraphStyleAttributeName:style])
After much digging I found the root cause, and a shortcut.
Shortcut first:
func adjustFrameWidthToFitText()
{
var size = intrinsicContentSize()
frame = CGRectMake(frame.origin.x, frame.origin.y, size.width, frame.height)
}
This gives me the size of the textfield.
The problem with my code was I was calculating the resize event BEFORE the character was added to the text. So my bounds were always being calculated before the latest keystroke was added to the string.
When I changed my logic
public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
adjustFrameWidthToFitText()
return true
}
To
textFieldDidEndEditing(_:)
{
adjustFrameWidthToFitText()
}
It works.
Voila!

Resources