Замена на устаревший размерWithFont: в iOS 7?

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

Ответ 1

Используйте 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));

Ответ 2

Я считаю, что функция устарела, потому что эта серия функций 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);

Ответ 3

Как вы можете видеть sizeWithFont на сайте Apple Developer, он устарел, поэтому нам нужно использовать 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]}];
}

Ответ 4

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

#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:, и вам будет удобно идти.

Ответ 5

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

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

Ответ 6

Многострочные метки, использующие динамическую высоту, могут потребовать дополнительной информации, чтобы правильно установить размер. Вы можете использовать атрибуты 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 'adjustSize' или CGRect как свойство rect.size.height, если вы ищете высоту.

Дополнительная информация о NSParagraphStyle здесь: https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.html

Ответ 7

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

Ответ 8

Создайте функцию, которая принимает экземпляр 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;

Ответ 9

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

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

Ответ 10

Основываясь на @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;
}

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

Ответ 11

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

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

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

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

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

Ответ 12

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

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

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

enter image description here

ВИДЕО: https://youtu.be/Sz3XfCsSb6k

Ответ 13

boundingRectWithSize:options:attributes:context:

Ответ 14

Принятый ответ в Xamarin будет (используйте sizeWithAttributes и UITextAttributeFont):

        UIStringAttributes attributes = new UIStringAttributes
        { 
            Font = UIFont.SystemFontOfSize(17) 
        }; 
        var size = text.GetSizeUsingAttributes(attributes);

Ответ 15

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

Ответ 16

Вот эквивалент monotouch, если кому-то это нужно:

/// <summary>
/// Measures the height of the string for the given width.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="width">The width.</param>
/// <param name="padding">The padding.</param>
/// <returns></returns>
public static float MeasureStringHeightForWidth(this string text, UIFont font, float width, float padding = 20)
{
    NSAttributedString attributedString = new NSAttributedString(text, new UIStringAttributes() { Font = font });
    RectangleF rect = attributedString.GetBoundingRect(new SizeF(width, float.MaxValue), NSStringDrawingOptions.UsesLineFragmentOrigin, null);
    return rect.Height + padding;
}

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

public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
    //Elements is a string array
    return Elements[indexPath.Row].MeasureStringHeightForWidth(UIFont.SystemFontOfSize(UIFont.LabelFontSize), tableView.Frame.Size.Width - 15 - 30 - 15);
}

Ответ 17

CGSize maximumLabelSize = CGSizeMake(label.frame.size.width, FLT_MAX);
CGSize expectedLabelSize = [label sizeThatFits:maximumLabelSize];
float heightUse = expectedLabelSize.height;

Ответ 18

Попробуйте этот синтаксис:

NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text 
                                    attributes:@{NSFontAttributeName: font}];

Ответ 19

Как ответ @Ayush:

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

Хорошо, предположим, что в 2019+ вы, вероятно, используете Swift и String вместо Objective-c и NSString, здесь правильным способом является получение размера String с предопределенным шрифтом:

let stringSize = NSString(string: label.text!).size(withAttributes: [.font : UIFont(name: "OpenSans-Regular", size: 15)!])

Ответ 20

Ничего из этого не сработало для меня в ios 7. Вот что я в итоге сделал. Я поместил это в свой собственный класс ячеек и вызвал метод в моем методе heightForCellAtIndexPath.

Моя ячейка похожа на ячейку описания при просмотре приложения в хранилище приложений.

Сначала в раскадровке установите метку "attributedText", установите количество строк в 0 (что автоматически изменит размер метки (только ios 6+)) и установите для нее перенос слов.

Затем я просто добавляю все высоты содержимого ячейки в свой собственный класс ячеек. В моем случае у меня есть ярлык наверху, который всегда говорит "Описание" (_descriptionHeadingLabel), меньшая метка, которая является переменной по размеру, которая содержит фактическое описание (_descriptionLabel) ограничение от вершины ячейки до заголовка (_descriptionHeadingLabelTopConstraint), Я также добавил 3 к пространству снизу немного (примерно столько же, сколько места яблока в ячейке типа субтитров.)

- (CGFloat)calculateHeight
{
    CGFloat width = _descriptionLabel.frame.size.width;
    NSAttributedString *attributedText = _descriptionLabel.attributedText;
    CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX} options: NSStringDrawingUsesLineFragmentOrigin context:nil];

    return rect.size.height + _descriptionHeadingLabel.frame.size.height + _descriptionHeadingLabelTopConstraint.constant + 3;
}

И в моем делегате Table View:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    if (indexPath.row == 0) {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"descriptionCell"];
        DescriptionCell *descriptionCell = (DescriptionCell *)cell;
        NSString *text = [_event objectForKey:@"description"];
        descriptionCell.descriptionLabel.text = text;

        return [descriptionCell calculateHeight];
    }

    return 44.0f;
}

Вы можете изменить оператор if как немного "умнее" и фактически получить идентификатор ячейки из какого-то источника данных. В моем случае ячейки будут жестко закодированы, так как будет зафиксировано их количество в определенном порядке.