Заміна застарілого розміруWithFont: в iOS 7?


Відповіді:


521

Використовуйте 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));

60
під час роботи з a NSStringі UILabel(не завжди, але це часто так), щоб запобігти дублюванню коду / тощо, ви також можете замінити [UIFont systemFontOfSize:17.0f]на label.font- допомагає технічному обслуговуванню коду шляхом посилання на наявні дані порівняно з тим, що ви вводите їх кілька разів або посилаєтесь на константи по всьому місце тощо
toblerpwn

8
@toblerpwn можливо, мітка не існує, і ви намагаєтеся обчислити теоретичну мітку.
ботбот

5
Як я використовую цей приклад, щоб отримати size.height з міткою, яка має фіксовану ширину, наприклад, 250? АБО якщо її мітка з автоматним відьом займає відрізок ширини, і я переходжу до пейзажу.
Pedroinpeace

12
@Pedroinpeace Ви б використовували boundingRectWithSize:options:attributes:context:натомість, пропускаючи CGSizeMake(250.0f, CGFLOAT_MAX)в більшості випадків.
Джеймс Куанг

9
Ще щось, що слід зазначити, розмірWithAttributes: не 100% еквівалент. sizeWithFont використовується для округлення розмірів до цілих (піксельних) значень. Запропонуйте скористатися верхнім шаром на отриманій висоті / ширині, або у вас можуть з’явитися розмиті артефакти (esp non retina HW), якщо ви використовуєте це для позиційних розрахунків.
Нік H247

172

Я вважаю, що функція була застарілою, оскільки серія NSString+UIKitфункцій ( sizewithFont:...тощо) базувалася на UIStringDrawingбібліотеці, яка не була безпечною для потоків. Якщо ви спробували запустити їх не на основній темі (як і будь-який інший UIKitфункціонал), ви отримаєте непередбачувані поведінки. Зокрема, якщо функцію запустити одночасно на декількох потоках, вона, ймовірно, завершить роботу вашого додатка. Ось чому в iOS 6 вони ввели boundingRectWithSize:...метод для NSAttributedString. Це було створено на вершині NSStringDrawingбібліотек і є безпечним для потоків.

Якщо ви подивитеся на нову NSString boundingRectWithSize:...функцію, вона запитує масив атрибутів так само, як і a 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);

В iOS 6.0 застарілий limitingRectWithSize застарілий.
Нірав

2
@Nirav Я не бачу згадок про депресію. Чи можете ви вказати мені, де сказано, що воно застаріле? Дякую! ( developer.apple.com/library/ios/documentation/uikit/reference/… )
Містер T

2
Можливо, @Nirav означав, що він не доступний для NSString в iOS 6 (про що побічно згадується у відповіді)?
newenglander

1
@VagueExplanation Я просто спробував, що і обмеженняRectForSize досі не застаріло для NSAttributedString. Це також не говорить про застаріле в документації.
Містер T

5
Фантастичний для згадки про використання ceilf (). Ніде більше не говорилося про це, і мій розмір завжди був трохи занадто малий. Дякую!
Джонатан Браун

29

Як ви можете бачити 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]}];
}

17
В цьому випадку краще використовувати [NSObject respondsToSelector:]метод , як тут: stackoverflow.com/a/3863039/1226304
derpoliuk

16

Я створив категорію для вирішення цієї проблеми, ось вона:

#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:і ви добре йти.


1
хоча це створить попередження компілятора на ios7 + для посилання sizeWithFont, або попередження / помилка компіляції на <ios7 для посилання sizeWithAttributes! Можливо, найкраще використовувати макрос замість перевірки respondsToSelector - якщо потрібно. Але оскільки ви більше не можете доставити яблуко з ioS6 SDK ... це, мабуть, не так!
Нік H247

Додайте це до придушення попередження #pragma GCC діагностики проігноровано "-Зазначені декларації"
Ryan Heitner

10

