Jak obliczyć wysokość UILabel dynamicznie?

Chcę obliczyć liczbę linii i wysokość UILabel dynamicznie z podanego tekstu dla tego samego.

Author: Filip Radelic, 2011-08-24

12 answers

Spróbuj tego

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

Lub

int lines = labelHeight/16;

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

Lub

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

Używając Categories, po prostu utwórz klasę category o nazwie

UILabel+UILabelDynamicHeight.h

UILabel+UILabelDynamicHeight.m

Koniec z napięciem związanym z obliczaniem wysokości. Zapoznaj się z poniższą realizacją.

Aktualizacje dla iOS7 i powyżej, iOS 7 poniżej: dynamicznie Oblicz UILabel wysokość

#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
 81
Author: Vijay-Apple-Dev.blogspot.com,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-30 05:48:05

Wywołanie -sizeToFit na instancji UILabel automatycznie zmieni jej rozmiar tak, aby pasował do wyświetlanego tekstu, bez konieczności obliczania. Jeśli potrzebujesz rozmiaru, możesz go pobrać z właściwości frame label.

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));
 49
Author: Filip Radelic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-24 10:38:08

Podsumowując, możesz obliczyć Wysokość etykiety, używając jej ciągu znaków i wywołując boundingRectWithSize. Musisz podać font jako atrybut i dołączyć .usesLineFragmentOrigin dla etykiet wielowierszowych.

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

Niektóre rozszerzenia do tego:

Wersja Swift:

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-Wersja C:

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
 27
Author: kgaidis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-05 00:04:21

[1]}obecne rozwiązanie zostało wycofane z systemu iOS 7.

Oto zaktualizowane rozwiązanie:

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

Powodem, dla którego moje rozwiązanie jest tak skonfigurowane, jest to, że używam UITableViewCell i dynamicznie zmieniam rozmiar komórki w stosunku do tego, ile miejsca zajmie Etykieta.

 17
Author: Infinity James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-30 23:34:26

Bez wywoływania sizeToFit, możesz to wszystko zrobić numerycznie za pomocą rozwiązania plug and play:

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

Używam go często dla UITableViewCells, które mają dynamicznie przydzielane wysokości.

Rozwiązuje również problem z atrybutami @Salman Zaidi.

 7
Author: AlexKoren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-17 01:14:12

Skopiuj i wklej tę metodę i użyj jej w następujący sposób:

[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;
    }
 5
Author: saraman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-22 13:14:43
CGSize maxSize = CGSizeMake(lbl.frame.size.width, CGFLOAT_MAX);
CGSize requiredSize = [lbl sizeThatFits:maxSize];
CGFloat height=requiredSize.height
 4
Author: ash999,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-27 13:17:34

Swift 4 Aby uzyskać wysokość dla NSAttributedString użyj poniższej funkcji. Gdzie width - Szerokość twojego UILabel lub 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(NSAttributedStringKey.font, value: font, range: NSMakeRange(0, textStorage.length))
    textContainter.lineFragmentPadding = 0.0
    layoutManager.glyphRange(for: textContainter)
    return layoutManager.usedRect(for: textContainter).size.height
}

Aby uzyskać wysokość dla String Użyj tej funkcji, jest ona prawie identyczna jak poprzednia metoda:

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(NSAttributedStringKey.font, value: font, range: NSMakeRange(0, textStorage.length))
    textContainter.lineFragmentPadding = 0.0
    layoutManager.glyphRange(for: textContainter)
    return layoutManager.usedRect(for: textContainter).size.height
}
 1
Author: Malder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-12 15:57:17

W moim przypadku używałem nagłówka o stałym rozmiarze dla każdej sekcji, ale z dynamicznym rozmiarem komórki w każdym nagłówku. Wysokość komórki zależy od wysokości etykiety.

Praca z:

tableView.estimatedRowHeight = SomeNumber
tableView.rowHeight = UITableViewAutomaticDimension

Działa ale przy użyciu:

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

Gdy wiele nagłówków nie jest zwiniętych, tworzy wiele błędów, takich jak powielanie nagłówków (typ nagłówka x poniżej tego samego typu) i dziwne animacje, gdy framework przeładowuje się animacją, nawet gdy używa z typem .none (FYI, stała wysokość nagłówka i wysokość komórki działa).

Rozwiązaniem jest wykorzystanie heightForRowAt callback i obliczenie wysokości etykiety przez siebie (Plus animacja wygląda dużo lepiej). Pamiętaj, że wysokość jest nazywana jako pierwsza .

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
}
 0
Author: OhadM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-20 10:27:54

Jest to rozszerzenie, którego używam do obliczania wysokości wieloliniowych UILabel, jest to skorygowany fragment z poprzedniego postu o przepełnieniu stosu:

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)
    }
}
 0
Author: Sasa Blagojevic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-30 10:52:00

Musisz utworzyć rozszerzenie String i wywołać tę metodę

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

Musisz wysłać szerokość etykiety

 0
Author: DURGESH Chaurasiya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-15 08:22:26

Jeśli chcesz, aby etykieta miała dynamiczne linie, możesz użyć tego

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));
 -2
Author: Amr Angry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-12 02:59:36