Замена устаревшего sizeWithFont: в iOS 7?

в iOS 7, sizeWithFont: теперь устарел. Как теперь передать объект UIFont в метод замены sizeWithAttributes:?

12 ответов


использовать sizeWithAttributes: вместо этого, который сейчас занимает NSDictionary. Пройдите в паре с ключом UITextAttributeFont и ваш объект шрифта такой:

CGSize size = [string sizeWithAttributes:
    @{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];

// Values are fractional -- you should take the ceilf to get equivalent values
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));

я считаю, что функция была устаревшей, потому что эта серия NSString+UIKit функции (sizewithFont:... и т. д.) были основаны на UIStringDrawing библиотека, которая не была потокобезопасной. Если вы пытались запустить их не в главном потоке (как и любой другой UIKit функциональность), вы получите непредсказуемое поведение. В частности, если вы запустили функцию одновременно в нескольких потоках, это, вероятно, приведет к сбою вашего приложения. Вот почему в iOS 6 они представили boundingRectWithSize:... метод NSAttributedString. Это было построено на вершина NSStringDrawing библиотеки и потокобезопасны.

если вы посмотрите на новые NSString boundingRectWithSize:... функция, она запрашивает массив атрибутов таким же образом, как NSAttributeString. Если бы мне пришлось угадать, это новое NSString функция в iOS 7-это просто оболочка для NSAttributeString функция от iOS 6.

на этой ноте, если бы вы поддерживали только iOS 6 и iOS 7, то я бы определенно изменил все ваши NSString sizeWithFont:... до NSAttributeString boundingRectWithSize. Это сэкономит вам много головной боли, если у вас есть странный многопоточный угловой корпус! Вот как я конвертировал NSString sizeWithFont:constrainedToSize::

что было:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font 
               constrainedToSize:(CGSize){width, CGFLOAT_MAX}];

можно заменить на:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text 
                                    attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
CGSize size = rect.size;

обратите внимание, что в документации упоминается:

в iOS 7 и более поздних версиях этот метод возвращает дробные размеры (в размере компонент возвращаемого CGRect); использовать возвращенный размер в размер взгляды, вы должны использовать поднять его значение до ближайшего более высокого целого использование функции ceil.

поэтому, чтобы вытащить расчетную высоту или ширину, которые будут использоваться для калибровки видов, я бы использовал:

CGFloat height = ceilf(size.height);
CGFloat width  = ceilf(size.width);

Как видите,sizeWithFont на сайте разработчика Apple он устарел, поэтому нам нужно использовать sizeWithAttributes.

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

NSString *text = @"Hello iOS 7.0";
if (SYSTEM_VERSION_LESS_THAN(@"7.0")) {
    // code here for iOS 5.0,6.0 and so on
    CGSize fontSize = [text sizeWithFont:[UIFont fontWithName:@"Helvetica" 
                                                         size:12]];
} else {
    // code here for iOS 7.0
   CGSize fontSize = [text sizeWithAttributes: 
                            @{NSFontAttributeName: 
                              [UIFont fontWithName:@"Helvetica" size:12]}];
}

Я создал категорию для решения этой проблемы, вот она:

#import "NSString+StringSizeWithFont.h"

@implementation NSString (StringSizeWithFont)

- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
    if ([self respondsToSelector:@selector(sizeWithAttributes:)])
    {
        NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
        return ([self sizeWithAttributes:attribs]);
    }
    return ([self sizeWithFont:fontToUse]);
}

таким образом, вам нужно только найти / заменить sizeWithFont: С sizeWithMyFont: и вы хорошо идти.


в iOS7 мне нужна логика для возврата правильной высоты для метода tableview:heightForRowAtIndexPath, но sizeWithAttributes всегда возвращает одинаковую высоту независимо от длины строки, потому что он не знает, что он будет помещен в ячейку таблицы фиксированной ширины. Я нашел, что это отлично работает для меня и вычисляет правильную высоту с учетом ширины ячейки таблицы! Это основано на ответе г-на т.

NSString *text = @"The text that I want to wrap in a table cell."

CGFloat width = tableView.frame.size.width - 15 - 30 - 15;  //tableView width - left border width - accessory indicator - right border width
UIFont *font = [UIFont systemFontOfSize:17];
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
CGSize size = rect.size;
size.height = ceilf(size.height);
size.width  = ceilf(size.width);
return size.height + 15;  //Add a little more padding for big thumbs and the detailText label

