NSTextView > linkTextAttributes when link is being clicked?

Problem:


It's possible to define how a link should look in a NSTextView using the linkTextAttributes property.


The problem is that this does not apparently let one define how the link should look like when being clicked (or after having been visited).


Question:


Is there some attribute names that can be used to define how the link should look like when it's being clicked?

Replies

NSTextView has the property:


var linkTextAttributes: [NSAttributedString.Key : Any]? { get set }


That is used for:


The attributes used to draw the onscreen presentation of link text.


Link text attributes are applied as temporary attributes to any text with a link attribute.


I managed to change the attributes as follow:


import Cocoa

class ChangeColorVisitedLinksViewController: NSViewController {

    @IBOutlet var textView: NSTextView!
    
    var linkRanges = [NSRange]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        textView.delegate = self
        let string = textView.string
        let types = NSTextCheckingResult.CheckingType.link
        
        let detector = try? NSDataDetector(types: types.rawValue)
        
        if let matches = detector?.matches(in: string, options: [], range: NSRange(location: 0, length: string.utf16.count)){
            for match in matches {
                linkRanges.append(match.range)
            }
        }
    }
    
}

extension ChangeColorVisitedLinksViewController : NSTextViewDelegate{
    func textView(_ textView: NSTextView, clickedOnLink link: Any, at charIndex: Int) -> Bool{
        for linkRange in linkRanges{
            if linkRange.contains(charIndex){
                guard let textStorage = textView.textStorage else { return false }
                textStorage.setAttributes([NSAttributedString.Key.foregroundColor: NSColor.red] , range: linkRange)
            }
        }
          return false
    }
}

Interesting solutions for the visited links. But this unfortunately does not solve the "being clicked" color problem.

If you would like to have a different color for the "being clicked", you need something like this.


It use red color for visited links and green color for "being clicked" link.


import Cocoa

class LinkRangesWithVisitedStatus{
    let range: NSRange
    var visited: Bool = false
    init(range: NSRange){
        self.range = range
    }
}

class ViewController: NSViewController {
    
    @IBOutlet var textView: NSTextView!

    var linkRangesWithVisitedStatus = [LinkRangesWithVisitedStatus]()

    override func viewDidLoad() {
        super.viewDidLoad()
        textView.delegate = self
        let string = textView.string

        let types = NSTextCheckingResult.CheckingType.link
        
        let detector = try? NSDataDetector(types: types.rawValue)

        if let matches = detector?.matches(in: string, options: [], range: NSRange(location: 0, length: string.utf16.count)){

            for match in matches {
                linkRangesWithVisitedStatus.append(LinkRangesWithVisitedStatus(range:match.range))
            }
        }
    }
}

extension ViewController : NSTextViewDelegate{
    func textView(_ textView: NSTextView, clickedOnLink link: Any, at charIndex: Int) -> Bool{
        for linkRangeWithVisitedStatus in linkRangesWithVisitedStatus{
            
            guard let textStorage = textView.textStorage else { return true }
            
            if linkRangeWithVisitedStatus.visited == true {
                textStorage.setAttributes([NSAttributedString.Key.foregroundColor: NSColor.red] , range: linkRangeWithVisitedStatus.range) // The previous visited links
            }
            
            if linkRangeWithVisitedStatus.range.contains(charIndex){
                textStorage.setAttributes([NSAttributedString.Key.foregroundColor: NSColor.green] , range: linkRangeWithVisitedStatus.range) // The link being clicked
                linkRangeWithVisitedStatus.visited = true
            }
        }
        return true
    }
}