Skip to content

Instantly share code, notes, and snippets.

@wh1pch81n
Created April 14, 2016 03:45
Show Gist options
  • Save wh1pch81n/585451344947db8c645fce0cee62c37d to your computer and use it in GitHub Desktop.
Save wh1pch81n/585451344947db8c645fce0cee62c37d to your computer and use it in GitHub Desktop.
UITextView supports clinking a link and going directly to the website, but sometimes you want that "link" to act like a button so you can do something else when you click it. In this example we have ""no-opclickno-op"" where "click" is a link that will print "hello world"
import UIKit
import XCPlayground
class ClickableLinkTextView: UITextView {
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
setUp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
func setUp() {
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(ClickableLinkTextView.tappedView(_:))))
}
func tappedView(gesture: UITapGestureRecognizer) {
var location: CGPoint = gesture.locationInView(self)
location.x -= textContainerInset.left
location.y -= textContainerInset.top
let characterIndex: Int = Int(layoutManager.characterIndexForPoint(location, inTextContainer:textContainer, fractionOfDistanceBetweenInsertionPoints: nil))
if case 0..<textStorage.length = characterIndex {
let value: AnyObject? = attributedText.attribute(LinkStringHelper.linkAttributeName, atIndex:characterIndex, effectiveRange:nil)
if let v = value as? LinkStringAction {
v.action()
}
}
}
}
class LinkStringAction: NSObject {
let text: String
let action: () -> ()
init(text: String, action: () -> ()) {
self.text = text
self.action = action
}
}
struct LinkStringHelper: StringInterpolationConvertible {
static let linkAttributeName: String = "\(String(LinkStringHelper)).linkAttributedName"
private var plainString: String?
private var linkAction: LinkStringAction?
private var linkObjects: [LinkStringHelper] = []
/// every segment created by init<T>(stringInterpolationSegment expr: T) will come here as an array of Segments.
init(stringInterpolation strings: LinkStringHelper...) {
linkObjects = strings // Keep as disparate segments
}
/// the string literal is broken up into intervals of all string and \(..) which are called segments
init<T>(stringInterpolationSegment expr: T) {
if let lk = expr as? LinkStringAction {
linkAction = lk
} else {
plainString = String(expr)
}
}
/// Combines all segments and applys attributes to it.
func attributedStringWithAttributes(attributes: [String : AnyObject]) -> NSAttributedString {
let mut = NSMutableAttributedString()
for i in linkObjects {
let t = (i.plainString, i.linkAction)
if case (nil, let x?) = t {
var newAttributes = attributes
newAttributes[NSLinkAttributeName] = NSURL(string: "http://www.google.com")!// For link color
newAttributes[LinkStringHelper.linkAttributeName] = x
mut.appendAttributedString(NSAttributedString(string: x.text, attributes: newAttributes))
} else if case (let y?, nil) = t {
mut.appendAttributedString(NSAttributedString(string: y, attributes: attributes))
}
}
mut.appendAttributedString(NSAttributedString(string: "\0")) // To prevent taps from registering if after clickable text.
return mut
}
}
//Example
var textview: ClickableLinkTextView = ClickableLinkTextView(frame: CGRect(origin: CGPoint(), size: CGSize(width: 100, height: 100)), textContainer: nil)
let action = LinkStringAction(text: "click", action: { print("hello world") })
let cs: LinkStringHelper = "no-op\(action)no-op"
textview.attributedText = cs.attributedStringWithAttributes([:])
XCPlaygroundPage.currentPage.liveView = textview
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment