Wie berechnet man die UILabel-Höhe dynamisch?

72

Ich möchte die Anzahl der Zeilen und die Höhe von UILabel dynamisch aus dem angegebenen Text berechnen.

Hitesh
quelle

Antworten:

85

Versuche dies

// UILabel *myLabel;

CGSize labelSize = [myLabel.text sizeWithFont:myLabel.font 
                            constrainedToSize:myLabel.frame.size 
                                lineBreakMode:NSLineBreakByWordWrapping];

CGFloat labelHeight = labelSize.height;


int lines = [myLabel.text sizeWithFont:myLabel.font 
                     constrainedToSize:myLabel.frame.size 
                         lineBreakMode:NSLineBreakByWordWrapping].height/16; 
             // '16' is font size

oder

int lines = labelHeight/16;

NSLog(@"lines count : %i \n\n",lines);  

oder

int lines = [myLabel.text sizeWithFont:myLabel.font 
                     constrainedToSize:myLabel.frame.size 
                         lineBreakMode:UILineBreakModeWordWrap].height /myLabel.font.pointSize; //fetching font size from font

Erstellen Sie mithilfe von Kategorien einfach die Kategorieklasse mit dem Namen

UILabel + UILabelDynamicHeight.h

UILabel + UILabelDynamicHeight.m

Keine Spannung mehr bei der Höhenberechnung. Bitte überprüfen Sie die folgende Implementierung.

Updates für iOS7 und höher, iOS 7 unten: Berechnen Sie dynamisch die UILabel-Höhe

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
#define iOS7_0 @"7.0"

UILabel + UILabelDynamicHeight.h

#import <UIKit/UIKit.h>
@interface UILabel (UILabelDynamicHeight)

#pragma mark - Calculate the size the Multi line Label
/*====================================================================*/

    /* Calculate the size of the Multi line Label */

/*====================================================================*/
/**
 *  Returns the size of the Label
 *
 *  @param aLabel To be used to calculte the height
 *
 *  @return size of the Label
 */
 -(CGSize)sizeOfMultiLineLabel;

@end

UILabel + UILabelDynamicHeight.m

#import "UILabel+UILabelDynamicHeight.h"
@implementation UILabel (UILabelDynamicHeight)


#pragma mark - Calculate the size,bounds,frame of the Multi line Label
/*====================================================================*/

/* Calculate the size,bounds,frame of the Multi line Label */

/*====================================================================*/
/**
 *  Returns the size of the Label
 *
 *  @param aLabel To be used to calculte the height
 *
 *  @return size of the Label
 */
-(CGSize)sizeOfMultiLineLabel{

    //Label text
    NSString *aLabelTextString = [self text];

    //Label font
    UIFont *aLabelFont = [self font];

    //Width of the Label
    CGFloat aLabelSizeWidth = self.frame.size.width;


    if (SYSTEM_VERSION_LESS_THAN(iOS7_0)) {
        //version < 7.0

        return [aLabelTextString sizeWithFont:aLabelFont
                            constrainedToSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT)
                                lineBreakMode:NSLineBreakByWordWrapping];
    }
    else if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(iOS7_0)) {
        //version >= 7.0

        //Return the calculated size of the Label
        return [aLabelTextString boundingRectWithSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT)
                                              options:NSStringDrawingUsesLineFragmentOrigin
                                           attributes:@{
                                                        NSFontAttributeName : aLabelFont
                                                        }
                                              context:nil].size;

    }

    return [self bounds].size;

}
@end
Vijay-Apple-Dev.blogspot.com
quelle
aber wie findet man die Anzahl der Zeilen für das Etikett?
Hitesh
1
Ich hatte in der ersten Codesequenz angegeben. Bitte werfen Sie einen Blick auf "// 16 ist Schriftgröße". Wenn sich die Schriftgröße unterscheidet, sollte die Größe des Etiketts größer sein. Große Dinge benötigen viel Platz in einem Raum !!!
Vijay-Apple-Dev.blogspot.com
@AppleVijay Entschuldigung, ich sehe trotzdem nicht aus, dank eines perfekten Codes, den ich nur für +1 hatte.
Dhara
1
@ RayofHope Gerne helfen :)
Vijay-Apple-Dev.blogspot.com
3
Diese Methode ist veraltet. Verwenden Sie stattdessen boundingRectWithSize: Optionen: Attribute: Kontext
Abdul Yasin
56

Der Aufruf -sizeToFitauf UILabel Instanz wird es automatisch die Größe Text , um ihn Displays zu passen, nicht erforderlich zu berechnen. Wenn Sie die Größe benötigen, können Sie sie anschließend über die Rahmeneigenschaft des Etiketts abrufen.

label.numberOfLines = 0; // allows label to have as many lines as needed
label.text = @"some long text";
[label sizeToFit];
NSLog(@"Label's frame is: %@", NSStringFromCGRect(label.frame));
Filip Radelic
quelle
Die Breite wird jedoch nicht eingeschränkt, um das Layout der Benutzeroberfläche zu verbessern.
Shiami
2
@shiami es beschränkt die Breite auf den aktuellen Wert, der vor dem Aufruf von -sizeToFit
Filip Radelic
Funktioniert für mich, muss aber versucht werden, die Höhe im Etikett einzuschränken, muss Major oder gleich setzen.
Overallduka
1
Es spart viel Zeit, wenn Sie sich an die Eigenschaft .numberOfLines = 0 erinnern! Vielen Dank!
CarmenA
1
Außerdem können Sie anrufen müssen sizeToFitin viewDidLayoutSubviewsstatt viewDidLoad.
Chase Roberts
34

Zusammenfassend können Sie die Höhe eines Etiketts mithilfe seiner Zeichenfolge und seines Aufrufs berechnen boundingRectWithSize. Sie müssen das fontals Attribut angeben und .usesLineFragmentOriginfür mehrzeilige Beschriftungen einschließen .

let labelWidth = label.frame.width
let maxLabelSize = CGSize(width: labelWidth, height: CGFloat.greatestFiniteMagnitude)
let actualLabelSize = label.text!.boundingRect(with: maxLabelSize, options: [.usesLineFragmentOrigin], attributes: [.font: label.font], context: nil)
let labelHeight = actualLabelSize.height(withWidth:labelWidth)

Einige Erweiterungen, um genau das zu tun:

Schnelle Version:

extension UILabel {
    func textHeight(withWidth width: CGFloat) -> CGFloat {
        guard let text = text else {
            return 0
        }
        return text.height(withWidth: width, font: font)
    }

    func attributedTextHeight(withWidth width: CGFloat) -> CGFloat {
        guard let attributedText = attributedText else {
            return 0
        }
        return attributedText.height(withWidth: width)
    }
}

extension String {
    func height(withWidth width: CGFloat, font: UIFont) -> CGFloat {
        let maxSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
        let actualSize = self.boundingRect(with: maxSize, options: [.usesLineFragmentOrigin], attributes: [.font : font], context: nil)
        return actualSize.height
    }
}

extension NSAttributedString {
    func height(withWidth width: CGFloat) -> CGFloat {
        let maxSize = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
        let actualSize = boundingRect(with: maxSize, options: [.usesLineFragmentOrigin], context: nil)
        return actualSize.height
    }
}

Objective-C-Version:

UILabel + Utility.h

#import <UIKit/UIKit.h>
@interface UILabel (Utility)
- (CGFloat)textHeightForWidth:(CGFloat)width;
- (CGFloat)attributedTextHeightForWidth:(CGFloat)width;
@end

UILabel + Utility.m

@implementation NSString (Utility)
- (CGFloat)heightForWidth:(CGFloat)width font:(UIFont *)font {
    CGSize maxSize = CGSizeMake(width, CGFLOAT_MAX);
    CGSize actualSize = [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil].size;
    return actualSize.height;
}
@end

@implementation NSAttributedString (Utility)
- (CGFloat)heightForWidth:(CGFloat)width {
    CGSize maxSize = CGSizeMake(width, CGFLOAT_MAX);
    CGSize actualSize = [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
    return actualSize.height;
}
@end

@implementation UILabel (Utility)
- (CGFloat)textHeightForWidth:(CGFloat)width {
    return [self.text heightForWidth:width font:self.font];
}
- (CGFloat)attributedTextHeightForWidth:(CGFloat)width {
    return [self.attributedText heightForWidth:width];
}
@end
kgaidis
quelle
@RoduckNickes Ohne den Code zu vertiefen, könnte ich sagen, dass es eine gute Chance gibt, dass der Rahmen / die Grenzen der Beschriftungen zum Zeitpunkt des Messens nicht korrekt sind. Wenn Sie es dort messen müssen, können Sie möglicherweise einen layoutSubviews-Aufruf erzwingen, indem Sie setNeedsLayout und layoutIfNeeded aufrufen.
kgaidis
@kgaidis könnte dies so geändert werden, dass der Text eines Etiketts, wenn er \ n enthält, immer noch korrekt berechnet werden kann?
user2363025
Dies funktioniert nicht, wenn uilabel eine niedrigere Höhenkonstante hat, als der tatsächliche Text stattfinden kann
Ucdemir
17

Die aktuelle Lösung ist ab iOS 7 veraltet.

Hier ist eine aktualisierte Lösung:

+ (CGFloat)heightOfCellWithIngredientLine:(NSString *)ingredientLine
                       withSuperviewWidth:(CGFloat)superviewWidth
{
    CGFloat labelWidth                  = superviewWidth - 30.0f;
    //    use the known label width with a maximum height of 100 points
    CGSize labelContraints              = CGSizeMake(labelWidth, 100.0f);

    NSStringDrawingContext *context     = [[NSStringDrawingContext alloc] init];

    CGRect labelRect                    = [ingredientLine boundingRectWithSize:labelContraints
                                                        options:NSStringDrawingUsesLineFragmentOrigin
                                                     attributes:nil
                                                        context:context];

    //    return the calculated required height of the cell considering the label
    return labelRect.size.height;
}

Der Grund, warum meine Lösung so eingerichtet ist, liegt darin, dass ich eine UITableViewCell verwende und die Größe der Zelle dynamisch relativ zu dem Platz ändere, den das Etikett einnimmt.

Unendlichkeit James
quelle
3
anstatt 'nil' pass '@ {NSFontAttributeName: label.font}' als Attribute zu
Salman Zaidi
Die Methode boundingRectWithSize: options: attribute: context: ist nur in iOS 7 und höher verfügbar.
Unendlichkeit James
8

Ohne sizeToFit aufzurufen, können Sie dies alles numerisch mit einer Plug-and-Play-Lösung tun:

+ (CGFloat)heightForText:(NSString*)text font:(UIFont*)font withinWidth:(CGFloat)width {
    CGSize size = [text sizeWithAttributes:@{NSFontAttributeName:font}];
    CGFloat area = size.height * size.width;
    CGFloat height = roundf(area / width);
    return ceilf(height / font.lineHeight) * font.lineHeight;
}

Ich benutze es häufig für UITableViewCells, denen dynamisch Höhen zugewiesen wurden.

Löst auch das Attributproblem @Salman Zaidi.

AlexKoren
quelle
Funktioniert nicht mit NSMutableAttributedString und ohne den Aufruf von sizeToFit, gibt es immer die gleiche Höhe.
Bruno
Getestet mit Plain UILabel Schriftart: Apple SD Gothic Neo. Funktioniert nicht
C0D3
6

Kopieren Sie diese Methode, fügen Sie sie ein und verwenden Sie sie wie folgt:

[lblText setFrame:CGRectMake(lblText.frame.origin.x, lblText.frame.origin.y, width, [self getLabelHeight:lblText])];

 - (CGFloat)getLabelHeight:(UILabel*)label
    {
        CGSize constraint = CGSizeMake(label.frame.size.width, CGFLOAT_MAX);
        CGSize size;

        NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
        CGSize boundingBox = [label.text boundingRectWithSize:constraint
                                                      options:NSStringDrawingUsesLineFragmentOrigin
                                                   attributes:@{NSFontAttributeName:label.font}
                                                      context:context].size;

        size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));

        return size.height;
    }
Saraman
quelle
Hallo @saraman, wenn Sie den Code kopieren und einfügen, geben Sie bitte auch die Quelle des Codes an.
Anup Gupta
5
CGSize maxSize = CGSizeMake(lbl.frame.size.width, CGFLOAT_MAX);
CGSize requiredSize = [lbl sizeThatFits:maxSize];
CGFloat height=requiredSize.height
ash999
quelle
2

Verwenden Sie die folgende Funktion, um die Höhe für den NSAttributedString zu ermitteln . Where width - Die Breite Ihres UILabel oder UITextView

func getHeight(for attributedString: NSAttributedString, font: UIFont, width: CGFloat) -> CGFloat {
    let textStorage = NSTextStorage(attributedString: attributedString)
    let textContainter = NSTextContainer(size: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude))
    let layoutManager = NSLayoutManager()
    layoutManager.addTextContainer(textContainter)
    textStorage.addLayoutManager(layoutManager)
    textStorage.addAttribute(NSAttributedString.Key.font, value: font, range: NSMakeRange(0, textStorage.length))
    textContainter.lineFragmentPadding = 0.0
    layoutManager.glyphRange(for: textContainter)
    return layoutManager.usedRect(for: textContainter).size.height
}

Um die Höhe für String zu ermitteln, verwenden Sie diese Funktion. Sie ist fast identisch mit der vorherigen Methode:

