Turn some parts of UILabel to act like a UIButton?

What you want to do is use an attributed string with a text view to make a link that will act as a button.

let attributedString = NSMutableAttributedString(string: "Some string with link", attributes: [<attributes>])

Then set a part of it as a link, and customize it's appearance using the linkAttributes property of the Text View. Because this is a button and not an actual link we just put a dummy url in for the link so we can handle it in our delegate later.

attributedString.setSubstringAsLink(substring: "link", linkURL: "CUSTOM://WHATEVER")
let linkAttributes: [String : AnyObject] = [NSForegroundColorAttributeName : .redColor(), NSUnderlineColorAttributeName : .redColor(), NSUnderlineStyleAttributeName : NSUnderlineStyle.StyleSingle.rawValue]
textView.linkTextAttributes = linkAttributes
textView.attributedText = attributedString
textView.selectable = true
textView.editable = false
textView.userInteractionEnabled = true

Finally in text view delegate we will check for the scheme and perform some action.

func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool {
    if URL.scheme == "CUSTOM" {
       // Do your button actions here
    }
    return true
}

Extension for setSubstringAsLink:

extension NSMutableAttributedString {
    // Set part of string as URL
    public func setSubstringAsLink(substring substring: String, linkURL: String) -> Bool {
        let range = self.mutableString.rangeOfString(substring)
        if range.location != NSNotFound {
            self.addAttribute(NSLinkAttributeName, value: linkURL, range: range)
            return true
        }
        return false
    }
}

Let's say you have a UILabel with text "abc123", and you want "abc" to function as a UIButton.

  • Calculate and store the rectangle that contains "abc".
  • Add a UITapGestureRecognizer to the UILabel.
  • When the UILabelis tapped, check if the tap is within the rectangle.

static func getRect(str: NSAttributedString, range: NSRange, maxWidth: CGFloat) -> CGRect {
    let textStorage = NSTextStorage(attributedString: str)
    let textContainer = NSTextContainer(size: CGSize(width: maxWidth, height: CGFloat.max))
    let layoutManager = NSLayoutManager()       
    layoutManager.addTextContainer(textContainer)
    textStorage.addLayoutManager(layoutManager)     
    textContainer.lineFragmentPadding = 0       
    let pointer = UnsafeMutablePointer<NSRange>.alloc(1)
    layoutManager.characterRangeForGlyphRange(range, actualGlyphRange: pointer)     
    return layoutManager.boundingRectForGlyphRange(pointer.move(), inTextContainer: textContainer)
}

let rect1 = getRect(label.attributedText!, range: NSMakeRange(0, 3), maxWidth: label.frame.width)

label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(MyClass.tappedLabel(_:))))

func tappedLabel(sender: UITapGestureRecognizer) {
    if rect1.contains(sender.locationInView(sender.view)) {
        // ...
    }
}