Skip to content

Instantly share code, notes, and snippets.

@jhonny-me
Created January 10, 2017 07:10
Show Gist options
  • Save jhonny-me/38b34eae77b73fe02605b96b4f07cbf9 to your computer and use it in GitHub Desktop.
Save jhonny-me/38b34eae77b73fe02605b96b4f07cbf9 to your computer and use it in GitHub Desktop.
Mutilple links in Label
//
// MLLabel.swift
// Starbucks
//
// Created by Johnny Gu on 09/01/2017.
// Copyright © 2017 Wiredcraft. All rights reserved.
//
import UIKit
class MLLabel: UILabel {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
override init(frame: CGRect) {
super.init(frame: frame)
isUserInteractionEnabled = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let attributedText = attributedText else { return }
let wholeRange = NSMakeRange(0, attributedText.length)
attributedText.enumerateAttribute(NSLinkAttributeName, in: wholeRange, options: []) { [weak self] (url, range, stop) in
if url == nil { return }
self?.boundingRectsForCharacterRange(range, using: { (rects) in
if
let point = touches.first?.location(in: self),
rects.contains(point){
stop.pointee = true
if let url = url as? URL{
if (UIApplication.shared.canOpenURL(url)){
UIApplication.shared.openURL(url)
}
}else if let string = url as? String, let url = URL(string: string) {
if (UIApplication.shared.canOpenURL(url)){
UIApplication.shared.openURL(url)
}
}else {
}
print(url ?? "ppp")
}
})
}
}
}
extension UILabel {
func boundingRectsForCharacterRange(_ range: NSRange, using block: @escaping ([CGRect])->Void) {
guard let attributedText = attributedText else {
block([])
return
}
let textStorage = NSTextStorage(attributedString: attributedText)
let layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
let textContainer = NSTextContainer(size: bounds.size)
textContainer.lineFragmentPadding = 0.0
layoutManager.addTextContainer(textContainer)
var glyphRange = NSRange()
var rects: [CGRect] = []
// Convert the range for glyphs.
layoutManager.characterRange(forGlyphRange: range, actualGlyphRange: &glyphRange)
layoutManager.enumerateLineFragments(forGlyphRange: glyphRange) { (lineRect, usedRect, container, range, stop) in
if let rect = self.rect(for: glyphRange, in: range, whole: lineRect) {
rects.append(rect)
}
if glyphRange.length + glyphRange.location <= range.length + range.location {
block(rects)
}
print(lineRect, usedRect, range.location, range.length, glyphRange.location, glyphRange.length, stop.pointee)
}
}
private func rect(for subRange: NSRange, in range: NSRange, whole rect: CGRect) -> CGRect?{
let intersectionRange = NSIntersectionRange(subRange, range)
if intersectionRange.length == 0 { return nil }
let x = CGFloat(intersectionRange.location - range.location)/CGFloat(range.length) * rect.width + rect.minX
let width = CGFloat(intersectionRange.length)/CGFloat(range.length) * rect.width
return CGRect(x: x, y: rect.minY, width: width, height: rect.height)
}
func boundingRectForCharacterRange(range: NSRange) -> CGRect? {
guard let attributedText = attributedText else { return nil }
let textStorage = NSTextStorage(attributedString: attributedText)
let layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
let textContainer = NSTextContainer(size: bounds.size)
textContainer.lineFragmentPadding = 0.0
layoutManager.addTextContainer(textContainer)
var glyphRange = NSRange()
// Convert the range for glyphs.
layoutManager.characterRange(forGlyphRange: range, actualGlyphRange: &glyphRange)
layoutManager.enumerateLineFragments(forGlyphRange: glyphRange) { (lineRect, usedRect, container, range, stop) in
// print(lineRect, usedRect, range.location, range.length, glyphRange.location, glyphRange.length)
}
return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
}
}
extension Sequence where Iterator.Element == CGRect {
func contains(_ point: CGPoint) -> Bool {
var result = false
self.forEach({ if $0.contains(point) { result = true }})
return result
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment