NSAttributedString сообщает о неверных размерах для UITextView sizeThatFits иboundingRectWithSize с правильным набором параметров

У меня есть NSAttributedString, который сообщает о boundingRectWithSize (и, соответственно, о UITextView, который неправильно вычисляет свой sizeThatFits), когда размер шрифта уменьшается по сравнению с размером шрифта, который использовался для его создания.

Это происходит не со всеми NSAttributedString, для которых я выполняю аналогичные операции, поэтому здесь приведены шаги для воспроизведения.

  1. Используйте нестандартный шрифт, который не включает полный набор символов Unicode.
  2. Убедитесь, что строка содержит символы из этого «неподдерживаемого» набора. iOS отобразит их как Helvetica нужного размера.
  3. Уменьшите шрифт во всех атрибутах шрифта в NSAttributedString. Мой код для этого, вызвавший проблему, выглядит следующим образом.

Изнутри подкласса UITextView:

NSMutableAttributedString *mutableString = [self.attributedText mutableCopy];
[mutableString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, mutableString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
    if (value) {
        UIFont *oldFont = (UIFont *)value;
        UIFont *newFont = [oldFont fontWithSize:oldFont.pointSize - 1];
        [mutableString removeAttribute:NSFontAttributeName range:range];
        [mutableString addAttribute:NSFontAttributeName value:newFont range:range];
    }
}];
self.attributedText = [mutableString copy];

Я заметил, что при запуске этого кода в цикле while проверяется sizeThatFits, чтобы знать, когда текст достаточно мал, чтобы вместиться, и в некоторых обстоятельствах у меня будет гонка до нуля. Высота рассчитывается как 60 пикселей для любого значения шрифта, меньшего, чем то, с которого я начал, что составляет 50 пикселей.

При NSLogинге NSAttributedString я обнаружил, что есть несколько атрибутов, которые я не добавил с ключом NSOriginalFont, которого нет в списке поддерживаемых атрибутов здесь. Что происходит с NSOriginalFont? Почему мой размер рассчитывается неправильно?


person Austin Fitzpatrick    schedule 03.04.2014    source источник
comment
Для всех, у кого есть эта проблема, этот атрибут может быть создан другим способом, если вы прочитали атрибутивную строку из UITextView, что в некоторых случаях может вставить атрибут.   -  person Tom Harrington    schedule 28.12.2017


Ответы (5)


В итоге я исправил это, но обнаружил недостаток информации в Интернете об этом, поэтому решил задокументировать свое решение здесь.

Атрибуты NSOriginalFont создаются, когда используемый шрифт не поддерживает один или несколько символов в строке. NSAttributedString добавляет эти атрибуты, которые отслеживают, каким должен был быть шрифт до того, как произошла замена на Helvetica. Я мог бы придумать ситуацию, когда это было бы полезно (шрифт с заглавными буквами, который вы иногда запускаете uppercaseString: on?), но мне это не помогло.

На самом деле это было вредно. Когда я повторял атрибуты, связанные со шрифтом, чтобы уменьшить размер, как показано выше, видимый размер текста уменьшался, но атрибут NSOriginalFont сохранял ссылку на большой размер.

Для NSOriginalFont нет встроенной константы, но если вы вызываете его по имени, его можно удалить из NSMutableAttributedString. Если вы это сделаете, вы начнете получать правильные результаты от sizeThatFits, boundingRectWithSize и других подобных функций, предполагая, что вы передаете правильные параметры.

В итоге я создал простой метод категории в NSMutableAttributedString, приведенный ниже, который хорошо работает.

NSMutableAttributedString+StripOriginalFont.h

@interface NSMutableAttributedString (StripOriginalFont)

- (void) stripOriginalFont;

@end

NSMutableAttributedString+StripOriginalFont.m

@implementation NSMutableAttributedString (StripOriginalFont)

- (void) stripOriginalFont{
    [self enumerateAttribute:@"NSOriginalFont" inRange:NSMakeRange(0, self.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
        if (value){
            [self removeAttribute:@"NSOriginalFont" range:range];
        }
    }];
}

@end

Предположительно, вы могли бы просто изменить его, чтобы он оставался «синхронизированным», вместо того, чтобы полностью удалять его, но мне это не пригодилось для этого конкретного проекта.

person Austin Fitzpatrick    schedule 03.04.2014
comment
Почему бы просто не использовать [self removeAttribute:@NSOriginalFont range:NSMakeRange( 0, self.length )]? - person Peter N Lewis; 05.06.2015

Для зачистки в Swift вы можете использовать это расширение:

extension NSAttributedString {
  func strippedOriginalFont() -> NSAttributedString? {
    let mutableCopy = self.mutableCopy() as? NSMutableAttributedString
    mutableCopy?.removeAttribute(NSAttributedStringKey(rawValue: "NSOriginalFont"), range: NSMakeRange(0, self.length))
    return mutableCopy?.copy() as? NSAttributedString
  }
}
person KIO    schedule 09.07.2018

Не знаю, поможет ли это решить вашу проблему, но ознакомьтесь с моим решением для автоматического изменения размера текста UITextView здесь: https://stackoverflow.com/a/30400391/1664123

person Andy Poes    schedule 22.05.2015

Создайте объект NSTextStorage и инициализируйте его с атрибутомString. и рассчитать границы.

NSTextStorage *attributedText = [[NSTextStorage alloc] initWithAttributedString:[[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName:systemFont}]];
CGRect textRect = [attributedText boundingRectWithSize:CGSizeMake(textW, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil];
person Claire Kim    schedule 24.04.2017

У меня такая же проблема. когда я вызываю textView.setAttributedString(), он автоматически добавит для меня атрибут NSOriginalFont, что приведет к неправильному размеру. Я также использую sizeThatFits для вычисления высоты.

Причина, по которой мы получили неправильный размер, заключается в том, что textView использует NSOriginalFont для расчета размера, который не подходит для измененного NSFont.

Но если мы используем NSTextStorage для создания attributeString и вызываем textView.setAttributedText, то он не добавит NSOriginalFont (не знаю почему, но это решает мою проблему) и вычисление размера даст правильный ответ.

Простой код:

func getAttributedStringForTextView(content: String) -> NSAttributedString {
    var attriString = NSMutableAttributedString(string: content)
    // add attributes here
    ...
    // at last, use an NSTextStorage to wrap the result
    return NSTextStorage(attributedString: attriString)
}

Надеюсь это поможет.

person Ypy    schedule 24.08.2015