Что не так с отображением символов валют в iOS

    В июне 2016 года в App Store появилось разработанное нами приложение ИЛЬ ДЕ БОТЭ. Клиент попросил нас использовать шрифт Carisma. К системным шрифтам платформы iOS он не относится, что можно понять после попытки найти его здесь. Работая над приложением, я отметил, что символы валют, попадающие в диапазон от U+20B6 (символ турского ливра) до U+20BE (символ грузинского лари) и набранные шрифтом, не входящим в число системных, снижают производительность приложения. Символ рубля — как раз из указанного диапазона.

    Найдите отличия:



    Капитан выходит на связь
    Экран справа лагает. Это заметно по показателю загруженности CPU.

    Давайте-ка это обсудим.

    Разберёмся подробнее, в каких именно ситуациях всё тормозит снижается производительность. Для этого разобьём представление строки с символом валюты на составные части:

    1. Текст может содержаться в следующих сущностях: UILabel, UITextView и UITextField
    2. В этих сущностях может использоваться обычная строка NSString и атрибутированная NSAttributedString
    3. Для строки мы можем получить символ рубля одним из трёх способов:

      1. Используя HTML:

        NSString *htmlString = @"<p>500 ₽</p> ";
        self.string = [[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUnicodeStringEncoding] options:@{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } documentAttributes:nil error:nil];
        

      2. Используя unicode:

        self.string = [[NSAttributedString alloc] initWithString: @"500 \u20BD"];
        

      3. Используя number formatter:

        NSNumberFormatter * numberFormatter = [[NSNumberFormatter alloc] init];
        [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
        [numberFormatter setCurrencyCode:@"RUB"];
        [numberFormatter setMaximumFractionDigits:0];
        self.string = [[NSAttributedString alloc] initWithString:[numberFormatter stringFromNumber:@500]];
        

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

    1. UILabel, тип строки: attributed, методы получения символа: 2 или 3
    2. UITextView, тип строки: любой, методы получения символа: любой
    3. UITextField, тип строки: любой, методы получения символа: любой

    Эксперимент заключался в измерении нагрузки на процессор при пролистывании списка, элементы которого содержали символ рубля. Средняя нагрузка на процессор для нормального состояния не превышала 10%, а если была проблема, нагрузка колебалась в районе 30-80%, что можно видеть на гифках.


    На картинке обведены символы, с которыми наблюдается описанная проблема.

    Чтобы приложение вело себя нормально, нужно избегать тех сочетаний, что я выявил, или использовать для символов валют системный шрифт San Francisco, а также шрифты из семейства Helvetica Neue. Но это минимальные требования. Истинная же причина увеличения нагрузки для меня осталась загадкой, и если кто-то разбирается в сути вопроса — делитесь соображениями в комментариях.
    • +28
    • 14,4k
    • 9
    Лайв Тайпинг
    44,00
    Мы создаём мобильные приложения и веб-сервисы
    Поделиться публикацией

    Комментарии 9

      +8
      А вы пробовали посмотреть, как нарисован символ в используемом шрифте? Может быть там ад из 100500 сплайнов?
        +2
        А есть ли в этой несистемной гарнитуре указанные символы?
          +3
          Ковырял этот баг в сентябре (ибо такое поведение проявилось только на десятке). Судя по профайлеру корень проблемы в том что размер такого символа считается очень долго. И кстати у меня воспроизводилось и на UILabel с не attributed строкой с Helvetica Neue

          Что то типа такого в конечно итоге выходит

            +3
            — Вообще-то UITextView/UITextField это поля для редактирования текста и они отлавливают обработку нажатий
            — Вы используете NSAttributedString. Этот класc используется для вывода текста со стилями.

            Попробуйте использовать:
            — UILabel с простыми строками NSString
            — Можно попробовать через чистый CoreText делать вывод
              +2
              P.S.
              Еще стоит проверить как вы формируете таблицу. Возможно в делегатах таблиц понапихано много всего.
              Тестирование стоит проводить на реальном устройстве. Симулятор привязан к железу вашего компа и может давать неверные результаты измерений.
                0
                Попробуйте использовать:
                — UILabel с простыми строками NSString
                Для наших задач необходимо было использование стилей и обычная строка не подходит

                — Можно попробовать через чистый CoreText делать вывод
                Стоит попробовать

                P.S.
                Еще стоит проверить как вы формируете таблицу. Возможно в делегатах таблиц понапихано много всего.
                В методах делегата для приведенного примера используется только указание высоты для строки. Описанная проблема не связана с вызовом методов делегирования

                Тестирование стоит проводить на реальном устройстве. Симулятор привязан к железу вашего компа и может давать неверные результаты измерений.
                Тестирование проводилось в том числе и на реальном устройстве и соответствует результатам проведенным на симуляторе
                0
                Неправильно было построено предложение, исправил. Речь была о том, что независимо от сущности содержащей текст проблема будет актуальна
                0
                Если в вашем кастомном шрифте много глифов возможно проблема в поиске нужного глифа при подготовке текста к рендерингу.
                Проверить что именно в этом проблема — замерять сколько времени работает CTLineCreateWithAttributedString с соответствующей строкой.
                Если в глиф плохо оптимизирован (дизайнер натыкал 100500 точек) — то тоже может быть проблема в расчетах границ глифа (примерно чекнуть можно юзая CTLineGetImageBounds) + сама прорисовка может быть не быстрая (CTFontDrawGlyphs)…
                  0
                  Столкнулся с резким увеличением потребления ресурсов и медленной загрузкой приложения. После профайлинга заметил, что дело в нестандартных шрифтах, добавленных в проект. После перехода на системные шрифты, приложение стало гораздо быстрее работать.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое