J'ai vu de nombreux exemples de modification de la taille d'un UILabel.
Voici ce que je voudrais faire: Changer la taille de la police pour que le texte soit aussi grand que possible dans la nouvelle hauteur.
Des indices?
Edit: Découvrez La réponse géniale de Joel Fischer pour obtenir par programme la taille correcte!
Vous pouvez définir la police pour qu'elle remplisse automatiquement la taille d'une étiquette et ne descende pas éventuellement sous une taille de police minimale. Définissez simplement adjustsFontSizeToFitWidth
sur YES.
Consultez le Référence de la classe UILabel si vous avez besoin de plus d'informations.
Bien que le booléen soit appelé "adjustsFontSizeToFitWidth", il s'agit en réalité de la taille la plus grande pour la hauteur de l'étiquette, qui restera sur une ligne de l'étiquette (ou le nombre de lignes que vous spécifiez).
J'ai eu le même problème et, grâce à ce fil et à l'algorithme de Joel, j'ai pu le réparer. :-)
Ci-dessous, mon code dans Swift. Je suis dans iOS 8 + Autolayout.
Problème:
Après le correctif:
Quel est exactement ce que le designer avait à l'esprit ... :)
J'ai sous-classé UILabel et surchargé layoutSubviews
. Ensuite, chaque fois que la taille de UILabel change, la taille de la police est recalculée:
//
// LabelWithAdaptiveTextHeight.Swift
// 123
//
// Created by https://github.com/backslash-f on 12/19/14.
//
/*
Designed with single-line UILabels in mind, this subclass 'resizes' the label's text (it changes the label's font size)
everytime its size (frame) is changed. This 'fits' the text to the new height, avoiding undesired text cropping.
Kudos to this Stack Overflow thread: bit.ly/setFontSizeToFillUILabelHeight
*/
import Foundation
import UIKit
class LabelWithAdaptiveTextHeight: UILabel {
override func layoutSubviews() {
super.layoutSubviews()
font = fontToFitHeight()
}
// Returns an UIFont that fits the new label's height.
private func fontToFitHeight() -> UIFont {
var minFontSize: CGFloat = DISPLAY_FONT_MINIMUM // CGFloat 18
var maxFontSize: CGFloat = DISPLAY_FONT_BIG // CGFloat 67
var fontSizeAverage: CGFloat = 0
var textAndLabelHeightDiff: CGFloat = 0
while (minFontSize <= maxFontSize) {
fontSizeAverage = minFontSize + (maxFontSize - minFontSize) / 2
// Abort if text happens to be nil
guard text?.characters.count > 0 else {
break
}
if let labelText: NSString = text {
let labelHeight = frame.size.height
let testStringHeight = labelText.sizeWithAttributes(
[NSFontAttributeName: font.fontWithSize(fontSizeAverage)]
).height
textAndLabelHeightDiff = labelHeight - testStringHeight
if (fontSizeAverage == minFontSize || fontSizeAverage == maxFontSize) {
if (textAndLabelHeightDiff < 0) {
return font.fontWithSize(fontSizeAverage - 1)
}
return font.fontWithSize(fontSizeAverage)
}
if (textAndLabelHeightDiff < 0) {
maxFontSize = fontSizeAverage - 1
} else if (textAndLabelHeightDiff > 0) {
minFontSize = fontSizeAverage + 1
} else {
return font.fontWithSize(fontSizeAverage)
}
}
}
return font.fontWithSize(fontSizeAverage)
}
}
Voici comment je l'ai fait, puisque la réponse de DGund n'a pas fonctionné pour moi, elle correspond à la largeur, mais je voulais qu'elle s'adapte à la hauteur.
+ (UIFont *)findAdaptiveFontWithName:(NSString *)fontName forUILabelSize:(CGSize)labelSize withMinimumSize:(NSInteger)minSize
{
UIFont *tempFont = nil;
NSString *testString = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSInteger tempMin = minSize;
NSInteger tempMax = 256;
NSInteger mid = 0;
NSInteger difference = 0;
while (tempMin <= tempMax) {
mid = tempMin + (tempMax - tempMin) / 2;
tempFont = [UIFont fontWithName:fontName size:mid];
difference = labelSize.height - [testString sizeWithFont:tempFont].height;
if (mid == tempMin || mid == tempMax) {
if (difference < 0) {
return [UIFont fontWithName:fontName size:(mid - 1)];
}
return [UIFont fontWithName:fontName size:mid];
}
if (difference < 0) {
tempMax = mid - 1;
} else if (difference > 0) {
tempMin = mid + 1;
} else {
return [UIFont fontWithName:fontName size:mid];
}
}
return [UIFont fontWithName:fontName size:mid];
}
Cela prendra un nom de police, une taille (il ne doit pas nécessairement s'agir d'un UILabel, mais je l'ai toujours utilisé avec un UILabel), ainsi qu'une taille minimale (vous pouvez également utiliser une taille maximale, remplacez simplement le 256 par un le paramètre de taille maximale). Cela testera essentiellement chaque taille de police entre les tailles de police minimale et maximale et renverra celle qui se trouve à la hauteur cible ou juste en dessous de celle-ci.
L'utilisation est explicite, mais ressemble à ceci:
self.myLabel.font = [self findAdaptiveFontWithName:@"HelveticaNeue-UltraLight" forUILabelSize:self.myLabel.frame.size withMinimumSize:30];
Vous pouvez également en faire une catégorie de méthode de classe sur UIFont (ce que j'ai fait).
EDIT: Sur suggestion, j'ai retiré la boucle for et ai passé un peu de temps à la rendre plus efficace avec une routine de recherche binaire. J'ai fait plusieurs vérifications pour être absolument sûr que la police de caractères correspondrait bien à l'étiquette. Lors des tests initiaux, cela semble fonctionner.
Il y a une solution plus simple. Ajoutez simplement les lignes ci-dessous et, comme par magie, l’étiquette ajuste la taille de la police à la hauteur de l’étiquette:
Swift 3:
label.minimumScaleFactor = 0.1 //or whatever suits your need
label.adjustsFontSizeToFitWidth = true
label.lineBreakMode = .byClipping
label.numberOfLines = 0
adapter le texte en fonction de la hauteur de mon étiquette, j’ai adapté la méthode Joel à Swift
func optimisedfindAdaptiveFontWithName(fontName:String, label:UILabel!, minSize:CGFloat,maxSize:CGFloat) -> UIFont!
{
var tempFont:UIFont
var tempHeight:CGFloat
var tempMax:CGFloat = maxSize
var tempMin:CGFloat = minSize
while (ceil(tempMin) != ceil(tempMax)){
let testedSize = (tempMax + tempMin) / 2
tempFont = UIFont(name:fontName, size:testedSize)
let attributedString = NSAttributedString(string: label.text!, attributes: [NSFontAttributeName : tempFont])
let textFrame = attributedString.boundingRectWithSize(CGSize(width: label.bounds.size.width, height: CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin , context: nil)
let difference = label.frame.height - textFrame.height
println("\(tempMin)-\(tempMax) - tested : \(testedSize) --> difference : \(difference)")
if(difference > 0){
tempMin = testedSize
}else{
tempMax = testedSize
}
}
//returning the size -1 (to have enought space right and left)
return UIFont(name: fontName, size: tempMin - 1)
}
et je l'utilise de cette façon:
myLabel.font = optimisedfindAdaptiveFontWithName("Helvetica", label: myLabel, minSize: 10, maxSize: 38)
println("\(myLabel.font)")
Bonnes nouvelles,
Vous avez seulement besoin d'itérer (plusieurs fois) à l'aide d'une recherche par ratio.
guess = guess * ( desiredHeight / guessHeight )
Voici une solution complète IBDesignable
complète.
Remarque: lorsque vous travaillez avec des concepteurs ou des typographes, vous devez définir le suivi/l’étirement des polices. (C’est absurde, n’incluez pas cela.) StyledLabel
inclut également suivi/étirement.
// the call fontToFitHeight FINDS THE POINT SIZE TO "FILL TO HEIGHT".
// Just use autolayout to make the frame THE ACTUAL HEIGHT
// you want the type ON ANY DEVICE
// ADDITIONALLY you can set:
// the tracking (that's the overall amount of space between all letters)
// and streching (actually squeeze or stretch the letters horizontally)
// Note: tracking and stretching IS SHOWN IN STORYBOARD LIVE
// WTT crazyrems http://stackoverflow.com/a/37300130/294884
import UIKit
@IBDesignable
class StyledLabel: UILabel
{
@IBInspectable var tracking:CGFloat = 0.8
// values between about 0.7 to 1.3. one means normal.
@IBInspectable var stretching:CGFloat = -0.1
// values between about -.5 to .5. zero means normal.
override func awakeFromNib()
{
Tweak()
}
override func prepareForInterfaceBuilder()
{
Tweak()
}
override func layoutSubviews()
{
super.layoutSubviews()
font = fontToFitHeight()
}
private func fontToFitHeight() -> UIFont
{
/* Apple have failed to include a basic thing needed in handling text: fitting the text to the height. Here's the simplest and fastest way to do that:
guess = guess * ( desiredHeight / guessHeight )
That's really all there is to it. The rest of the code in this routine is safeguards. Further, the routine iterates a couple of times, which is harmless, to take care of any theoretical bizarre nonlinear sizing issues with strange typefaces. */
guard text?.characters.count > 0 else { return font }
let desiredHeight:CGFloat = frame.size.height
guard desiredHeight>1 else { return font }
var guess:CGFloat
var guessHeight:CGFloat
print("searching for... ", desiredHeight)
guess = font.pointSize
if (guess>1&&guess<1000) { guess = 50 }
guessHeight = sizeIf(guess)
if (guessHeight==desiredHeight)
{
print("fluke, exact match within float math limits, up front")
return font.fontWithSize(guess)
}
var iterations:Int = 4
/* It is incredibly unlikely you would need more than four iterations, "two" would rarely be needed. You could imagine some very strange glyph handling where the relationship is non-linear (or something weird): That is the only theoretical reason you'd ever need more than one or two iterations. Note that when you watch the output of the iterations, you'll sometimes/often see same or identical values for the result: this is correct and expected in a float iteration. */
while(iterations>0)
{
guess = guess * ( desiredHeight / guessHeight )
guessHeight = sizeIf(guess)
if (guessHeight==desiredHeight)
{
print("unbelievable fluke, exact match within float math limits while iterating")
return font.fontWithSize(guess)
}
iterations -= 1
}
print("done. Shame Apple doesn't do this for us!")
return font.fontWithSize(guess)
}
private func sizeIf(pointSizeToTry:CGFloat)->(CGFloat)
{
let s:CGFloat = text!.sizeWithAttributes(
[NSFontAttributeName: font.fontWithSize(pointSizeToTry)] )
.height
print("guessing .. ", pointSizeToTry, " .. " , s)
return s
}
private func Tweak()
{
let ats = NSMutableAttributedString(string: self.text!)
let rg = NSRange(location: 0, length: self.text!.characters.count)
ats.addAttribute(
NSKernAttributeName, value:CGFloat(tracking), range:rg )
ats.addAttribute(
NSExpansionAttributeName, value:CGFloat(stretching), range:rg )
self.attributedText = ats
}
}
Une ligne appelée dans viewWillAppear fait le tour:
testLabel.font = testLabel.font.fontWithSize(testLabel.frame.height * 2/3)
Dans le storyboard, je règle toutes les hauteurs d'étiquette par rapport à la hauteur totale de la vue, ce qui permet à la taille de la police de s'adapter de manière dynamique à leur taille.
Notez que la taille de la police est en réalité 2/3 de la hauteur de l'étiquette. Si la police que vous utilisez a des queues qui descendent au-dessous de la ligne (comme dans y, g, q, p ou j), vous voudrez que la taille de la police corresponde à la hauteur de l'étiquette afin que ces queues ne soient pas hachées de. Les 2/3 fonctionnent bien pour Helvetica Neue, mais essayez d’autres ratios en fonction de la police que vous utilisez. Pour les polices sans texte, caractères numériques ou majuscules, un rapport 1: 1 peut suffire.
Sur la base de l'excellente réponse de @ Conaaando, je l'ai mise à jour avec une version avec les paramètres IBDesignable inclus, ce qui permet de la modifier dans le générateur d'interface:
Et le code:
//
// TIFFitToHeightLabel.Swift
//
import Foundation
import UIKit
@IBDesignable class TIFFitToHeightLabel: UILabel {
@IBInspectable var minFontSize:CGFloat = 12 {
didSet {
font = fontToFitHeight()
}
}
@IBInspectable var maxFontSize:CGFloat = 30 {
didSet {
font = fontToFitHeight()
}
}
override func layoutSubviews() {
super.layoutSubviews()
font = fontToFitHeight()
}
// Returns an UIFont that fits the new label's height.
private func fontToFitHeight() -> UIFont {
var minFontSize: CGFloat = self.minFontSize
var maxFontSize: CGFloat = self.maxFontSize
var fontSizeAverage: CGFloat = 0
var textAndLabelHeightDiff: CGFloat = 0
while (minFontSize <= maxFontSize) {
fontSizeAverage = minFontSize + (maxFontSize - minFontSize) / 2
if let labelText: NSString = text {
let labelHeight = frame.size.height
let testStringHeight = labelText.sizeWithAttributes(
[NSFontAttributeName: font.fontWithSize(fontSizeAverage)]
).height
textAndLabelHeightDiff = labelHeight - testStringHeight
if (fontSizeAverage == minFontSize || fontSizeAverage == maxFontSize) {
if (textAndLabelHeightDiff < 0) {
return font.fontWithSize(fontSizeAverage - 1)
}
return font.fontWithSize(fontSizeAverage)
}
if (textAndLabelHeightDiff < 0) {
maxFontSize = fontSizeAverage - 1
} else if (textAndLabelHeightDiff > 0) {
minFontSize = fontSizeAverage + 1
} else {
return font.fontWithSize(fontSizeAverage)
}
}
}
return font.fontWithSize(fontSizeAverage)
}
}
Cela emprunte beaucoup à la réponse de Joel Fischer. Sa réponse ne prend en compte que la hauteur de l'étiquette. J'ai apporté quelques modifications pour prendre en compte la largeur de l'étiquette (en fonction d'une chaîne d'entrée).
typedef enum
{
kDimensionHeight,
kDimensionWidth,
} DimensionType;
@implementation UIFont (AdaptiveFont)
+ (UIFont *)_adaptiveFontWithName:(NSString *)fontName minSize:(NSInteger)minSize labelDimension:(CGFloat)labelDimension testString:(NSString *)testString dimension:(DimensionType)dimension
{
UIFont *tempFont = nil;
NSInteger tempMin = minSize;
NSInteger tempMax = 256;
NSInteger mid = 0;
NSInteger difference = 0;
CGFloat testStringDimension = 0.0;
while (tempMin <= tempMax) {
@autoreleasepool {
mid = tempMin + (tempMax - tempMin) / 2;
tempFont = [UIFont fontWithName:fontName size:mid];
// determine dimension to test
if (dimension == kDimensionHeight) {
testStringDimension = [testString sizeWithFont:tempFont].height;
} else {
testStringDimension = [testString sizeWithFont:tempFont].width;
}
difference = labelDimension - testStringDimension;
if (mid == tempMin || mid == tempMax) {
if (difference < 0) {
return [UIFont fontWithName:fontName size:(mid - 1)];
}
return [UIFont fontWithName:fontName size:mid];
}
if (difference < 0) {
tempMax = mid - 1;
} else if (difference > 0) {
tempMin = mid + 1;
} else {
return [UIFont fontWithName:fontName size:mid];
}
}
}
return [UIFont fontWithName:fontName size:mid];
}
+ (UIFont *)adaptiveFontWithName:(NSString *)fontName minSize:(NSInteger)minSize labelSize:(CGSize)labelSize string:(NSString *)string
{
UIFont *adaptiveFont = nil;
NSString *testString = nil;
// get font, given a max height
testString = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
UIFont *fontConstrainingHeight = [UIFont _adaptiveFontWithName:fontName minSize:minSize labelDimension:labelSize.height testString:testString dimension:kDimensionHeight];
CGSize boundsConstrainingHeight = [string sizeWithFont:fontConstrainingHeight];
CGSize boundsConstrainingWidth = CGSizeZero;
// if WIDTH is fine (while constraining HEIGHT), return that font
if (boundsConstrainingHeight.width <= labelSize.width) {
adaptiveFont = fontConstrainingHeight;
} else {
// get font, given a max width
// i.e., fontConstrainingWidth
testString = string;
adaptiveFont = [UIFont _adaptiveFontWithName:fontName minSize:minSize labelDimension:labelSize.width testString:testString dimension:kDimensionWidth];
// TEST comparison
boundsConstrainingWidth = [string sizeWithFont:adaptiveFont];
}
return adaptiveFont;
}
Pour en savoir plus sur la réponse de @Joe Blow, voici une catégorie Objective-C UILabel+FitToHeight
qui vous permet d'importer et de basculer facilement une adjustsFontSizeToFitHeight
de la même manière que vous pouvez déjà adjustsFontSizeToFitWidth
.
UILabel + FitToHeight.h
#import <UIKit/UIKit.h>
@interface UILabel (FitToHeight)
@property (nonatomic, assign) BOOL adjustsFontSizeToFitHeight;
@end
UILabel + FitToHeight.m
#import "UILabel+FitToHeight.h"
#import <objc/runtime.h>
@implementation UILabel (FitToHeight)
-(BOOL)adjustsFontSizeToFitHeight {
NSNumber *number = objc_getAssociatedObject(self, @selector(adjustsFontSizeToFitHeight));
return [number boolValue];
}
-(void)setAdjustsFontSizeToFitHeight:(BOOL)adjustsFontSizeToFitHeight {
NSNumber *number = [NSNumber numberWithBool:adjustsFontSizeToFitHeight];
objc_setAssociatedObject(self, @selector(adjustsFontSizeToFitHeight), number, OBJC_ASSOCIATION_ASSIGN);
}
-(UIFont *)fontToFitHeight {
float desiredHeight = [self frame].size.height;
float guess;
float guessHeight;
guess = [[self font] pointSize];
guessHeight = [self sizeIf:guess];
if(guessHeight == desiredHeight) {
return [[self font] fontWithSize:guess];
}
int attempts = 4;
while(attempts > 0) {
guess = guess * (desiredHeight / guessHeight);
guessHeight = [self sizeIf:guess];
if(guessHeight == desiredHeight) {
return [[self font] fontWithSize:guess];
}
attempts--;
}
return [[self font] fontWithSize:guess];
}
-(float)sizeIf:(float)sizeToTry {
CGSize size = [[self text] sizeWithAttributes:@{ NSFontAttributeName : [[self font] fontWithSize:sizeToTry] }];
return size.height;
}
-(void)layoutSubviews {
[super layoutSubviews];
if([self adjustsFontSizeToFitHeight]) {
[self setFont:[self fontToFitHeight]];
}
}
Importez comme n'importe quelle autre catégorie ...
#import "UILabel+FitToHeight.h"
et utiliser comme suit ...
UILabel *titleLabel = [[UILabel alloc] init];
[titleLabel setAdjustsFontSizeToFitHeight:YES];
[titleLabel setAdjustsFontSizeToFitWidth:YES];
Il est intéressant de noter que ce still fonctionne avec [titleLabel setAdjustsFontSizeToFitWidth:YES];
, de sorte que l’utilisation conjointe des deux est tout à fait possible.
Pardonnez-moi si je me trompe, mais tout ce qui est mentionné ici est inutile. Définissez votre police à nouveau juste après le changement avec une nouvelle taille de police de votreLabel.height
Vous pouvez également rechercher une comparaison conditionnelle entre ces valeurs (yourLabel.height et fontSize) pour éviter les mises à jour inutiles.
Tout ce que vous devez faire c'est:
[yourLabel setFont:[UIFont fontWithName:@"*your fontname*" size:yourLabel.frame.size.height]];
La réponse acceptée a un bug. La distance variable doit être un nombre à virgule flottante, sinon une taille de police trop grande peut être renvoyée. En outre, l'utilisation de "- (CGSize) sizeWithFont: (UIFont *) font;"; est obsolète. Voici le code avec ces 2 problèmes résolus.
+ (UIFont *)findAdaptiveFontWithName:(NSString *)fontName forUILabelSize:(float)maxHeight withMaxFontSize:(int)maxFontSize
{
UIFont *tempFont = nil;
NSString *testString = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
NSInteger tempMin = 0;
NSInteger tempMax = maxFontSize;
NSInteger mid = 0;
float difference = 0;
while (tempMin <= tempMax) {
mid = tempMin + (tempMax - tempMin) / 2;
tempFont = [UIFont fontWithName:fontName size:mid];
UILabel* dummyLabel = [[UILabel alloc] initWithFrame:CGRectZero];
dummyLabel.text = testString;
dummyLabel.font = tempFont;
[dummyLabel sizeToFit];
difference = maxHeight - dummyLabel.bounds.size.height;
if (mid == tempMin || mid == tempMax) {
if (difference < 0) {
return [UIFont fontWithName:fontName size:(mid - 1)];
}
return [UIFont fontWithName:fontName size:mid];
}
if (difference < 0) {
tempMax = mid - 1;
} else if (difference > 0) {
tempMin = mid + 1;
} else {
return [UIFont fontWithName:fontName size:mid];
}
}
return [UIFont fontWithName:fontName size:mid];
}
Variation rapide:
J'ai réussi à le faire avec une extension. Fonctionne très bien, la taille de police minimale est 5. Je soustrais 10 à la hauteur, je laisse donc une "marge" également, mais vous pouvez le supprimer ou le modifier.
extension UILabel {
//Finds and sets a font size that matches the height of the frame.
//Use in case the font size is epic huge and you need to resize it.
func resizeToFitHeight(){
var currentfontSize = font.pointSize
let minFontsize = CGFloat(5)
let constrainedSize = CGSizeMake(frame.width, CGFloat.max)
while (currentfontSize >= minFontsize){
let newFont = font.fontWithSize(currentfontSize)
let attributedText: NSAttributedString = NSAttributedString(string: text!, attributes: [NSFontAttributeName: newFont])
let rect: CGRect = attributedText.boundingRectWithSize(constrainedSize, options: .UsesLineFragmentOrigin, context: nil)
let size: CGSize = rect.size
if (size.height < frame.height - 10) {
font = newFont
break;
}
currentfontSize--
}
//In case the text is too long, we still show something... ;)
if (currentfontSize == minFontsize){
font = font.fontWithSize(currentfontSize)
}
}
}
Mes excuses, si j'ai manqué quelque chose ici dans tout le texte.
J'ai suivi les suggestions @Crazyrems pour choisir automatiquement la police de caractères de l'étiquette. Cela met à l'échelle la police en fonction de la largeur, comme d'autres l'ont observé.
Ensuite, je viens de définir "Lignes" sur 0 dans la section de police de Xcode d'UILabel. Dans le code, cela devrait être numberOfLines
. C'est tout.
Le mérite revient à @Mikrasya, qui a fait allusion à cette solution dans l'un des commentaires ci-dessus.
Testé sur Xcode 7.3 et iOS 9.3.2.
Cela semblait fonctionner pour moi. J'ai sous-classé UILabel et, dans le layoutSubviews, j'ai vérifié la hauteur réelle et réglé la taille de la police en conséquence.
import UIKit
class HeightAdjustableLabel: UILabel {
override func layoutSubviews() {
super.layoutSubviews()
if frame.height < font.pointSize + 2 {
font = font.withSize(frame.height - 2)
}
}
}
Construit à partir de réponse épique de Joel Fisher mais écrit comme extension Swift 4
extension String {
/// Attempts to return the font specified by name of the appropriate point
/// size for this string to fit within a particular container size and
/// constrained to a lower and upper bound point size.
/// - parameter name: of the font.
/// - parameter containerSize: that this string should fit inside.
/// - parameter lowerBound: minimum allowable point size of this font.
/// - parameter upperBound: maximum allowable point size of this font.
/// - returns: the font specified by name of the appropriate point
/// size for this string to fit within a particular container size and
/// constrained to a lower and upper bound point size; `nil` if no such
/// font exists.
public func font(named name: String,
toFit containerSize: CGSize,
noSmallerThan lowerBound: CGFloat = 1.0,
noLargerThan upperBound: CGFloat = 256.0) -> UIFont? {
let lowerBound = lowerBound > upperBound ? upperBound : lowerBound
let mid = lowerBound + (upperBound - lowerBound) / 2
guard let tempFont = UIFont(name: name, size: mid) else { return nil }
let difference = containerSize.height -
self.size(withAttributes:
[NSAttributedStringKey.font : tempFont]).height
if mid == lowerBound || mid == upperBound {
return UIFont(name: name, size: difference < 0 ? mid - 1 : mid)
}
return difference < 0 ? font(named: name,
toFit: containerSize,
noSmallerThan: mid,
noLargerThan: mid - 1) :
(difference > 0 ? font(named: name,
toFit: containerSize,
noSmallerThan: mid,
noLargerThan: mid - 1) :
UIFont(name: name, size: mid))
}
/// Returns the system font of the appropriate point size for this string
/// to fit within a particular container size and constrained to a lower
/// and upper bound point size.
/// - parameter containerSize: that this string should fit inside.
/// - parameter lowerBound: minimum allowable point size of this font.
/// - parameter upperBound: maximum allowable point size of this font.
/// - returns: the system font of the appropriate point size for this string
/// to fit within a particular container size and constrained to a lower
/// and upper bound point size.
public func systemFont(toFit containerSize: CGSize,
noSmallerThan lowerBound: CGFloat = 1.0,
noLargerThan upperBound: CGFloat = 256.0) -> UIFont {
let lowerBound = lowerBound > upperBound ? upperBound : lowerBound
let mid = lowerBound + (upperBound - lowerBound) / 2
let tempFont = UIFont.systemFont(ofSize: mid)
let difference = containerSize.height -
self.size(withAttributes:
[NSAttributedStringKey.font : tempFont]).height
if mid == lowerBound || mid == upperBound {
return UIFont.systemFont(ofSize: difference < 0 ? mid - 1 : mid)
}
return difference < 0 ? systemFont(toFit: containerSize,
noSmallerThan: mid,
noLargerThan: mid - 1) :
(difference > 0 ? systemFont(toFit: containerSize,
noSmallerThan: mid,
noLargerThan: mid - 1) :
UIFont.systemFont(ofSize: mid))
}
}
Utilisation:
let font = "Test string".font(named: "Courier New",
toFit: CGSize(width: 150.0, height: 30.0),
noSmallerThan: 12.0,
noLargerThan: 20.0)
let sysfont = "Test string".systemFont(toFit: CGSize(width: 150.0, height: 30.0),
noSmallerThan: 12.0,
noLargerThan: 20.0)
Pour les étiquettes UIL qui redimensionnent proportionnellement pour les appareils plus grands/plus petits:
La solution la plus efficace pour moi a été de définir la taille de la police en fonction de la hauteur de l'étiquette +/- d'un facteur de correction. En supposant l'utilisation de contraintes de mise en page automatique, positionnez y y centre-vertical aligné au bas de la vue d'ensemble, multiplié par un rapport. De même, dans IB, contraignez la largeur de l'étiquette à une proportion de la largeur de l'écran.
Vous pouvez éventuellement verrouiller le rapport hauteur/largeur de l'étiquette avec une contrainte d'aspect. Toutefois, cela peut entraîner un écrêtage si vous ne maîtrisez pas correctement le calcul de la taille de la police. Le seul motif de verrouillage des proportions est si les positions des autres contrôles/vues sont relatives à cette étiquette. Cependant, je recommande fortement de placer de tels contrôles/vues par rapport à la hauteur/largeur du dessus afin qu'ils ne dépendent pas de cette étiquette.
Je comprends que ce n’est pas vraiment une solution encapsulée, mais elle m’a toujours causé le moins de chagrin. La seule autre solution qui se rapprochait utilisait les boucles while, mais dans mon cas, je ne pouvais pas gérer les délais imposés pour chaque appel système d'agencement/d'actualisation.