UILabel'in kesilip kesilmediğini nasıl kontrol edebilirim?


106

Bir var UILabeldeğişen olabilir uzunlukları benim app iPhone veya iPad'de dikey ya da yatay modda çalışıyor olsa da olmasa bağlı. Metin bir satırda gösterilemeyecek kadar uzun olduğunda ve kesildiğinde, kullanıcının ona basıp tam metnin bir açılır penceresini alabilmesini istiyorum.

UILabelMetni kesip kırpmadığını nasıl kontrol edebilirim ? Hatta mümkün mü? Şu anda hangi modda olduğuma bağlı olarak farklı uzunlukları kontrol ediyorum ama çok iyi çalışmıyor.



Bunca yıldan sonra Apple'ın UILabelAPI'ye bu kadar basit bir şeyi hala dahil etmediğine inanamıyorum .
funct7

Yanıtlar:


108

Dizenin genişliğini hesaplayabilir ve genişliğin daha büyük olup olmadığını görebilirsiniz.label.bounds.size.width

NSString UIKit Additions , dizenin boyutunu belirli bir yazı tipiyle hesaplamak için birkaç yönteme sahiptir. Ancak, etiketiniz için sistemin metni bu boyuta küçültmesine izin veren bir minimumFontSize varsa. Bu durumda sizeWithFont: minFontSize: actualFontSize: forWidth: lineBreakMode: kullanmak isteyebilirsiniz .

CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:label.font}];
if (size.width > label.bounds.size.width) {
   ...
}

1
Teşekkürler, tam da ihtiyacım olan şey bu. Tek fark, sizeWithFont: bir CGSize döndürmesiydi.
Randall

Ah, bunu belirttiğin için teşekkürler, örnek kodu düzelttim.
progrmr

16
sizeWithFont kullanımdan kaldırılmıştır: [label.text sizeWithAttributes: @ {NSFontAttributeName: label.font}];
Amelia777

2
@fatuhoku numberOfLines, metni UILabelsınıf referansında belirtildiği gibi görüntülemek için kullanılan maksimum satır sayısını döndürür : developer.apple.com/library/ios/documentation/UIKit/Reference/…
Paul

1
eğer etiket satır sayısına sahipse genişliği şu şekilde satır sayısıyla çarpmayı deneyin if (size.width> label.bounds.size.width * label.numberOfLines) {...}
Fadi Abuzant

90

Swift (uzantı olarak) - çok satırlı uilabel için çalışır:

swift4: ( attributesparametresi boundingRectbiraz değişti)

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
    }

}

999999.0 yüksekliğini CGFloat (FLT_MAX) ile değiştirin
ortam ışığı

2
hızlı 3 için CGFloat.greatestFiniteMagnitude
zero3nna

2
güzel cevap, ancak func yerine hesaplanan özelliği kullanmak daha iyidir: var isTruncated: Bool {if let string = self.text {let size: CGSize = (string as NSString) .boundingRect (with: CGSize (width: self.frame.size .width, height: CGFloat.greatestFiniteMagnitude), seçenekler: NSStringDrawingOptions.usesLineFragmentOrigin, nitelikler: [NSFontAttributeName: self.font], bağlam: nil) .size return (size.height> self.bounds.size.height)} return false}
Mohammadalijf

1
Bu benim için işe yaramadı çünkü bir NSAttributedString kullanıyordum. Çalışmasını sağlamak için, nitelik değerini kullanmak için değiştirmem gerekiyordu: attributedText? .Attributes (at: 0, effectiveRange: nil)
pulse4life

1
Harika cevap, gereksinimlerime göre biraz değiştirmek zorunda kaldı, ancak mükemmel çalıştı. Ayrıca atıfta bulunulan bir dize kullanıyordum - bu yüzden kullandığım nitelikler için: (attributedText? .Attributes (at: 0, effectiveRange: nil) ?? [.font: font]), sadece labelText'in ne zaman boş olmadığını kontrol ettiğinizden emin olun. bu çözümü kullanarak.
Crt Gregoric

20

DÜZENLEME: Cevabımın oy verildiğini yeni gördüm, ancak verdiğim kod pasajı kullanımdan kaldırıldı.
Şimdi bunu yapmanın en iyi yolu (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");
}

Hesaplanan boyutun tamsayı değeri olmadığını unutmayın. Dolayısıyla int height = rect.size.height, benzer şeyler yaparsanız, bazı kayan nokta hassasiyetini kaybedersiniz ve yanlış sonuçlara sahip olabilirsiniz.

Eski yanıt (kullanımdan kaldırıldı):

Etiketiniz çok satırlıysa, bu kodu kullanabilirsiniz:

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");
}

13

UILabel ile kategori oluşturabilirsiniz

- (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;
}

3
dokümandan: textRectForBounds:limitedToNumberOfLines:"Bu yöntemi doğrudan çağırmamalısınız" ...
Martin

