How to Get .Adjustsfontsizetofitwidth to Function Properly

UILabel: adjustsFontSizeToFitWidth doesn't work

UIKit needs a hint about how many lines of text you want, or it doesn't know how much to scale down. If you want the whole text to fit into the label on one line, you also need: nameLabel.numberOfLines = 1.

Working playground example:

import Foundation
import UIKit

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
label.numberOfLines = 1
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5
label.text = "Some very long text goes here"

adjustsFontSizeToFitWidth has different effect on UILabel than on UITextField

I think it's fair to say that .adjustsFontSizeToFitWidth = true only "mostly" works.

It is also primarily for use with string width.

Also, UILabel and UITextField do not use the same font rendering, and do not have the same bounding-box (text fields have an inset).

If you want both elements to have the same visual behaviors, your best bet is to use a UITextField with user interaction disabled instead of a UILabel.

Multiline UILabel with adjustsFontSizeToFitWidth

In this question, 0x90 provides a solution that - although a bit ugly - does what I want. Specifically, it deals correctly with the situation that a single word does not fit the width at the initial font size. I've slightly modified the code so that it works as a category on NSString:

- (CGFloat)fontSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
CGFloat fontSize = [font pointSize];
CGFloat height = [self sizeWithFont:font constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
UIFont *newFont = font;

//Reduce font size while too large, break if no height (empty string)
while (height > size.height && height != 0) {
fontSize--;
newFont = [UIFont fontWithName:font.fontName size:fontSize];
height = [self sizeWithFont:newFont constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
};

// Loop through words in string and resize to fit
for (NSString *word in [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
CGFloat width = [word sizeWithFont:newFont].width;
while (width > size.width && width != 0) {
fontSize--;
newFont = [UIFont fontWithName:font.fontName size:fontSize];
width = [word sizeWithFont:newFont].width;
}
}
return fontSize;
}

To use it with a UILabel:

    CGFloat fontSize = [label.text fontSizeWithFont:[UIFont boldSystemFontOfSize:15] constrainedToSize:label.frame.size];
label.font = [UIFont boldSystemFontOfSize:fontSize];

EDIT: Fixed the code to initialize newFont with font. Fixes a crash under certain circumstances.

Swift - Adjusting fontSize to fit the width of the layout (programmatically)

Try the following commands for your label:

label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.2

And try to change the lines of the label to 0 and 1 (check both cases):

label.numberOfLines = 0 // or 1

adjustsFontSizeToFitWidth in utilities pane in Xcode storyboard

Select your label in the storyboard, and set the drop down to Minimum Font Size. Then in the text field, enter the lowest size your label should drop to to try to fit the contents onscreen:

Sample Image

How to manage text size in segment control for different devices?

Below is one way I can think of doing it but by no means the only way.

First, I started by setting up a collection view with a horizontal flow layout.

// Flow layout configuration, mainly to figure out width of the cells
private func createLayout() -> UICollectionViewFlowLayout {

let flowLayout = UICollectionViewFlowLayout()
flowLayout.minimumLineSpacing = horizontalPadding
flowLayout.minimumInteritemSpacing = 0
flowLayout.scrollDirection = .horizontal

// Calculate the available width to divide the segments evenly
var availableWidth = UIScreen.main.bounds.width

// There will always be segments - 1 gaps, for 3 segments, there will be
// 2 gaps and for 4 segments there will be 3 gaps etc
availableWidth -= horizontalPadding * CGFloat(segments.count - 1)

let cellWidth = availableWidth / CGFloat(segments.count)

flowLayout.itemSize = CGSize(width: cellWidth,
height: collectionViewHeight)

return flowLayout
}

Once I do this, I run into the same problem, that depending on the width of the screen, my text could get cut off.

UICollectionView UISegmentController UICollectionViewCell custom swift iOS

So once the width of each segment is determined, we have to calculate the maximum font size to show the complete text for the longest segment and that font size should be applied to all

In this case, the long segment is Vibration Intensity in terms of string length.

// Flow layout configuration, mainly to figure out width of the cells
private func createLayout() -> UICollectionViewFlowLayout {

let flowLayout = UICollectionViewFlowLayout()
flowLayout.minimumLineSpacing = horizontalPadding
flowLayout.minimumInteritemSpacing = 0
flowLayout.scrollDirection = .horizontal
flowLayout.sectionInset = UIEdgeInsets(top: 0,
left: horizontalPadding,
bottom: 0,
right: horizontalPadding)

// Calculate the available width to divide the segments evenly
var availableWidth = UIScreen.main.bounds.width

// There will always be segments - 1 gaps, for 3 segments, there will be
// 2 gaps and for 4 segments there will be 3 gaps etc
availableWidth -= horizontalPadding * CGFloat(segments.count - 1)

// Remove the insets
availableWidth -= flowLayout.sectionInset.left + flowLayout.sectionInset.right

let cellWidth = availableWidth / CGFloat(segments.count)

// Add this function
calculateApproxFontSize(forWidth: cellWidth)

flowLayout.itemSize = CGSize(width: cellWidth,
height: collectionViewHeight)

return flowLayout
}

private func calculateApproxFontSize(forWidth width: CGFloat) {

// Get the longest segment by length
if let longestSegmentTitle = segments.max(by: { $1.count > $0.count }) {

let tempLabel = UILabel()
tempLabel.numberOfLines = 1
tempLabel.text = longestSegmentTitle
tempLabel.sizeToFit()

guard var currentFont = tempLabel.font else { return }

var intrinsicSize
= (longestSegmentTitle as NSString).size(withAttributes: [.font : currentFont])

// Keep looping and reduce the font size till the text
// fits into the label
// This could be optimized further using binary search
// However this should be ok for small strings
while intrinsicSize.width > width
{
currentFont = currentFont.withSize(currentFont.pointSize - 1)
tempLabel.font = currentFont

intrinsicSize
= (longestSegmentTitle as NSString).size(withAttributes: [.font : currentFont])
}

// Set the font of the current label
// segmentFontSize is a global var in the VC
segmentFontSize = currentFont.pointSize
}
}

Then set the segmentFontSize in cellForItemAt indexPath

func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

let cell = collectionView
.dequeueReusableCell(withReuseIdentifier: SegmentCell.reuseIdentifier,
for: indexPath) as! SegmentCell

cell.backgroundColor = .orange
cell.title.text = segments[indexPath.item]

// Adjust the font
cell.title.font = cell.title.font.withSize(segmentFontSize)

return cell
}

This will give you something like this:

Resize UILabel to fit frame rect in swift iOS using UICollectionView as UISegmentView

If you found some part difficult to follow, here is the link to the complete code: https://gist.github.com/shawn-frank/03bc06d13f90a54e23e9ea8c6f30a70e



Related Topics



Leave a reply



Submit