Autoshrink на UILabel с несколькими строками
можно ли использовать свойство autoshrink совместно на нескольких строках на A UILabel
? например, большой размер текста можно на 2 линии.
16 ответов:
эти люди нашли решение:
http://www.11pixel.com/blog/28/resize-multi-line-text-to-fit-uilabel-on-iphone/
их решение выглядит следующим образом:
int maxDesiredFontSize = 28; int minFontSize = 10; CGFloat labelWidth = 260.0f; CGFloat labelRequiredHeight = 180.0f; //Create a string with the text we want to display. self.ourText = @"This is your variable-length string. Assign it any way you want!"; /* This is where we define the ideal font that the Label wants to use. Use the font you want to use and the largest font size you want to use. */ UIFont *font = [UIFont fontWithName:@"Marker Felt" size:maxDesiredFontSize]; int i; /* Time to calculate the needed font size. This for loop starts at the largest font size, and decreases by two point sizes (i=i-2) Until it either hits a size that will fit or hits the minimum size we want to allow (i > 10) */ for(i = maxDesiredFontSize; i > minFontSize; i=i-2) { // Set the new font size. font = [font fontWithSize:i]; // You can log the size you're trying: NSLog(@"Trying size: %u", i); /* This step is important: We make a constraint box using only the fixed WIDTH of the UILabel. The height will be checked later. */ CGSize constraintSize = CGSizeMake(labelWidth, MAXFLOAT); // This step checks how tall the label would be with the desired font. CGSize labelSize = [self.ourText sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap]; /* Here is where you use the height requirement! Set the value in the if statement to the height of your UILabel If the label fits into your required height, it will break the loop and use that font size. */ if(labelSize.height <= labelRequiredHeight) break; } // You can see what size the function is using by outputting: NSLog(@"Best size is: %u", i); // Set the UILabel's font to the newly adjusted font. msg.font = font; // Put the text into the UILabel outlet variable. msg.text = self.ourText;
для того, чтобы получить эту работу, IBOutlet должен быть назначен в построителе интерфейса к UILabel.
" IBOutlet UILabel * msg;"
все заслуги людей в 11pixel.
Я несколько изменил приведенный выше код, чтобы сделать его категорией на
UILabel
:заголовочный файл:
#import <UIKit/UIKit.h> @interface UILabel (MultiLineAutoSize) - (void)adjustFontSizeToFit; @end
и файл реализации:
@implementation UILabel (MultiLineAutoSize) - (void)adjustFontSizeToFit { UIFont *font = self.font; CGSize size = self.frame.size; for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumFontSize; maxSize -= 1.f) { font = [font fontWithSize:maxSize]; CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT); CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap]; if(labelSize.height <= size.height) { self.font = font; [self setNeedsLayout]; break; } } // set the font to the minimum size anyway self.font = font; [self setNeedsLayout]; } @end
Я нашел эту ссылку http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/
проблема может быть решена с помощью построителя интерфейса в 3 простых шага:
- установите "Autoshrink" в " минимальный размер шрифта."
- установить шрифт на ваш самый большой желаемый размер шрифта (20) и установить строки, скажем, 10, который в моем случае был как многие строки как бы вписываются в метку при таком размере шрифта.
- затем измените "Линия Разрывы " от "обертывания слов" до "усечения хвоста"."
надеюсь, что это помогает!
вот решение категории обновлено до iOS 7 на основе обновлений itecedor для iOS 6.
заголовочный файл:
#import <UIKit/UIKit.h> @interface UILabel (MultiLineAutoSize) - (void)adjustFontSizeToFit; @end
и файл реализации:
@implementation UILabel (MultiLineAutoSize) - (void)adjustFontSizeToFit { UIFont *font = self.font; CGSize size = self.frame.size; for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor * self.font.pointSize; maxSize -= 1.f) { font = [font fontWithSize:maxSize]; CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT); CGRect textRect = [self.text boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil]; CGSize labelSize = textRect.size; if(labelSize.height <= size.height) { self.font = font; [self setNeedsLayout]; break; } } // set the font to the minimum size anyway self.font = font; [self setNeedsLayout]; } @end
ответ, отмеченный как решение, является избитым и неточным. UILabel будет обрабатывать его автоматически, если вы правильно установите следующие свойства:
numberOfLines
должно быть ненулевым
adjustsFontSizeToFitWidth
должно бытьYES
lineBreakMode
должны не бытьNSLineBreakByCharWrapping
илиNSLineBreakByWordWrapping
Я не могу прокомментировать сообщение MontiRabbit из-за отсутствия репутации, поэтому я сделаю новый ответ. Решение, которое он (и ее реферер) предложил, не работает на Xcode 7.3 или лучше, это неточно. Чтобы заставить его работать, в раскадровке, я должен был:
- Установите ограничение ширины (чистая ширина или хвост и свинец)
- установите ограничение по высоте (это очень важно, обычно при авторазборе не задается высота метки)
- установите свойство" Autoshrink " в " Minimum масштаб шрифта" или "минимальный размер шрифта" (работает в обоих случаях)
- установите свойство " разрывы строк "в"усечение хвоста"
- установите свойство "Lines" в ненулевое значение
надеюсь, что это помогает! ;)
версия swifty адаптирована из @DaGaMs.
SWIFT 2:
extension UILabel { func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) { let maxFontSize = maximumFontSize ?? font.pointSize for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) { let proposedFont = font.fontWithSize(size) let constraintSize = CGSizeMake(bounds.size.width, CGFloat(MAXFLOAT)) let labelSize = ((text ?? "") as NSString).boundingRectWithSize(constraintSize, options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName: proposedFont], context: nil) if labelSize.height <= bounds.size.height { font = proposedFont setNeedsLayout() break; } } } }
SWIFT 3:
extension UILabel { func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) { let maxFontSize = maximumFontSize ?? font.pointSize for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) { let proposedFont = font.withSize(size) let constraintSize = CGSize(width: bounds.size.width, height: CGFloat(MAXFLOAT)) let labelSize = ((text ?? "") as NSString).boundingRect(with: constraintSize, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: proposedFont], context: nil) if labelSize.height <= bounds.size.height { font = proposedFont setNeedsLayout() break; } } } }
Мне понравился ответ DaGaMs, но при использовании меток, таких как UITableViewCells, которые могут быть возвращены из dequeueReusableCell:, обычный размер шрифта будет продолжать уменьшаться, даже если исходный размер шрифта по-прежнему желателен для некоторых ячеек tableView, которые имеют меньше текста и могут использовать исходный размер шрифта исходной метки.
Итак, я начинаю с категории DaGaMs в качестве точки прыжка, я создал отдельный класс, а не отдельную категорию, и я уверен, что мой UILabels в моей раскадровке используют этот новый класс:
#import "MultiLineAutoShrinkLabel.h" @interface MultiLineAutoShrinkLabel () @property (readonly, nonatomic) UIFont* originalFont; @end @implementation MultiLineAutoShrinkLabel @synthesize originalFont = _originalFont; - (UIFont*)originalFont { return _originalFont ? _originalFont : (_originalFont = self.font); } - (void)quoteAutoshrinkUnquote { UIFont* font = self.originalFont; CGSize frameSize = self.frame.size; CGFloat testFontSize = _originalFont.pointSize; for (; testFontSize >= self.minimumFontSize; testFontSize -= 0.5) { CGSize constraintSize = CGSizeMake(frameSize.width, MAXFLOAT); CGSize testFrameSize = [self.text sizeWithFont:(font = [font fontWithSize:testFontSize]) constrainedToSize:constraintSize lineBreakMode:self.lineBreakMode]; // the ratio of testFontSize to original font-size sort of accounts for number of lines if (testFrameSize.height <= frameSize.height * (testFontSize/_originalFont.pointSize)) break; } self.font = font; [self setNeedsLayout]; } @end
в ответе itedcedor есть проблема, на которую указал pwightman. Кроме того, нет необходимости обрезать пробелы. Вот это модифицированная версия:
- (void)adjustFontSizeToFit { UIFont *font = self.font; CGSize size = self.frame.size; for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor * self.font.pointSize; maxSize -= 1.f) { font = [font fontWithSize:maxSize]; CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT); CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping]; if(labelSize.height <= size.height) { self.font = font; [self setNeedsLayout]; break; } } // set the font to the minimum size anyway self.font = font; [self setNeedsLayout]; }
спасибо DaGaMs за это решение.
я обновил его следующим образом:
1-для работы с iOS 6 (так как и minimumFontSize и UILineBreakModeWordWrap являются устаревшими) 2-чтобы удалить пробелы из текста метки, так как это приведет к сбою изменения размера (вы не хотите знать, сколько времени мне потребовалось, чтобы найти эту ошибку)
-(void)adjustFontSizeToFit { self.text = [self.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; UIFont *font = self.font; CGSize size = self.frame.size; for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor; maxSize -= 1.f) { font = [font fontWithSize:maxSize]; CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT); CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping]; if(labelSize.height <= size.height) { self.font = font; [self setNeedsLayout]; break; } } // set the font to the minimum size anyway self.font = font; [self setNeedsLayout]; }
для UIButton, только эти строки работают для меня:
self.centerBtn.titleLabel.numberOfLines = 2; self.centerBtn.titleLabel.textAlignment = NSTextAlignmentCenter; self.centerBtn.titleLabel.adjustsFontSizeToFitWidth = YES;
Я написал небольшую категорию на UILabel, основанную на ответе" The Dude " выше, чтобы достичь этой функциональности.
я использовал решение @ wbarksdale Swift 3, но обнаружил, что длинные слова усекаются посередине. Чтобы сохранить слова нетронутыми, мне пришлось изменить, как показано:
extension UILabel { func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) { let maxFontSize = maximumFontSize ?? font.pointSize let words = self.text?.components(separatedBy: " ") var longestWord: String? if let max = words?.max(by: {.characters.count > .characters.count}) { longestWord = max } for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) { let proposedFont = font.withSize(size) let constraintSize = CGSize(width: bounds.size.width, height: CGFloat(MAXFLOAT)) let labelSize = ((text ?? "") as NSString).boundingRect(with: constraintSize, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: proposedFont], context: nil) let wordConstraintSize = CGSize(width: CGFloat(MAXFLOAT), height: CGFloat(MAXFLOAT)) let longestWordSize = ((longestWord ?? "") as NSString).boundingRect(with: wordConstraintSize, options: .usesFontLeading, attributes: [NSFontAttributeName: proposedFont], context: nil) if labelSize.height <= bounds.size.height && longestWordSize.width < constraintSize.width { font = proposedFont setNeedsLayout() break } } } }
есть метод
NSString
,-sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:
который, по-видимому, существует с iOS 2.0, но, к сожалению, устарел в iOS 7 без предложенной альтернативы, поскольку автоматическое уменьшение размера шрифта не рекомендуется. Я действительно не понимаю позицию Apple по этому вопросу, поскольку они используют ее в keynote и т. д., и я думаю, что если размеры шрифтов находятся в небольшом диапазоне, это нормально. Вот реализация в Swift с использованием этого метода.var newFontSize: CGFloat = 30 let font = UIFont.systemFontOfSize(newFontSize) (self.label.text as NSString).sizeWithFont(font, minFontSize: 20, actualFontSize: &newFontSize, forWidth: self.label.frame.size.width, lineBreakMode: NSLineBreakMode.ByWordWrapping) self.label.font = font.fontWithSize(newFontSize)
Я не знаю, как это может быть достигнуто без использования устаревших методов.
попробуйте это:
либо подкласс UILabel или вызовите метод adjustFontSize после установки свойства text на метке
override var text : String? { didSet { self.adjustFontSize() } } func adjustFontSize() { var lineCount = self.string.components(separatedBy: "\n").count - 1 var textArray = self.string.components(separatedBy: " ") var wordsToCompare = 1 while(textArray.count > 0) { let words = textArray.first(n: wordsToCompare).joined(separator: " ") let wordsWidth = words.widthForHeight(0, font: self.font) if(wordsWidth > self.frame.width) { textArray.removeFirst(wordsToCompare) lineCount += 1 wordsToCompare = 1 } else if(wordsToCompare > textArray.count) { break } else { wordsToCompare += 1 } } self.numberOfLines = lineCount + 1 }
extension UILabel{ func adjustFont(minSize:Int, maxSize:Int){ var newFont = self.font for index in stride(from: maxSize, to: minSize, by: -1) { newFont = UIFont.systemFont(ofSize: CGFloat(index)) let size = CGSize(width: self.frame.width, height: CGFloat(Int.max)) let size2 = (self.text! as NSString).boundingRect(with: size, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedStringKey.font:newFont!], context: nil).size if size2.height < self.frame.size.height{ break } } self.font = newFont }
}
вам также необходимо присвоить значение свойству numberOfLines UILabel.