Как заставить работать атрибут стиля подчеркивания NSAttributedString? - PullRequest
0 голосов
/ 05 ноября 2018

Используя NSAttributedString, я бы хотел подчеркнуть часть строки. Следующий код не работает должным образом:

let string = NSMutableAttributedString(string: "This is my string.")

string.addAttributes([.underlineStyle : NSUnderlineStyle.single], 
                           range: NSRange(location: 5, length: 2))

Можно ожидать, что «это» будет подчеркнуто. Вместо этого механизм компоновки UITextView игнорирует атрибут и генерирует предупреждение в консоли. Бесполезно, предупреждение не упоминает underlineStyle или NSUnderlineStyle.

Эта проблема является распространенной. В StackOverflow есть множество примеров, показывающих правильное использование. Однако, хотя я уверен, что должны быть некоторые ответы, объясняющие проблему, ответы, которые я прочитал, просто показывают непрозрачные примеры использования.

Этот вопрос ставится (и дан ответ ниже), чтобы увековечить объяснение проблемы, чтобы будущие я и другие могли извлечь из нее урок.

1 Ответ

0 голосов
/ 05 ноября 2018

NSUnderlineStyle неправильный тип

Точно, но несколько бесполезно, документация объясняет, что ключ атрибута underlineStyle принимает значение атрибута "NSNumber, содержащее целое число".

Значением этого атрибута является объект NSNumber, содержащий целое число. Это значение указывает, подчеркнут ли текст и соответствует одной из констант, описанных в NSUnderlineStyle. значение по умолчанию для этого атрибута - styleNone.

Многие читатели тянутся к NSUnderlineStyle, щелкают по нему и находят список стилей, похожий на enum: single, thick, patternDash, double и т. Д. Это все кажется хорошим Быстрое решение. Читатель интуитивно понимает, что это перечисление должно указывать значение атрибута, и вводит код, показанный в вопросе выше.


Свойство rawValue NSUnderlineStyle обеспечивает правильный тип

NSUnderlineStyle не является перечислением. Это структура, соответствующая протоколу OptionSet. Протокол OptionSet - это удобный, быстрый способ установки битов целого числа, где каждый бит представляет двоичное состояние. Структура NSUnderlineStyle является оберткой вокруг этого целого числа. Его enum-подобные стили на самом деле являются статическими переменными в структуре, каждый из которых возвращает экземпляр NSUnderlineStyle со встроенным целым числом, при этом только один из битов переворачивается, чтобы соответствовать желаемому стилю.

Словарь атрибутов

NSAttributedString принимает буквально что угодно как значение, поэтому он с радостью принимает экземпляры NSUnderlineStyle. Но TextKit, движок, который заставляет работать UITextView и UILabel, не знает, что делать с экземпляром NSUnderlineStyle. Когда TextKit пытается отобразить атрибут underlineStyle, ему требуется целое число, чтобы он мог определить правильный стиль, проверяя биты целого числа.

К счастью, есть простой способ получить это целое число. Свойство rawValue NSUnderlineStyle продает его. Таким образом, правильно работающая версия нашего фрагмента кода выглядит следующим образом:

let string = NSMutableAttributedString(string: "This is my string.")

string.addAttributes([.underlineStyle : NSUnderlineStyle.single.rawValue], 
                           range: NSRange(location: 5, length: 2))

Единственная разница между этим кодом и кодом в вопросе заключается в добавлении ".rawValue" к "NSUnderlineStyle.single". И "is" подчеркнуто.

В качестве примечания, тип, продаваемый свойством rawValue, - Int, а не NSNumber. Ссылка в документации на значение атрибута NSNumber может сбить с толку. Под капотом для взаимодействия с ObjC NSAttributedString оборачивает Int внутри NSNumber. Нет необходимости (или выгоды?) Явно включать Int в NSNumber.


Используйте метод OptionSet union для объединения подчеркнутых стилей

Мы можем объединить несколько стилей подчеркивания в составной стиль. На побитовом уровне это достигается путем принятия логического ИЛИ целых чисел, представляющих два стиля. Будучи OptionSet, NSUnderlineStyle предоставляет альтернативу Swifty. Метод union(_:) для NSUnderlineStyle выполняет логическое ИЛИ более простым языком. Как пример:

NSUnderlineStyle.double.union(.patternDot).rawValue

Этот код выдает Int с соответствующими битами, а TextKit рисует очень красивую пунктирную двойную подчеркивание. Конечно, не все комбинации работают. NSAttributedString может принять все, но здравый смысл и механизм TextKit в конечном итоге будут диктовать.

...