@Martin teşekkür ederim, görüyorum ki buradaki alet sınırlı. Ama bu yöntemi sınır ve metin belirledikten sonra çağırdığınızda iyi çalışacak
DongXu

LimitedToNumberOfLines'i ayarlarken neden + 1'liyorsunuz?
Ash

@ Ash, metin için daha fazla alana izin verildiğinde etiketin daha yüksek olup olmadığını kontrol etmek için.
vrwim

1
Bu kod için teşekkürler, otomatik düzen kullanırken bazı sınır durumları dışında benim için çalıştı. Bunları ekleyerek düzelttim: setNeedsLayout() layoutIfNeeded()yöntemin başında.
Jovan Jovanovski

9

İOS 7 ve sonraki sürümlerde bir etiketin kesilip kırpılmadığını bulmak için bu kategoriyi kullanın.

// 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

9

Hızlı 3

Dizeyi atadıktan sonra satır sayısını sayabilir ve etiketin maksimum satır sayısı ile karşılaştırabilirsiniz.

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
    }
}

Swift için güzel cevap bu. Teşekkürler.
JimmyLee

8

İDev'in cevabına eklemek için, Otomatik Düzen için çalışmasını sağlamak için intrinsicContentSizeyerine kullanmalısınızframe

- (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;
}

Teşekkürler! Çerçeve yerine intrinsicContentSize kullanımı, UILabel yüksekliği metni sığdırmak için gerçekten yeterli olduğunda, ancak sınırlı sayıda satıra sahip ve bu nedenle hala kesiliyorsa sorunumun çözümüydü.
Anton Matosov

Bazı nedenlerden dolayı, metin etikete sığmadığında bile HAYIR döndürüyor
Can Poyrazoğlu

6

Budur. Bu attributedText, düzlüğe geri dönmeden önce çalışır text, bu da birden çok yazı tipi ailesi, boyutu ve hatta NSTextAttachment'ları ile uğraşan bizler için çok mantıklıdır!

Otomatik düzen ile iyi çalışıyor, ancak tabii ki kısıtlamaların biz kontrol etmeden önce tanımlanması ve ayarlanması gerekiyor isTruncated, aksi takdirde etiketin kendisi nasıl yerleştirileceğini bilemeyeceği için kesilip kesilmediğini bile bilemez.

Bu soruna sade bir NSStringve ile yaklaşmak işe yaramaz sizeThatFits. İnsanların nasıl böyle olumlu sonuçlar aldığından emin değilim. BTW, defalarca belirtildiği gibi, kullanmak sizeThatFitshiç de ideal değildir çünkü numberOfLinessonuçta ortaya çıkan boyutu hesaba katar , bu da yapmaya çalıştığımız şeyin tüm amacını bozar, çünkü kesilmiş olsun ya da olmasın isTruncatedher zaman geri döner false.

extension UILabel {
    var isTruncated: Bool {
        layoutIfNeeded()

        let rectBounds = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)
        var fullTextHeight: CGFloat?

        if attributedText != nil {
            fullTextHeight = attributedText?.boundingRect(with: rectBounds, options: .usesLineFragmentOrigin, context: nil).size.height
        } else {
            fullTextHeight = text?.boundingRect(with: rectBounds, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil).size.height
        }

        return (fullTextHeight ?? 0) > bounds.size.height
    }
}

Bu benim için iyi çalışıyor, teşekkürler !! Bunun için herhangi bir güncelleme veya değişiklik var mı?
amodkanthe

Mükemmel cevap. Tek sorun attributedText, siz ayarlamasanız bile asla sıfır değildir. Bu yüzden, bunu iki değişken isTextTruncated ve isAttributedTextTruncated olarak uyguladım.
Harris

@Harris, uilabel.h dosyasında varsayılanın sıfır olduğunu söylüyor
Lucas

@LucasChwe Biliyorum, ama pratikte bu doğru değil. Kendiniz deneyebilir ve görebilirsiniz. fyi
Harris

4

İşte Swift 3'te seçilen cevap (bir uzantı olarak). OP 1 satır etiketi soruyordu. Burada denediğim hızlı yanıtların çoğu, çok satırlı etiketlere özgü ve tek satırlı etiketlerde doğru şekilde işaretlenmiyor.

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
    }
}

Axel'in cevabı bunun için işe yaramadı. Bu yaptı. Teşekkürler!
Glenn

2

Bu, iOS 8 için çalışır:

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");
}

2

UILabel'in kesilmesiyle çalışmak için bir kategori yazdım. İOS 7 ve sonraki sürümlerde çalışır. Umarım yardımcı olur ! uilabel kuyruk kesme

@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

her seferinde yalnızca NSNotFound verir
Dari

2
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
    }
}

Dizenin genişliğini hesaplayabilir ve genişliğin etiket genişliğinden büyük olup olmadığını görebilirsiniz.


1

