jeremyforan
jeremyforan

Reputation: 1437

Simple clickable link in Cocoa and Swift

Im writing a desktop app in swift for mac 10.11 and I would like to add a link to the about page.

Very much like this: https://developer.apple.com/library/mac/qa/qa1487/_index.html

I haven't been able to find a good tutorial or reference.

Any help would be much appreciated

Upvotes: 8

Views: 5306

Answers (6)

Ken Zira
Ken Zira

Reputation: 1176

swift 4.2

class HyperlinkTextField: NSTextField {


private var detector: NSDataDetector!

override func awakeFromNib() {
    super.awakeFromNib()
     detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
}


func setText(text: String ){
    let matches = detector.matches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count))

    for match in matches {
        guard let range = Range(match.range, in: text) else { continue }
        let url = text[range]
        let attributedString = NSMutableAttributedString(string: text)
        attributedString.addAttribute(.link, value: url, range: match.range)
        self.attributedStringValue = attributedString
    }
}

}

Upvotes: 0

jeremyforan
jeremyforan

Reputation: 1437

Swift 4, xCode 9

@IBDesignable
class HyperlinkTextField: NSTextField {

    @IBInspectable var href: String = ""

    override func resetCursorRects() {
        discardCursorRects()
        addCursorRect(self.bounds, cursor: NSCursor.pointingHand)
    }

    override func awakeFromNib() {
        super.awakeFromNib()

        // TODO:  Fix this and get the hover click to work.

        let attributes: [NSAttributedStringKey: Any] = [
            NSAttributedStringKey.foregroundColor: NSColor.linkColor,
            NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue as AnyObject
        ]
        attributedStringValue = NSAttributedString(string: self.stringValue, attributes: attributes)
    }

    override func mouseDown(with theEvent: NSEvent) {
        if let localHref = URL(string: href) {
            NSWorkspace.shared.open(localHref)
        }
    }
}

Upvotes: 7

Mahdi Khalili
Mahdi Khalili

Reputation: 1198

let link = NSTextField()
link.isBezeled = false
link.drawsBackground = false
link.isEditable = false
link.isSelectable = true
link.allowsEditingTextAttributes = true
let url = URL(string: "http://www.google.com")
let linkTextAttributes: [NSAttributedStringKey: Any] = [
        NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue,
        NSAttributedStringKey.foregroundColor: NSColor.blue,
        NSAttributedStringKey.link: url as Any
            ]
let string = "whatever"
link.attributedStringValue = NSAttributedString(string: string, attributes: linkTextAttributes)
window.contentView?.addSubview(link)

Upvotes: 1

GabeV
GabeV

Reputation: 1046

Modified the existing answers to allow for a substring of the label's text to become underlined and blue, so you can do something like: This is the answer

// A text field that can contain a hyperlink within a range of characters in the text.
@IBDesignable
public class SubstringLinkedTextField: NSTextField {
    // the URL that will be opened when the link is clicked.
    public var link: String = ""
    @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'link' instead.")
    @IBInspectable public var HREF: String {
        get {
            return self.link
        }
        set {
            self.link = newValue
            self.needsDisplay = true
        }
    }

    // the substring within the field's text that will become an underlined link. if empty or no match found, the entire text will become the link.
    public var linkText: String = ""
    @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'linkText' instead.")
    @IBInspectable public var LinkText: String {
        get {
            return self.linkText
        }
        set {
            self.linkText = newValue
            self.needsDisplay = true
        }
    }

    override public func awakeFromNib() {
        super.awakeFromNib()

        self.allowsEditingTextAttributes = true
        self.isSelectable = true

        let url = URL(string: self.link)
        let attributes: [NSAttributedStringKey: AnyObject] = [
            NSAttributedStringKey(rawValue: NSAttributedStringKey.link.rawValue): url as AnyObject
        ]
        let attributedStr = NSMutableAttributedString(string: self.stringValue)

        if self.linkText.count > 0 {
            if let range = self.stringValue.indexOf(substring: self.linkText) {
                attributedStr.setAttributes(attributes, range: range)
            } else {
                attributedStr.setAttributes(attributes, range: NSMakeRange(0, self.stringValue.count))
            }
        } else {
            attributedStr.setAttributes(attributes, range: NSMakeRange(0, self.stringValue.count))
        }
        self.attributedStringValue = attributedStr
    }
}

Upvotes: 5

Fred Appelman
Fred Appelman

Reputation: 716

Exactly what I needed. Here is the Swift3 version:

import Cocoa

@IBDesignable
class HyperTextField: NSTextField {
    @IBInspectable var href: String = ""

    override func awakeFromNib() {
        super.awakeFromNib()

        let attributes: [String: AnyObject] = [
            NSForegroundColorAttributeName: NSColor.blue,
            NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue as AnyObject
        ]
        self.attributedStringValue = NSAttributedString(string: self.stringValue, attributes: attributes)
    }

    override func mouseDown(with event: NSEvent) {
        NSWorkspace.shared().open(URL(string: self.href)!)
    }
}

Upvotes: 3

Code Different
Code Different

Reputation: 93161

The easiest way is to subclass NSTextField to create a HyperlinkTextField. Below is an example:

First, let's add a HyperlinkTextField class to your project:

// HyperlinkTextField.swift

import Cocoa

@IBDesignable
class HyperlinkTextField: NSTextField {
    @IBInspectable var href: String = ""

    override func awakeFromNib() {
        super.awakeFromNib()

        let attributes: [String: AnyObject] = [
            NSForegroundColorAttributeName: NSColor.blueColor(),
            NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue
        ]
        self.attributedStringValue = NSAttributedString(string: self.stringValue, attributes: attributes)
    }

    override func mouseDown(theEvent: NSEvent) {
        NSWorkspace.sharedWorkspace().openURL(NSURL(string: self.href)!)
    }
}

Next, in Interface Builder, drag a label from the Object library to your window.

Select that label, go to the menu View > Utilities > Show Identity Inspector (or press Cmd + Opt + 3) and change the class to HyperlinkTextField

Identity Inspector

Go to the Attributes Inspector (Cmd + Opt + 4) and set Href to the URL you want to visit.

Attributes Inspector

The label shows black text in Interface Builder but everything will be fine when you run your app. Clicking on the label will open the link in your default browser.

One thing I couldn't achieve was to make the HyperlinkTextField shows up as blue and underlined in Interface Builder. Comments on how to do that are welcome.

Upvotes: 4

Related Questions