Виджет iOS Today с пользовательским шрифтом прекращен из-за ошибки памяти

Виджет «Сегодня»

У нас есть приложение для iOS 8+, в котором используется собственный шрифт. Файл TTF поставляется вместе с приложением.

Сейчас мы находимся в процессе создания виджета «Сегодня» (расширения), который должен использовать тот же пользовательский шрифт, чтобы правильно отображать содержимое.

Сам файл TTF весит 142 КБ.

С моими тестовыми данными будет только 3-4 символа, нарисованные с использованием пользовательского шрифта.

Симптомы

Проблема в том, что использование этого шрифта приводит к тому, что виджет использует слишком много памяти, виджет получает несколько вызовов didReceiveMemoryWarning и затем завершается.

Если вместо этого мы используем шрифт «HelveticaNeue», все работает нормально, но рассматриваемые символы отображаются в соответствии с HelveticaNeue, а не с пользовательским шрифтом.

В симуляторе iOS все работает нормально, но несколько вопросов/ответов Stackoverflow заставляют меня поверить, что это вполне нормально и что механизм защиты памяти iOS сработает только на устройстве.

На iPhone 6 виджет убивается при использовании памяти около 20 МБ, поэтому мы не говорим здесь о многом. На iPhone 4S предел еще ниже.

Вопросы

Так что вопрос в том, есть ли обходной путь. Или какое-то свойство пользовательского шрифта, которое можно настроить или изучить.

Есть ли смысл в том, чтобы основное приложение каким-то образом предварительно отображало шрифт во что-то (файлы png?) на диске, которое могло бы быть загружено виджетом? Как бы это сделать?

Любые предложения по подходу или чему-то, что нужно изучить?


person Nicolai Henriksen    schedule 14.03.2015    source источник
comment
Вы уверены, что это шрифт? У вас есть большие изображения или большое количество ограничений?   -  person Ashraf Tawfeeq    schedule 15.03.2015
comment
Да, я уверен. Без каких-либо других изменений, кроме изменения шрифта, который я создаю на HelveticaNeue, виджет работает нормально.   -  person Nicolai Henriksen    schedule 15.03.2015


Ответы (1)


Нам не удалось найти способ уменьшить объем памяти, используемой шрифтом.

Мы преуспели в обходном пути, используя ImageMagick и сценарий Perl для создания файлов png для всех возможных размеров и глифов, которые мы используем.

Файлы png помещаются в структуру каталогов с именем шрифта и размером пункта. Каждый глиф имеет свое собственное имя png, названное в честь шестнадцатеричного кода Unicode.

Затем все png-файлы помещаются в проект Xcode и, таким образом, попадают в пакет.

Во время выполнения вместо использования шрифта для рендеринга мы делаем [UIImage imageNamed:] и вместо этого рендерим его.

Решение работает хорошо и использует ограниченный объем памяти в виджете.

Есть несколько недостатков:

  1. Виджет и основные пакеты приложений раздуваются: добавляется более 1000 png-файлов (575 КБ = 4,7 МБ на диске). Мы еще не знаем, насколько это увеличивает размер дистрибутива приложения.
  2. Мы теряем гибкость исходного подхода. Новые типы устройств или лучшая поддержка динамического типа могут привести к необходимости большего размера, а затем нам нужно создать больше png.
  3. Большой риск ошибок

Скрипт для генерации png:

#!/usr/bin/perl

use File::Path qw(make_path);

$fontName = "SSSymboliconsBlock";
@sizes = (10, 11, 13, 16, 18);

foreach $size (@sizes) {
    $path = "fontCache/$fontName/$size";
    make_path($path);
    @glyphs = ("1f50d", "1f512", "1f511");

    foreach $glyphNo (@glyphs) {
        $glyph = chr(hex($glyphNo));
        system("convert -background none -fill black -font $fontName.ttf -pointsize $size label:\"$glyph\" $path/$glyphNo.png");
    }
}

Чтобы загрузить изображение, это примерно так:

- (UIImage *)loadImageForGlyph:(NSString *)glyph
                      fontName:(NSString *)fontName
                      fontSize:(CGFloat)fontSize
{
    // NSDictionary that translates from a glyph to a string with the hex
    // value of the code point.
    // e.g. glyph = @"????", codePoint = @"1f50d"
    NSString * codePoint = self.codePointByGlyph[glyph];
    NSString * path
    = [[[[@"fontCache" stringByAppendingPathComponent:fontName]
       stringByAppendingPathComponent:[@(fontSize) stringValue]]
        stringByAppendingPathComponent:codePoint]
       stringByAppendingPathExtension:@"png"];

    UIImage * img = [UIImage imageNamed:path];

    return img;
}
person Nicolai Henriksen    schedule 23.03.2015