UILabel의 hyperLink를 터치할 수 있도록 만들기
Posted on 2021-11-17 by GKSRUDTN99
Swift&Xcode
Swift
UILabel의 hyperLink를 터치할 수 있도록 만들기
1. UILabel의 터치 위치가 주어졌을 때, 터치 위치의 index를 반환하는 함수를 UILabel에 추가한다.
extension UILabel {
/// 입력된 포지션에 따라 라벨의 문자열의 인덱스 반환
/// - Parameter point: 인덱스 값을 알고 싶은 CGPoint
func textIndex(at point: CGPoint) -> Int? {
guard let attributedText = attributedText else { return nil }
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: self.bounds.size)
let textStorage = NSTextStorage(attributedString: attributedText)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0.0
layoutManager.addTextContainer(textContainer)
var textOffset = CGPoint.zero
// 정확한 자체(glyph)의 범위를 구하고 그 범위의 CGRect 값을 구합니다.
let range = layoutManager.glyphRange(for: textContainer)
let textBounds = layoutManager.boundingRect(
forGlyphRange: range,
in: textContainer
)
// textOffset.x가 패딩을 제외한 부분부터 시작하도록 합니다.
let paddingWidth = (self.bounds.size.width - textBounds.size.width) / 2
if paddingWidth > 0 {
textOffset.x = paddingWidth
}
// 눌려진 정확한 포인트를 구합니다.
let newPoint = CGPoint(
x: point.x - textOffset.x,
y: point.y - textOffset.y
)
// textContainer내에서 newPoint 위치의 glyph index를 반환합니다
return layoutManager.glyphIndex(for: newPoint, in: textContainer)
}
}
2. 하이퍼링크가 있는 부분을 찾아 글씨체와 색을 바꾸고, attachment 속성도 추가해주는 configureLabel 함수를 정의한다.
func configureLabel(label: UILabel) {
guard let messageText = label.text else { return }
let mutableString = NSMutableAttributedString()
let normalAttributes: [NSMutableAttributedString.Key: Any] = [
.foregroundColor: <#TextColor#>,
.font: <#TextFont#>
]
var urlAttributes: [NSMutableAttributedString.Key: Any] = [
.foregroundColor: <#LinkTextColor#>,
.underlineStyle: NSUnderlineStyle.single.rawValue,
.font: <#LinkTextFont#>
]
let normalText = NSAttributedString(string: messageText, attributes: normalAttributes)
mutableString.append(normalText)
guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
let matches = detector.matches(
in: messageText,
options: [],
range: NSRange(location: 0, length: messageText.count)
)
for m in matches {
if let url = m.url {
urlAttributes[.attachment] = url
mutableString.setAttributes(urlAttributes, range: m.range)
}
}
label.attributedText = mutableString
}
3. hyperLink가 들어있는 label이 tap 되었을 때 실행할 함수를 정의한다.
@objc func dynamicLabelTapped(_ sender: UITapGestureRecognizer) {
let point = sender.location(in: label)
guard let selectedIndex = label.textIndex(at: point) else { return }
guard let attr = label.attributedText?.attributes(at: selectedIndex, effectiveRange: nil),
let url = attr[.attachment] as? URL else { return }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
4. Label이 터치를 인식할 수 있도록 UITapGestureRecognizer를 추가해준다.
let recognizer = UITapGestureRecognizer(target: self, action: #selector(dynamicLabelTapped(_:)))
label.addGestureRecognizer(recognizer)
Caller UILabel 객체를 hyperLink를 인식해 밑줄을 긋고, 링크 연결이 가능하도록 만드는 Extension 전문
extension UILabel {
/// 입력된 포지션에 따라 라벨의 문자열의 인덱스 반환
/// - Parameter point: 인덱스 값을 알고 싶은 CGPoint
func textIndex(at point: CGPoint) -> Int? {
guard let attributedText = attributedText else { return nil }
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: self.bounds.size)
let textStorage = NSTextStorage(attributedString: attributedText)
textStorage.addLayoutManager(layoutManager)
textContainer.lineFragmentPadding = 0.0
layoutManager.addTextContainer(textContainer)
var textOffset = CGPoint.zero
// 정확한 자체(glyph)의 범위를 구하고 그 범위의 CGRect 값을 구합니다.
let range = layoutManager.glyphRange(for: textContainer)
let textBounds = layoutManager.boundingRect(
forGlyphRange: range,
in: textContainer
)
// textOffset.x가 패딩을 제외한 부분부터 시작하도록 합니다.
let paddingWidth = (self.bounds.size.width - textBounds.size.width) / 2
if paddingWidth > 0 {
textOffset.x = paddingWidth
}
// 눌려진 정확한 포인트를 구합니다.
let newPoint = CGPoint(
x: point.x - textOffset.x,
y: point.y - textOffset.y
)
// textContainer내에서 newPoint 위치의 glyph index를 반환합니다
return layoutManager.glyphIndex(for: newPoint, in: textContainer)
}
func makeHyperLinkTappable() {
self.configureHyperLinkLabel()
let recognizer = UITapGestureRecognizer(target: self, action: #selector(labelTapped(_:)))
self.addGestureRecognizer(recognizer)
}
@objc func labelTapped(_ sender: UITapGestureRecognizer) {
let point = sender.location(in: self)
guard let selectedIndex = self.textIndex(at: point) else { return }
guard let attr = self.attributedText?.attributes(at: selectedIndex, effectiveRange: nil),
let url = attr[.attachment] as? URL else { return }
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
private func configureHyperLinkLabel() {
guard let messageText = self.text else { return }
let mutableString = NSMutableAttributedString()
let normalAttributes: [NSMutableAttributedString.Key: Any] = [
.foregroundColor: self.textColor!,
.font: self.font!
]
var urlAttributes: [NSMutableAttributedString.Key: Any] = [
.foregroundColor: self.textColor!,
.underlineStyle: NSUnderlineStyle.single.rawValue,
.font: self.font!
]
let normalText = NSAttributedString(string: messageText, attributes: normalAttributes)
mutableString.append(normalText)
guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
else { return }
let matches = detector.matches(
in: messageText,
options: [],
range: NSRange(location: 0, length: messageText.count)
)
for m in matches {
if let url = m.url {
urlAttributes[.attachment] = url
mutableString.setAttributes(urlAttributes, range: m.range)
}
}
self.attributedText = mutableString
}
}