У iOS7 мені була потрібна логіка, щоб повернути правильну висоту для перегляду таблиці: heightForRowAtIndexPath, але розмірWithAttributes завжди повертає ту саму висоту незалежно від довжини рядка, оскільки він не знає, що він буде розміщений у комірці таблиці фіксованої ширини . Я виявив, що це для мене чудово працює і обчислює правильну висоту, враховуючи ширину комірки таблиці! Це ґрунтується на відповіді пана Т. вище.

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

7

Багаторядкові мітки, що використовують динамічну висоту, можуть потребувати додаткової інформації для правильного встановлення розміру. Ви можете використовувати 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 'customSize' або CGRect як властивість rect.size.height, якщо шукаєте висоти.

Більше інформації про NSParagraphStyle тут: https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.html


1
Схоже, виникає проблема з синтаксисом одразу після коригування розміру.
Кріс Принс

Дякуємо, що
знайшли

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

1
Це врятувало мені години головних болів, дякую Kirit, що для мене було визначено атрибути та словники до блоку CGRect ... і набагато простіше читати.
Азін Мерноош

3

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

Я мій випадок для динамічної висоти рядка, я повністю видалив метод HeightForRow і застосував UITableViewAutomaticDimension. tableView.estimatedRowHeight = 68.0 tableView.rowHeight = UITableViewAutomaticDimension
Євген Брагінець

3

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

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

1
Це призведе до попередження компілятора для обох iOS6 та 7. І буде мати по-різному поведінку для кожного!
Нік H247

3

Спираючись на @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;
}

Я просто використовую розмір отриманого кадру.


2

Ви все ще можете використовувати sizeWithFont. але, в iOS> = 7.0 метод викликає збої, якщо рядок містить провідні та кінцеві пробіли або кінцеві рядки \n.

Обрізання тексту перед його використанням

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

Це також може стосуватися sizeWithAttributesі [label sizeToFit].

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


2

Краще використовувати автоматичні розміри (Swift):

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

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

введіть тут опис зображення

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


Візьмемо статичну висоту комірок, визначену в прототипі осередку прототипу
Kirit Vaghela

Додав ці два рядки і переконався, що мій UILabel встановлений на 0 кількість рядків. Не працювало так, як рекламували. Що ще ти зробив для того, щоб він працював лише з цими двома рядками коду?
Чи буде

1
хм, також вам доведеться закріпити обмеження мітки вгорі та внизу клітинки
Євген Брагінець

1
Ні, цього має бути достатньо. Чи є у вас обмеження для автоматичного розкладу для закріпленої мітки для перегляду?
Євген Брагінець



1

Як відповідь @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)!])

Як ви могли використовувати це для визначення розміру шрифту?
Йосиф Астрахан

@JosephAstrahan це не для визначення розміру шрифту, це для отримання розміру (CGSize) рядка з визначеним шрифтом. Якщо ви хочете отримати розмір шрифту етикетки, ви можете легко скористатися label.font
Romulo BM

використовуючи вашу ідею, я створив спосіб отримати розмір шрифту більш-менш ( stackoverflow.com/questions/61651614 / ... ), label.font б не працював би в моєму випадку з - за деякі складні проблеми , з етикетками бути інтерактивними , тільки якщо точний шрифт відомий, ви можете прочитати про нього на моєму дописі.
Йосиф Астрахан

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

0

Ось одномоточний еквівалент, якщо комусь це потрібно:

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

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


-1

Нічого з цього не працювало для мене в ios 7. Ось, що я закінчив робити. Я поміщую це у свій власний клас комірок і називаю метод в моєму висотіForCellAtIndexPath метод.

Під час перегляду програми в магазині додатків моя клітинка виглядає схожою на комірку для опису.

Спочатку в дошці розкадровки встановіть мітку на "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;
}

І в моєму представнику табличного перегляду:

- (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 на трохи розумніший і фактично отримати ідентифікатор комірки з якогось джерела даних. У моєму випадку комірки будуть жорстко закодовані, оскільки буде визначено їх кількість у певному порядку.


boundingRectWithSizeв ios 9.2 присутні проблеми, відрізняється від результатів до ios <9.2. Ви знайшли чи знаєте будь-який інший найкращий спосіб зробити це.
jose920405
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.