NSLayoutManager, boundingRectForGlyphRange и эмодзи

Я показываю текст, который может содержать эмодзи, используя метод drawInRect(rect: CGRect) NSAttributedString. Поскольку я хочу обнаруживать нажатия на текст, я использую следующий метод, чтобы увидеть, какой символ был нажат:

let textStorage = NSTextStorage(attributedString: attributedString)
let layoutManager = NSLayoutManager()
layoutManager.usesFontLeading = true
textStorage.addLayoutManager(layoutManager)

let textContainer = NSTextContainer(size: containerSize)
layoutManager.addTextContainer(textContainer)

textContainer.lineFragmentPadding = 0.0

layoutManager.ensureLayoutForTextContainer(textContainer)

let tappedIndex = layoutManager.characterIndexForPoint(point, 
        inTextContainer: textContainer, 
        fractionOfDistanceBetweenInsertionPoints: nil)

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

let emojiText = "????"
let font = UIFont.systemFontOfSize(20.0)
let containerSize = CGSize(width: 300.0, height: 20000.0)

let attributedString = NSAttributedString(string: emojiText, attributes: [NSFontAttributeName: font])

let textStorage = NSTextStorage(attributedString: attributedString)
let layoutManager = NSLayoutManager()
layoutManager.usesFontLeading = true
textStorage.addLayoutManager(layoutManager)

let textContainer = NSTextContainer(size: containerSize)
layoutManager.addTextContainer(textContainer)
textContainer.lineFragmentPadding = 0.0

layoutManager.ensureLayoutForTextContainer(textContainer)

let glyphRect = layoutManager.boundingRectForGlyphRange(NSRange(location: 0, length: attributedString.length), inTextContainer: textContainer)
let boundingRect = attributedString.boundingRectWithSize(containerSize, options:[.UsesLineFragmentOrigin, .UsesFontLeading], context: nil)

Выполнение этого кода привело к следующим CGRects:

glyphRect = (0.0, 0.0, 23.0, 28.875)
boundingRect = (0.0, 0.0, 23.0, 23.8671875)

Это означает, что эти два метода дают два совершенно разных размера! Это не было бы проблемой, но «смещение» складывается с большим количеством строк.

Пример смещенного сдвига

Я установил фиолетовый фон для символа, который дал мне characterIndexForPoint, дал прямоугольнику boundingRectForGlyphRange зеленый контур, а желтая точка — это фактическое местоположение касания. Обратите внимание, что зеленый прямоугольник хорошо сочетается с другим символом, однако это никоим образом не указывает, поскольку в данном конкретном случае он просто хорошо совпадает.

Я упускаю из виду что-то очевидное или это проблема iOS?


person Gert-Jan    schedule 27.07.2016    source источник


Ответы (1)


Я решил проблему. Похоже, что NSAttributedString.drawInRect рисует не так, как CoreText. Теперь я использую следующий код для рисования текста в drawRect:

let totalRange = layoutManager.glyphRangeForTextContainer(textContainer)

layoutManager.drawBackgroundForGlyphRange(range, atPoint: CGPointZero)
layoutManager.drawGlyphsForGlyphRange(range, atPoint: CGPointZero)
person Gert-Jan    schedule 28.07.2016