2016-10-07 7 views
9

В NSAttributedString диапазон букв имеет атрибут ссылки и собственный атрибут цвета.Атрибут цвета игнорируется в NSAttributedString с NSLinkAttributeName

В Xcode 7 с Swift 2, он работает:

enter image description here

В Xcode 8 с Swift 3, обычай приписывали цвет для ссылки всегда игнорируется (она должна быть оранжевого цвета на скриншоте).

enter image description here

Вот код для тестирования.

Swift 2, Xcode 7:

import Cocoa 
import XCPlayground 

let text = "Hey @user!" 

let attr = NSMutableAttributedString(string: text) 
let range = NSRange(location: 4, length: 5) 
attr.addAttribute(NSForegroundColorAttributeName, value: NSColor.orangeColor(), range: range) 
attr.addAttribute(NSLinkAttributeName, value: "http://somesite.com/", range: range) 

let tf = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 50)) 
tf.allowsEditingTextAttributes = true 
tf.selectable = true 
tf.stringValue = text 
tf.attributedStringValue = attr 

XCPlaygroundPage.currentPage.liveView = tf 

Swift 3, Xcode 8:

import Cocoa 
import PlaygroundSupport 

let text = "Hey @user!" 

let attr = NSMutableAttributedString(string: text) 
let range = NSRange(location: 4, length: 5) 
attr.addAttribute(NSForegroundColorAttributeName, value: NSColor.orange, range: range) 
attr.addAttribute(NSLinkAttributeName, value: "http://somesite.com/", range: range) 

let tf = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 50)) 
tf.allowsEditingTextAttributes = true 
tf.isSelectable = true 
tf.stringValue = text 
tf.attributedStringValue = attr 

PlaygroundPage.current.liveView = tf 

Я послал сообщение об ошибке в Apple, но в то же время, если кто-то есть идея исправление или обходной путь в Xcode 8, это было бы здорово.

+0

При использовании 'NSTextView', вы должны установить' свойство linkTextAttributes'. Когда «NSTextField» сфокусирован и может быть отредактирован, текст отображается в «NSTextView», который использует свой 'linkTextAttributes' (синий, подчеркнутый и ручной курсор по умолчанию). – Willeke

+0

@Willeke Спасибо, к сожалению, я уже пробовал переопределить linkTextAttributes, это хорошее решение, но оно применяет одни и те же атрибуты ко всем ссылкам в атрибутной строке, и мне нужно иметь возможность устанавливать разные. Дело в том, что * ваш ответ на мой другой вопрос работал до недавнего времени *, большой мистерий вот почему тот же самый код, на который вы ответили, больше не работает. :) – Moritz

+0

Ох. Ссылка помечена оранжевым цветом в Xcode 7. Она синяя в Xcode 8. Я подаю радар ... Извините, что беспокоил вас этим, @Willeke ... – Moritz

ответ

4

Apple, разработчик ответил:

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

И они объясняют, почему она работала раньше, но не больше:

К сожалению, предыдущее поведение (приписывается строка диапазонов с NSLinkAttributeName рендеринга в другой цвет) не был явно поддерживается , Случилось так, что NSTextField только показывал ссылку, когда присутствовал редактор полей; без редактора полей, мы возвращаемся к цвету, указанному NSForegroundColorAttributeName.

Версия 10.12 обновила NSLayoutManager и NSTextField для визуализации ссылок, используя внешний вид ссылки по умолчанию, аналогичный iOS. (see AppKit release notes for 10.12.))

Для обеспечения согласованности предполагаемое поведение предназначено для диапазонов, представляющих ссылки (указанные через NSLinkAttributeName), которые будут рисоваться с использованием внешнего вида ссылки по умолчанию. Таким образом, текущее поведение является ожидаемым поведением.

(курсив мой)

+1

Спасибо! Очень полезная информация. – Oleg

4

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


С помощью этого обходного пути мы не используем NSLinkAttributeName вообще, так как она заставляет стиль, мы не хотим.

Вместо этого мы используем пользовательские атрибуты, и мы подклассифицируем NSTextField/NSTextView, чтобы обнаружить атрибуты под щелчком мыши и действовать соответственно.

Существует несколько ограничений, очевидно: вы должны иметь возможность подклассифицировать поле/представление, переопределить mouseDown и т. Д., Но «он работает для меня» в ожидании исправления.

При подготовке NSMutableAttributedString, где бы вы установили в NSLinkAttributeName, установить связь как атрибут с пользовательским ключом вместо:

theAttributedString.addAttribute("CUSTOM", value: theLink, range: theLinkRange) 
theAttributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.orange, range: theLinkRange) 
theAttributedString.addAttribute(NSCursorAttributeName, value: NSCursor.arrow(), range: theLinkRange) 

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

Для этого подкласс NSTextView и переопределить mouseDown(with event: NSEvent).

Мы получим местоположение события мыши в окне, найдите индекс символа в текстовом представлении в этом месте и попросите атрибуты символа в этом индексе в строке атрибута текстового вида.

class MyTextView: NSTextView { 

    override func mouseDown(with event: NSEvent) { 
     // the location of the click event in the window 
     let point = self.convert(event.locationInWindow, from: nil) 
     // the index of the character in the view at this location 
     let charIndex = self.characterIndexForInsertion(at: point) 
     // if we are not outside the string... 
     if charIndex < super.attributedString().length { 
      // ask for the attributes of the character at this location 
      let attributes = super.attributedString().attributes(at: charIndex, effectiveRange: nil) 
      // if the attributes contain our key, we have our link 
      if let link = attributes["CUSTOM"] as? String { 
       // open the link, or send it via delegate/notification 
      } 
     } 
     // cascade the event to super (optional) 
     super.mouseDown(with: event) 
    } 

} 

Всё.

В моем случае мне нужно было настроить разные слова с разными цветами и типами ссылок, поэтому вместо передачи только ссылки в виде строки я передаю структуру, содержащую ссылку и дополнительную метаинформацию, но идея такая же.

Если вам нужно использовать NSTextField вместо NSTextView, это немного сложнее найти местоположение события клика. Решением является создание NSTextView внутри NSTextField и оттуда использовать ту же технику, что и раньше.

class MyTextField: NSTextField { 

    var referenceView: NSTextView { 
     let theRect = self.cell!.titleRect(forBounds: self.bounds) 
     let tv = NSTextView(frame: theRect) 
     tv.textStorage!.setAttributedString(self.attributedStringValue) 
     return tv 
    } 

    override func mouseDown(with event: NSEvent) { 
     let point = self.convert(event.locationInWindow, from: nil) 
     let charIndex = referenceView.textContainer!.textView!.characterIndexForInsertion(at: point) 
     if charIndex < self.attributedStringValue.length { 
      let attributes = self.attributedStringValue.attributes(at: charIndex, effectiveRange: nil) 
      if let link = attributes["CUSTOM"] as? String { 
       // ... 
      } 
     } 
     super.mouseDown(with: event) 
    } 

}