многострочные метки с использованием динамической высоты могут потребовать дополнительной информации для правильной установки размера. Вы можете использовать sizeWithAttributes с UIFont и NSParagraphStyle для указания как шрифта, так и режима разрыва строки.

вы должны определить стиль абзаца и использовать NSDictionary следующим образом:

// set paragraph style
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setLineBreakMode:NSLineBreakByWordWrapping];
// make dictionary of attributes with paragraph style
NSDictionary *sizeAttributes        = @{NSFontAttributeName:myLabel.font, NSParagraphStyleAttributeName: style};
// get the CGSize
CGSize adjustedSize = CGSizeMake(label.frame.size.width, CGFLOAT_MAX);

// alternatively you can also get a CGRect to determine height
CGRect rect = [myLabel.text boundingRectWithSize:adjustedSize
                                                         options:NSStringDrawingUsesLineFragmentOrigin
                                                      attributes:sizeAttributes
                                                         context:nil];

вы можете использовать CGSize 'adjustedSize' или CGRect как rect.размер.свойство height, если вы ищете на высоту.

подробнее о NSParagraphStyle здесь: https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.html


// max size constraint
CGSize maximumLabelSize = CGSizeMake(184, FLT_MAX)

// font
UIFont *font = [UIFont fontWithName:TRADE_GOTHIC_REGULAR size:20.0f];

// set paragraph style
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;

// dictionary of attributes
NSDictionary *attributes = @{NSFontAttributeName:font,
                             NSParagraphStyleAttributeName: paragraphStyle.copy};

CGRect textRect = [string boundingRectWithSize: maximumLabelSize
                                     options:NSStringDrawingUsesLineFragmentOrigin
                                  attributes:attributes
                                     context:nil];

CGSize expectedLabelSize = CGSizeMake(ceil(textRect.size.width), ceil(textRect.size.height));

создайте функцию, которая принимает экземпляр UILabel. и возвращает CGSize

CGSize constraint = CGSizeMake(label.frame.size.width , 2000.0);
// Adjust according to requirement

CGSize size;
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0){

    NSRange range = NSMakeRange(0, [label.attributedText length]);

    NSDictionary *attributes = [label.attributedText attributesAtIndex:0 effectiveRange:&range];
    CGSize boundingBox = [label.text boundingRectWithSize:constraint options: NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;

    size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
}
else{
    size = [label.text sizeWithFont:label.font constrainedToSize:constraint lineBreakMode:label.lineBreakMode];
}

return size;

альтернативное решение-

CGSize expectedLabelSize;
if ([subTitle respondsToSelector:@selector(sizeWithAttributes:)])
{
    expectedLabelSize = [subTitle sizeWithAttributes:@{NSFontAttributeName:subTitleLabel.font}];
}else{
    expectedLabelSize = [subTitle sizeWithFont:subTitleLabel.font constrainedToSize:subTitleLabel.frame.size lineBreakMode:NSLineBreakByWordWrapping];
}

построение на @bitsand, это новый метод, который я только что добавил в свою категорию NSString + Extras:

- (CGRect) boundingRectWithFont:(UIFont *) font constrainedToSize:(CGSize) constraintSize lineBreakMode:(NSLineBreakMode) lineBreakMode;
{
    // set paragraph style
    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    [style setLineBreakMode:lineBreakMode];

    // make dictionary of attributes with paragraph style
    NSDictionary *sizeAttributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName: style};

    CGRect frame = [self boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:sizeAttributes context:nil];

    /*
    // OLD
    CGSize stringSize = [self sizeWithFont:font
                              constrainedToSize:constraintSize
                                  lineBreakMode:lineBreakMode];
    // OLD
    */

    return frame;
}

Я просто использую размер результирующего кадра.


вы все еще можете использовать sizeWithFont. но в iOS >= 7.0 метод вызывает сбой, если строка содержит начальные и конечные пробелы или конечные строки \n.

обрезка текста перед его использованием

label.text = [label.text stringByTrimmingCharactersInSet:
             [NSCharacterSet whitespaceAndNewlineCharacterSet]];

это также может относиться к sizeWithAttributes и [label sizeToFit].

кроме того, всякий раз, когда у вас есть nsstringdrawingtextstorage message sent to deallocated instance в устройстве iOS 7.0 он имеет дело с этим.


лучше использовать автоматические размеры (Swift):

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

NB: 1. Прототип UITableViewCell должен быть правильно спроектирован (например, не забудьте установить UILabel.numberOfLines = 0 etc) 2. Удалить метод HeightForRowAtIndexPath

enter image description here

видео: https://youtu.be/Sz3XfCsSb6k