@İDev'in yaptığını eklemek için , self.frame.size.heightkullanmak için değiştirdim label.frame.size.heightve kullanmadım NSStringDrawingUsesLineFontLeading. Bu değişikliklerden sonra, kesmenin ne zaman olacağına dair mükemmel bir hesaplama yaptım (en azından benim durumum için).

- (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;
}

1

Otomatik boundingRect(with:options:attributes:context:)düzen kullanırken (maksimum yüksekliği ayarlamak için) ve atıfta bulunulan bir metin kullanırken sorunlar yaşadımNSParagraph.lineSpacing

Çizgileri arasındaki mesafe (geçirilen bile göz ardı edilmiştir attributesiçin boundingRectbu zaman kesilmeyip gibi etiket kabul edilebilir, böylece bir yöntemle).

Bulduğum çözüm UIView.sizeThatFitsşunları kullanmaktır :

extension UILabel {
    var isTruncated: Bool {
        layoutIfNeeded()
        let heightThatFits = sizeThatFits(bounds.size).height
        return heightThatFits > bounds.size.height
    }
}

0

Yukarıdaki tüm cevaplar amortismana tabi yöntemler kullandığı için, bunun yararlı olabileceğini düşündüm:

- (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;
}

Genişliği yüksekliğe bölüyorsunuz ve satır sayısından büyükse (bu çok iyi 0 olabilir) etiketin kesildiğini söylüyorsunuz. Bunun işe yaraması imkansız.
Can Leloğlu

@ CanLeloğlu lütfen test edin. Çalışan bir örnektir.
Raz

2
Ya numberOfLines sıfıra eşitse?
Can Leloğlu

0

İOS 6'nın üstesinden gelmek için (evet, bazılarımızın hala bunu yapması gerekiyor), işte @ iDev'in cevabına bir başka genişletme. Ana fikir, iOS 6 için sizeThatFits'i çağırmadan önce UILabel'in numberOfLines'inin 0 olarak ayarlandığından emin olmanızdır; değilse, etiket metnini çizmek için "numberOfLines yüksekliği değerinde çizilecek noktalar" gerektiğini söyleyen bir sonuç verecektir.

- (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);
}

0

ViewDidLayoutSubviews'te bunlardan birini çağırdığınızdan emin olun.

public extension UILabel {

    var isTextTruncated: Bool {
        layoutIfNeeded()
        return text?.boundingRect(with: CGSize(width: bounds.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font!], context: nil).size.height ?? 0 > bounds.size.height
    }

    var isAttributedTextTruncated: Bool {
        layoutIfNeeded()
        return attributedText?.boundingRect(with: CGSize(width: bounds.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil).size.height ?? 0 > bounds.size.height
    }

}

0

SWIFT 5

Yalnızca 3 satır görüntüleyecek şekilde ayarlanmış çok çizgili bir UILabel örneği.

    let labelSize: CGSize = myLabel.text!.size(withAttributes: [.font: UIFont.systemFont(ofSize: 14, weight: .regular)])

    if labelSize.width > myLabel.intrinsicContentSize.width * 3 {
        // your label will truncate
    }

Kullanıcı, "metin genişliğine" eklemeden fazladan bir satır ekleyerek dönüş anahtarını seçebilse de, bu durumda bunun gibi bir şey de yararlı olabilir.

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

    if text == "\n" {
        // return pressed 

    }
}

-1

Swift 3 çözümü

Bence en iyi çözüm, (1)UILabel kesme için kontrol ettiğiniz etiketle aynı özelliklere sahip bir oluşturmak , (2) aramak .sizeToFit(), (3) sahte etiketin özelliklerini gerçek etiketinizle karşılaştırmaktır.

Örneğin, değişen genişliğe sahip tek çizgili bir etiketin kesilip kırpılmadığını kontrol etmek istiyorsanız, bu uzantıyı kullanabilirsiniz:

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
        }
    }
}

... ancak yine, yukarıdaki kodu ihtiyaçlarınıza uyacak şekilde kolayca değiştirebilirsiniz. Diyelim ki etiketiniz çok satırlı ve değişen yüksekliğe sahip. Daha sonra uzantı şuna benzer görünecektir:

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
        }
    }
}

-6

Etiket için başlık özniteliğini ayarlamak kolay olmaz mıydı, bunu ayarlamak üzerine gelindiğinde tam etiket görüntülenecektir.

etiketin uzunluğunu ve div genişliğini hesaplayabilirsiniz (uzunluğa dönüştür - jQuery / Javascript - Bir piksel değerini (20px) bir sayı değerine (20) nasıl dönüştürebilirim ).

uzunluk div genişliğinden büyükse başlığı ayarlamak için jquery'yi ayarlayın.

var divlen = parseInt(jQuery("#yourdivid").width,10);
var lablen =jQuery("#yourlabelid").text().length;
if(lablen < divlen){
jQuery("#yourlabelid").attr("title",jQuery("#yourlabelid").text());
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.