Last active
November 21, 2018 08:03
-
-
Save HamGuy/4c5c51b044264aef4eeb991ce97c9dcb to your computer and use it in GitHub Desktop.
Subclass of UILabel could handle navigation when touch link
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// TapableLabel.swift | |
// AspirinUIKit | |
// | |
// Created by HamGuy on 2018/11/21. | |
// | |
import UIKit | |
public protocol TapableLabelDelegate: NSObjectProtocol { | |
func tapableLabel(_ label: TapableLabel, didTapUrl url: String, atRange range: NSRange) | |
} | |
public class TapableLabel: UILabel { | |
private var links: [String: NSRange] = [:] | |
private(set) var layoutManager = NSLayoutManager() | |
private(set) var textContainer = NSTextContainer(size: CGSize.zero) | |
private(set) var textStorage = NSTextStorage() { | |
didSet { | |
textStorage.addLayoutManager(layoutManager) | |
} | |
} | |
public weak var delegate: TapableLabelDelegate? | |
public override var attributedText: NSAttributedString? { | |
didSet { | |
if let attributedText = attributedText { | |
textStorage = NSTextStorage(attributedString: attributedText) | |
} else { | |
textStorage = NSTextStorage() | |
links = [:] | |
} | |
} | |
} | |
public override var lineBreakMode: NSLineBreakMode { | |
didSet { | |
textContainer.lineBreakMode = lineBreakMode | |
} | |
} | |
public override var numberOfLines: Int { | |
didSet { | |
textContainer.maximumNumberOfLines = numberOfLines | |
} | |
} | |
public override init(frame: CGRect) { | |
super.init(frame: frame) | |
setup() | |
} | |
public required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
setup() | |
} | |
public override func layoutSubviews() { | |
super.layoutSubviews() | |
textContainer.size = bounds.size | |
} | |
/// addLinks | |
/// | |
/// - Parameters: | |
/// - text: text of link | |
/// - url: link url string | |
public func addLink(_ text: String, withURL url: String) { | |
guard let theText = attributedText?.string as? NSString else { | |
return | |
} | |
let range = theText.range(of: text) | |
guard range.location != NSNotFound else { | |
return | |
} | |
links[url] = range | |
} | |
private func setup() { | |
isUserInteractionEnabled = true | |
layoutManager.addTextContainer(textContainer) | |
textContainer.lineFragmentPadding = 0 | |
textContainer.lineBreakMode = lineBreakMode | |
textContainer.maximumNumberOfLines = numberOfLines | |
} | |
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { | |
guard let locationOfTouch = touches.first?.location(in: self) else { | |
return | |
} | |
textContainer.size = bounds.size | |
let indexOfCharacter = layoutManager.glyphIndex(for: locationOfTouch, in: textContainer) | |
for (urlString, range) in links { | |
if NSLocationInRange(indexOfCharacter, range), let url = URL(string: urlString) { | |
delegate?.tapableLabel(self, didTapUrl: urlString, atRange: range) | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment