Результирующие строки UILabel с UILineBreakModeWordWrap
У меня есть UILabel, размер которого вычисляется с помощью метода sizeWithFont:
. Режим разрыва строки установлен на UILineBreakModeWordWrap
(тот же флаг используется при расчете размера с помощью sizeWithFont:
)...
Все отлично работает, ярлык имеет нужный размер и отображает текст по мере необходимости.
Теперь мне нужно знать строки, которые используются для отображения метки (или строк, которые генерируются при использовании sizeWithFont:
). Я мог бы технически написать собственную реализацию разрыва строки на основе возвратов пространств/кареток, но тогда это не будет гарантировано так же, как реализация Apple, и, следовательно, результирующие строки не будут теми, которые используются для вычисления размера текста, не забывайте о том, как изобретать колесо.
В идеале я бы передал свою строку, задал режим ширины и разрыва строки и получал массив строк, представляющих визуальные строки текста.
Любые идеи, как сделать это самым элегантным способом?
Ответы
Ответ 1
Я не думаю, что для этого есть серебряная пуля.
Вот метод категории, который, похоже, работает для нескольких базовых тестов, которые я бросил на него. Нет гарантий, что он не сломается с чем-то сложным!
Как это работает, нужно пройти через тестирование строк, чтобы узнать, подходит ли диапазон слов по ширине метки. Когда он вычисляет, что текущий диапазон слишком широк, он записывает диапазон последнего соответствия как строку.
Я не утверждаю, что это эффективно. Лучший способ может состоять в том, чтобы реализовать собственный UILabel...
@interface UILabel (Extensions)
- (NSArray*) lines;
@end
@implementation UILabel (Extensions)
- (NSArray*) lines
{
if ( self.lineBreakMode != UILineBreakModeWordWrap )
{
return nil;
}
NSMutableArray* lines = [NSMutableArray arrayWithCapacity:10];
NSCharacterSet* wordSeparators = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString* currentLine = self.text;
int textLength = [self.text length];
NSRange rCurrentLine = NSMakeRange(0, textLength);
NSRange rWhitespace = NSMakeRange(0,0);
NSRange rRemainingText = NSMakeRange(0, textLength);
BOOL done = NO;
while ( !done )
{
// determine the next whitespace word separator position
rWhitespace.location = rWhitespace.location + rWhitespace.length;
rWhitespace.length = textLength - rWhitespace.location;
rWhitespace = [self.text rangeOfCharacterFromSet: wordSeparators options: NSCaseInsensitiveSearch range: rWhitespace];
if ( rWhitespace.location == NSNotFound )
{
rWhitespace.location = textLength;
done = YES;
}
NSRange rTest = NSMakeRange(rRemainingText.location, rWhitespace.location-rRemainingText.location);
NSString* textTest = [self.text substringWithRange: rTest];
CGSize sizeTest = [textTest sizeWithFont: self.font forWidth: 1024.0 lineBreakMode: UILineBreakModeWordWrap];
if ( sizeTest.width > self.bounds.size.width )
{
[lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];
rRemainingText.location = rCurrentLine.location + rCurrentLine.length;
rRemainingText.length = textLength-rRemainingText.location;
continue;
}
rCurrentLine = rTest;
currentLine = textTest;
}
[lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];
return lines;
}
@end
используйте следующее:
NSArray* lines = [_theLabel lines];
int count = [lines count];
Ответ 2
Чтобы вычислить количество строк, которые a UILabel
имеет после обертывания текста, вам нужно будет найти ведущую (высоту строки) вашего шрифта UILabel
(label.font.leading
), а затем разделить высоту вашего мульти- line UILabel
по высоте каждой строки, чтобы получить количество строк.
Вот пример:
- (void)viewDidLoad {
[super viewDidLoad];
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
label.numberOfLines = 0;
label.lineBreakMode = UILineBreakModeWordWrap;
label.text = @"Some really really long string that will cause the label text to wrap and wrap and wrap around. Some really really long string that will cause the label text to wrap and wrap and wrap around.";
CGRect frame = label.frame;
frame.size.width = 150.0f;
frame.size = [label sizeThatFits:frame.size];
label.frame = frame;
CGFloat lineHeight = label.font.leading;
NSUInteger linesInLabel = floor(frame.size.height/lineHeight);
NSLog(@"Number of lines in label: %i", linesInLabel);
[self.view addSubview:label];
}
Или вы можете сделать это в двух строках:
[label sizeToFit];
int numLines = (int)(label.frame.size.height/label.font.leading);
Ответ 3
Просто позвоните ниже и передайте либо UILabel
, либо UITextView
:
-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj
{
NSInteger lineCount = 0;
if([obj isKindOfClass:[UILabel class]])
{
UILabel *label = (UILabel *)obj;
// This method is deprecated in iOS 7.0 or later
// CGSize requiredSize = [label.text sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode];
CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size;
int charSize = label.font.leading;
int rHeight = requiredSize.height;
lineCount = rHeight/charSize;
}
else if ([obj isKindOfClass:[UITextView class]])
{
UITextView *textView = (UITextView *)obj;
lineCount = textView.contentSize.height / textView.font.leading;
}
return lineCount;
}
Теперь вызовите этот метод: -
NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:label]);
NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:textView]);
ОБНОВЛЕНО: SWIFT CODE
func getNumberOfLinesInLabelOrTextView(obj:AnyObject) -> NSInteger {
var lineCount: NSInteger = 0
if (obj.isKindOfClass(UILabel)) {
let label: UILabel = obj as! UILabel
let requiredSize: CGSize = (label.text)!.boundingRectWithSize(CGSizeMake(CGRectGetWidth(label.frame), CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: label.font], context: nil).size
let charSize: CGFloat = label.font.leading
let rHeight: CGFloat = requiredSize.height
lineCount = (NSInteger)(rHeight/charSize)
}
else if (obj.isKindOfClass(UITextView)){
let textView: UITextView = obj as! UITextView
lineCount = (NSInteger)(textView.contentSize.height / textView.font.leading)
}
return lineCount
}
Теперь вызовите этот метод: -
println("%d \(self.getNumberOfLinesInLabelOrTextView(textView))")
println("%d \(self.getNumberOfLinesInLabelOrTextView(label))")
Примечание: leading
- используйте lineHeight. не возвращает фактическое руководство. будет формально устаревшим в будущем.
Ответ 4
для Xcode 7 и выше Ответ TheTiger требует обновления, прокомментированного следующим кодом:
-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj
{
NSInteger lineCount = 0;
if([obj isKindOfClass:[UILabel class]])
{
UILabel *label = (UILabel *)obj;
CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size;
int charSize = label.font.leading;
// now listen , you need to set the text or label with only 1
// then nslog(@"%d",charSize);
// then change the line int charSize = label.font.leading; into
// int charSize = the printed value in case of 1 line
int rHeight = requiredSize.height;
lineCount = rHeight/charSize;
}
else if ([obj isKindOfClass:[UITextView class]])
{
UITextView *textView = (UITextView *)obj;
lineCount = textView.contentSize.height / textView.font.leading;
}
return lineCount;
}
это будет работать, только если вы используете тот же шрифт и размер, это не умный ход, но он мне помог, и я хотел поделиться им как с текущим решением, которое я знаю.
Ответ 5
Обновлено для Swift 3
Чтобы вычислить количество строк, которые имеет UILabel, после обтекания текста вам нужно разделить высоту вашего многострочного UILabel на высоту каждой строки (ascender
).
При попытке ответа Адольфо по какой-то причине label.font.leading
возвращается 0.0, поэтому я использовал label.font.ascender
, который возвращает высоту от базовой линии до вершины фрейма UILabel
. См. Рисунок ниже.
![введите описание изображения здесь]()
//Makes label go to another line if necessary
label.numberOfLines = 0 //Set num of lines to infinity
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
let numLines = Int(label.frame.size.height/label.font.ascender)