Как проверить, если UILabel усечен?
у меня есть UILabel
Это может быть разной длины в зависимости от того, работает ли мое приложение в портретном или ландшафтном режиме на iPhone или iPad. Когда текст слишком длинный, чтобы показать на одной строке, и он усекается, я хочу, чтобы пользователь мог нажать его и получить всплывающее окно полного текста.
Как я могу проверить, чтобы увидеть, если UILabel
усечение текста? Это вообще возможно? Прямо сейчас я просто проверяю разные длины на основе того, в каком режиме я нахожусь, но он не работает супер что ж.
17 ответов:
Вы можете посчитать ширина строки и посмотреть, если ширина превышает
label.bounds.size.width
NSString UIKit Дополнения имеет несколько методов для вычисления размера строки с определенным шрифтом. Однако, если у вас есть minimumFontSize для вашей метки, которая позволяет системе сжимать текст до этого размера. Вы можете использовать sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode: в этом случай.
CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:label.font}]; if (size.width > label.bounds.size.width) { ... }
Swift (как расширение) - работает для многострочного uilabel:
swift4: (
attributes
парам изboundingRect
немного изменен)extension UILabel { var isTruncated: Bool { guard let labelText = text else { return false } let labelTextSize = (labelText as NSString).boundingRect( with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil).size return labelTextSize.height > bounds.size.height } }
swift3:
extension UILabel { var isTruncated: Bool { guard let labelText = text else { return false } let labelTextSize = (labelText as NSString).boundingRect( with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil).size return labelTextSize.height > bounds.size.height } }
swift2:
extension UILabel { func isTruncated() -> Bool { if let string = self.text { let size: CGSize = (string as NSString).boundingRectWithSize( CGSize(width: self.frame.size.width, height: CGFloat(FLT_MAX)), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: self.font], context: nil).size if (size.height > self.bounds.size.height) { return true } } return false } }
EDIT: Я только что увидел, что мой ответ был поддержан, но фрагмент кода, который я дал, устарел.
Теперь лучший способ сделать это (ARC):NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init]; paragraph.lineBreakMode = mylabel.lineBreakMode; NSDictionary *attributes = @{NSFontAttributeName : mylabel.font, NSParagraphStyleAttributeName : paragraph}; CGSize constrainedSize = CGSizeMake(mylabel.bounds.size.width, NSIntegerMax); CGRect rect = [mylabel.text boundingRectWithSize:constrainedSize options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes:attributes context:nil]; if (rect.size.height > mylabel.bounds.size.height) { NSLog(@"TOO MUCH"); }
Примечание вычисленный размер не целое число. Так что если вы делаете такие вещи, как
int height = rect.size.height
, вы потеряете некоторую точность с плавающей запятой и можете иметь неправильные результаты.ответ (устаревший) :
Если ваша метка многострочная, вы можете использовать этот код:
CGSize perfectSize = [mylabel.text sizeWithFont:mylabel.font constrainedToSize:CGSizeMake(mylabel.bounds.size.width, NSIntegerMax) lineBreakMode:mylabel.lineBreakMode]; if (perfectSize.height > mylabel.bounds.size.height) { NSLog(@"TOO MUCH"); }
вы можете сделать категорию С UILabel
- (BOOL)isTextTruncated { CGRect testBounds = self.bounds; testBounds.size.height = NSIntegerMax; CGRect limitActual = [self textRectForBounds:[self bounds] limitedToNumberOfLines:self.numberOfLines]; CGRect limitTest = [self textRectForBounds:testBounds limitedToNumberOfLines:self.numberOfLines + 1]; return limitTest.size.height>limitActual.size.height; }
добавить здоровье ответ, вы должны использовать
intrinsicContentSize
вместоframe
, чтобы он работал для Autolayout- (BOOL)isTruncated:(UILabel *)label{ CGSize sizeOfText = [label.text boundingRectWithSize: CGSizeMake(label.intrinsicContentSize.width, CGFLOAT_MAX) options: (NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes: [NSDictionary dictionaryWithObject:label.font forKey:NSFontAttributeName] context: nil].size; if (self.intrinsicContentSize.height < ceilf(sizeOfText.height)) { return YES; } return NO; }
используйте эту категорию, чтобы найти, если метка усечена на iOS 7 и выше.
// UILabel+Truncation.h @interface UILabel (Truncation) @property (nonatomic, readonly) BOOL isTruncated; @end // UILabel+Truncation.m @implementation UILabel (Truncation) - (BOOL)isTruncated { CGSize sizeOfText = [self.text boundingRectWithSize:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) attributes:@{ NSFontAttributeName : label.font } context: nil].size; if (self.frame.size.height < ceilf(sizeOfText.height)) { return YES; } return NO; } @end
Swift 3
вы можете подсчитать количество строк после присвоения строки и сравнить с максимальным количеством строк метки.
import Foundation import UIKit extension UILabel { func countLabelLines() -> Int { // Call self.layoutIfNeeded() if your view is uses auto layout let myText = self.text! as NSString let attributes = [NSFontAttributeName : self.font] let labelSize = myText.boundingRect(with: CGSize(width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: attributes, context: nil) return Int(ceil(CGFloat(labelSize.height) / self.font.lineHeight)) } func isTruncated() -> Bool { if (self.countLabelLines() > self.numberOfLines) { return true } return false } }
это работает для iOS 8:
CGSize size = [label.text boundingRectWithSize:CGSizeMake(label.bounds.size.width, NSIntegerMax) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil].size; if (size.height > label.frame.size.height) { NSLog(@"truncated"); }
Я написал категорию для работы с усечением UILabel. Работает на iOS 7 и позже. Надеюсь, это поможет ! усечение хвоста uilabel
@implementation UILabel (Truncation) - (NSRange)truncatedRange { NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[self attributedText]]; NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; [textStorage addLayoutManager:layoutManager]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:[self bounds].size]; textContainer.lineFragmentPadding = 0; [layoutManager addTextContainer:textContainer]; NSRange truncatedrange = [layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:0]; return truncatedrange; } - (BOOL)isTruncated { return [self truncatedRange].location != NSNotFound; } - (NSString *)truncatedText { NSRange truncatedrange = [self truncatedRange]; if (truncatedrange.location != NSNotFound) { return [self.text substringWithRange:truncatedrange]; } return nil; } @end
добавить к чему @iDev сделал, я изменил
self.frame.size.height
использоватьlabel.frame.size.height
, а также не использоватьNSStringDrawingUsesLineFontLeading
. После этих изменений я достиг идеального расчета того, когда произойдет усечение (по крайней мере, для моего случая).- (BOOL)isTruncated:(UILabel *)label { CGSize sizeOfText = [label.text boundingRectWithSize: CGSizeMake(label.bounds.size.width, CGFLOAT_MAX) options: (NSStringDrawingUsesLineFragmentOrigin) attributes: [NSDictionary dictionaryWithObject:label.font forKey:NSFontAttributeName] context: nil].size; if (label.frame.size.height < ceilf(sizeOfText.height)) { return YES; } return NO; }
вот выбранный ответ в Swift 3 (как расширение). ОП спрашивал о метках 1 линии. Многие из быстрых ответов, которые я пробовал здесь, специфичны для многострочных меток и не помечаются правильно на однострочных метках.
extension UILabel { var isTruncated: Bool { guard let labelText = text as? NSString else { return false } let size = labelText.size(attributes: [NSFontAttributeName: font]) return size.width > self.bounds.width } }
extension UILabel { public func resizeIfNeeded() -> CGFloat? { guard let text = text, !text.isEmpty else { return nil } if isTruncated() { numberOfLines = 0 sizeToFit() return frame.height } return nil } func isTruncated() -> Bool { guard let text = text, !text.isEmpty else { return false } let size: CGSize = text.size(withAttributes: [NSAttributedStringKey.font: font]) return size.width > self.bounds.size.width } }
можно вычислить ширину строки и посмотреть, если ширина больше, чем ширина этикетки.
поскольку все ответы выше используют амортизированные методы, я подумал, что это может быть полезно:
- (BOOL)isLabelTruncated:(UILabel *)label { BOOL isTruncated = NO; CGRect labelSize = [label.text boundingRectWithSize:CGSizeFromString(label.text) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil]; if (labelSize.size.width / labelSize.size.height > label.numberOfLines) { isTruncated = YES; } return isTruncated; }
для обработки iOS 6 (Да, некоторые из нас все еще должны), вот еще одно расширение для ответа @iDev. Ключевой вывод заключается в том, что для iOS 6, чтобы убедиться, что numberOfLines вашего UILabel установлен в 0 перед вызовом sizeThatFits; если нет, это даст вам результат, который говорит, что "точки для рисования numberOfLines стоит высоты" необходимо нарисовать текст метки.
- (BOOL)isTruncated { CGSize sizeOfText; // iOS 7 & 8 if([self.text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) { sizeOfText = [self.text boundingRectWithSize:CGSizeMake(self.bounds.size.width,CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes:@{NSFontAttributeName:self.font} context:nil].size; } // iOS 6 else { // For iOS6, set numberOfLines to 0 (i.e. draw label text using as many lines as it takes) // so that siteThatFits works correctly. If we leave it = 1 (for example), it'll come // back telling us that we only need 1 line! NSInteger origNumLines = self.numberOfLines; self.numberOfLines = 0; sizeOfText = [self sizeThatFits:CGSizeMake(self.bounds.size.width,CGFLOAT_MAX)]; self.numberOfLines = origNumLines; } return ((self.bounds.size.height < sizeOfText.height) ? YES : NO); }
у меня были проблемы с
boundingRect(with:options:attributes:context:)
при использовании autolayout (для установки максимальной высоты) и приписанного текста сNSParagraph.lineSpacing
интервал между строками игнорировался (даже при передаче в
attributes
доboundingRect
метод), поэтому метка может считаться не усеченной, когда она была.решение, которое я нашел, чтобы использовать
UIView.sizeThatFits
:extension UILabel { var isTruncated: Bool { layoutIfNeeded() let heightThatFits = sizeThatFits(bounds.size).height return heightThatFits > bounds.size.height } }
Swift 3 решение
Я думаю, что лучшим решением будет (1) создать
UILabel
С теми же свойствами, что и метка, которую вы проверяете на усечение,(2) вызов.sizeToFit()
,(3) сравните атрибуты фиктивной метки с вашей фактической меткой.например, если вы хотите проверить, усекается ли одна выровненная метка с переменной шириной или нет, то вы можете использовать это расширение:
extension UILabel { func isTruncated() -> Bool { let label = UILabel(frame: CGRect(x: 0, y: 0, width: CGFloat.greatestFiniteMagnitude, height: self.bounds.height)) label.numberOfLines = 1 label.font = self.font label.text = self.text label.sizeToFit() if label.frame.width > self.frame.width { return true } else { return false } } }
...но опять же, вы можете легко изменить приведенный выше код в соответствии с вашими потребностями. Итак, предположим, что ваш ярлык многолинейный и имеет разную высоту. Тогда расширение будет выглядеть примерно так:
extension UILabel { func isTruncated() -> Bool { let label = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude)) label.numberOfLines = 0 label.font = self.font label.text = self.text label.sizeToFit() if label.frame.height > self.frame.height { return true } else { return false } } }
не было бы легко установить атрибут заголовка для метки, установка этого будет отображать полную метку при наведении.
вы можете рассчитать длину метки и ширину div (преобразовать в длину -jQuery / Javascript - как преобразовать значение пикселя (20px) в числовое значение (20)).
установите jquery для установки заголовка, если длина больше ширины div.
var divlen = parseInt(jQuery("#yourdivid").width,10); var lablen =jQuery("#yourlabelid").text().length; if(lablen < divlen){ jQuery("#yourlabelid").attr("title",jQuery("#yourlabelid").text()); }