func getHeight(for string: String, font: UIFont, width: CGFloat) -> CGFloat {
    let textStorage = NSTextStorage(string: string)
    let textContainter = NSTextContainer(size: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude))
    let layoutManager = NSLayoutManager()
    layoutManager.addTextContainer(textContainter)
    textStorage.addLayoutManager(layoutManager)
    textStorage.addAttribute(NSAttributedString.Key.font, value: font, range: NSMakeRange(0, textStorage.length))
    textContainter.lineFragmentPadding = 0.0
    layoutManager.glyphRange(for: textContainter)
    return layoutManager.usedRect(for: textContainter).size.height
}
Kiryl Belasheuski
quelle
1

Sie müssen eine Erweiterung von String erstellen und diese Methode aufrufen

func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
    let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
    let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
    return ceil(boundingBox.height)
}

Sie müssen die Breite Ihres Etiketts senden

DURGESH
quelle
0

In meinem Fall habe ich für jeden Abschnitt einen Header mit fester Größe verwendet, in jedem Header jedoch eine dynamische Zellengröße . Die Höhe der Zelle hängt von der Höhe des Etiketts ab.

Arbeiten mit:

tableView.estimatedRowHeight = SomeNumber
tableView.rowHeight = UITableViewAutomaticDimension

Funktioniert aber bei Verwendung von:

tableView.reloadSections(IndexSet(integer: sender.tag) , with: .automatic)

Wenn viele Header nicht reduziert werden, entstehen viele Fehler, wie z. B. Header-Duplizierung (Header-Typ x unter demselben Typ) und seltsame Animationen, wenn das Framework mit Animation neu geladen wird, selbst wenn mit Typ .none(FYI, feste Header-Höhe und Zellenhöhe funktioniert).

Die Lösung besteht darin, heightForRowAtRückruf zu verwenden und die Höhe des Etiketts selbst zu berechnen (und die Animation sieht viel besser aus). Denken Sie daran, dass die Höhe zuerst aufgerufen wird .

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
    let object = dataDetailsController.getRowObject(forIndexPath: indexPath)
    let label = UILabel(frame: tableView.frame)
    let font = UIFont(name: "HelveticaNeue-Bold", size: 25)
    label.text = object?.name
    label.font = font
    label.numberOfLines = 0
    label.textAlignment = .center
    label.sizeToFit()
    let size = label.frame.height
    return Float(size) == 0 ? 34 : size
}
OhadM
quelle
0

Dies ist die Erweiterung, die ich für die Berechnung mehrzeiliger UILabel-Höhen verwende. Es handelt sich um ein angepasstes Snippet aus einem früheren Stapelüberlaufpfosten:

extension UILabel {
    func estimatedHeight(forWidth: CGFloat, text: String, ofSize: CGFloat) -> CGFloat {

        let size = CGSize(width: forWidth, height: CGFloat(MAXFLOAT))

        let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)

        let attributes = [NSFontAttributeName: UIFont.systemFont(ofSize: ofSize)]

        let rectangleHeight = String(text).boundingRect(with: size, options: options, attributes: attributes, context: nil).height

        return ceil(rectangleHeight)
    }
}
Sasa Blagojevic
quelle
0

Wenn Sie ein UILabel mit Attributen verwenden, können Sie die Methode ausprobieren textRect(forBounds:limitedToNumberOfLines).

Dies ist mein Beispiel:

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
label.numberOfLines = 0
label.text = "Learn how to use RxSwift and RxCocoa to write applications that can react to changes in your underlying data without you telling it to do so."

let rectOfLabel = label.textRect(forBounds: CGRect(x: 0, y: 0, width: 100, height: CGFloat.greatestFiniteMagnitude), limitedToNumberOfLines: 0)
let rectOfLabelOneLine = label.textRect(forBounds: CGRect(x: 0, y: 0, width: 100, height: CGFloat.greatestFiniteMagnitude), limitedToNumberOfLines: 1)
let heightOfLabel = rectOfLabel.height
let heightOfLine = rectOfLabelOneLine.height
let numberOfLines = Int(heightOfLabel / heightOfLine)

Und meine Ergebnisse auf dem Spielplatz:

Geben Sie hier die Bildbeschreibung ein

Tuan Nguyen
quelle
-1

Um UILabel an den dynamischen Inhalt anzupassen, können Sie die Eigenschaft lines im Eigenschafteninspektor verwenden und auf 0 setzen.

Und Sie müssen dafür keine Codierung vornehmen.

Weitere Details finden Sie im folgenden Demonstrationsvideo

https://www.youtube.com/watch?v=rVeDS_OGslU

Juber Khan
quelle
-2

Wenn Sie möchten, dass die Beschriftung dynamische Linien annimmt, können Sie diese verwenden

label.numberOfLines = 0; // allows label to have as many lines as needed
label.text = @"some long text ";
[label sizeToFit];
NSLog(@"Label's frame is: %@", NSStringFromCGRect(label.frame));
Amr wütend
quelle