许多应用程序都有文本,文本中是圆角矩形的web超链接,当我点击它们时,UIWebView就会打开。让我困惑的是,他们经常有自定义链接,例如,如果单词以#开头,它也是可点击的,应用程序通过打开另一个视图来响应。我该怎么做呢?是否可以用UILabel或者我需要UITextView或者其他什么?
当前回答
我遵循这个版本,
斯威夫特4:
import Foundation
class AELinkedClickableUILabel: UILabel {
typealias YourCompletion = () -> Void
var linkedRange: NSRange!
var completion: YourCompletion?
@objc func linkClicked(sender: UITapGestureRecognizer){
if let completionBlock = completion {
let textView = UITextView(frame: self.frame)
textView.text = self.text
textView.attributedText = self.attributedText
let index = textView.layoutManager.characterIndex(for: sender.location(in: self),
in: textView.textContainer,
fractionOfDistanceBetweenInsertionPoints: nil)
if linkedRange.lowerBound <= index && linkedRange.upperBound >= index {
completionBlock()
}
}
}
/**
* This method will be used to set an attributed text specifying the linked text with a
* handler when the link is clicked
*/
public func setLinkedTextWithHandler(text:String, link: String, handler: @escaping ()->()) -> Bool {
let attributextText = NSMutableAttributedString(string: text)
let foundRange = attributextText.mutableString.range(of: link)
if foundRange.location != NSNotFound {
self.linkedRange = foundRange
self.completion = handler
attributextText.addAttribute(NSAttributedStringKey.link, value: text, range: foundRange)
self.isUserInteractionEnabled = true
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(linkClicked(sender:))))
return true
}
return false
}
}
电话的例子:
button.setLinkedTextWithHandler(text: "This website (stackoverflow.com) is awesome", link: "stackoverflow.com")
{
// show popup or open to link
}
其他回答
我创建了一个名为ResponsiveLabel的UILabel子类,它是基于ios7中引入的textkit API。它使用了NAlexN建议的相同方法。它提供了在文本中指定搜索模式的灵活性。可以指定应用于这些模式的样式,以及在敲击模式时要执行的操作。
//Detects email in text
NSString *emailRegexString = @"[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}";
NSError *error;
NSRegularExpression *regex = [[NSRegularExpression alloc]initWithPattern:emailRegexString options:0 error:&error];
PatternDescriptor *descriptor = [[PatternDescriptor alloc]initWithRegex:regex withSearchType:PatternSearchTypeAll withPatternAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];
[self.customLabel enablePatternDetection:descriptor];
如果你想让一个字符串可点击,你可以这样做。这段代码将属性应用到字符串“text”的每个出现处。
PatternTapResponder tapResponder = ^(NSString *string) {
NSLog(@"tapped = %@",string);
};
[self.customLabel enableStringDetection:@"text" withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor],
RLTapResponderAttributeName: tapResponder}];
UIButtonTypeCustom是一个可点击的标签,如果你没有为它设置任何图像。
我找到了另一个解决方案:
我找到了一种方法来检测你从互联网上找到的html文本链接,你把它转换成nsattributeString:
func htmlAttributedString(fontSize: CGFloat = 17.0) -> NSAttributedString? {
let fontName = UIFont.systemFont(ofSize: fontSize).fontName
let string = self.appending(String(format: "<style>body{font-family: '%@'; font-size:%fpx;}</style>", fontName, fontSize))
guard let data = string.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
guard let html = try? NSMutableAttributedString (
data: data,
options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil) else { return nil }
return html
}
我的方法允许您检测超链接,而无需指定它们。
first you create an extension of the tapgesturerecognizer : extension UITapGestureRecognizer { func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool { guard let attrString = label.attributedText else { return false } let layoutManager = NSLayoutManager() let textContainer = NSTextContainer(size: .zero) let textStorage = NSTextStorage(attributedString: attrString) layoutManager.addTextContainer(textContainer) textStorage.addLayoutManager(layoutManager) textContainer.lineFragmentPadding = 0 textContainer.lineBreakMode = label.lineBreakMode textContainer.maximumNumberOfLines = label.numberOfLines let labelSize = label.bounds.size textContainer.size = labelSize let locationOfTouchInLabel = self.location(in: label) let textBoundingBox = layoutManager.usedRect(for: textContainer) let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y) let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y) let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) return NSLocationInRange(indexOfCharacter, targetRange) } }
然后在你的视图控制器中,你创建了一个url和范围列表来存储属性文本包含的所有链接和范围:
var listurl : [String] = []
var listURLRange : [NSRange] = []
找到URL和URLRange,你可以使用:
fun findLinksAndRange(attributeString : NSAttributeString){
notification.enumerateAttribute(NSAttributedStringKey.link , in: NSMakeRange(0, notification.length), options: [.longestEffectiveRangeNotRequired]) { value, range, isStop in
if let value = value {
print("\(value) found at \(range.location)")
let stringValue = "\(value)"
listurl.append(stringValue)
listURLRange.append(range)
}
}
westlandNotifcationLabel.addGestureRecognizer(UITapGestureRecognizer(target : self, action: #selector(handleTapOnLabel(_:))))
}
然后你实现了手柄tap:
@objc func handleTapOnLabel(_ recognizer: UITapGestureRecognizer) {
for index in 0..<listURLRange.count{
if recognizer.didTapAttributedTextInLabel(label: westlandNotifcationLabel, inRange: listURLRange[index]) {
goToWebsite(url : listurl[index])
}
}
}
func goToWebsite(url : String){
if let websiteUrl = URL(string: url){
if #available(iOS 10, *) {
UIApplication.shared.open(websiteUrl, options: [:],
completionHandler: {
(success) in
print("Open \(websiteUrl): \(success)")
})
} else {
let success = UIApplication.shared.openURL(websiteUrl)
print("Open \(websiteUrl): \(success)")
}
}
}
开始了!
我希望这个解决方案能帮助你,就像它帮助我一样。
我扩展了@samwize的答案来处理多行UILabel,并给出了一个使用UIButton的例子
extension UITapGestureRecognizer {
func didTapAttributedTextInButton(button: UIButton, inRange targetRange: NSRange) -> Bool {
guard let label = button.titleLabel else { return false }
return didTapAttributedTextInLabel(label, inRange: targetRange)
}
func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
let textStorage = NSTextStorage(attributedString: label.attributedText!)
// Configure layoutManager and textStorage
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
// Configure textContainer
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
let labelSize = label.bounds.size
textContainer.size = labelSize
// Find the tapped character location and compare it to the specified range
let locationOfTouchInLabel = self.locationInView(label)
let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)
let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
let locationOfTouchInTextContainer = CGPointMake((locationOfTouchInLabel.x - textContainerOffset.x),
0 );
// Adjust for multiple lines of text
let lineModifier = Int(ceil(locationOfTouchInLabel.y / label.font.lineHeight)) - 1
let rightMostFirstLinePoint = CGPointMake(labelSize.width, 0)
let charsPerLine = layoutManager.characterIndexForPoint(rightMostFirstLinePoint, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
let adjustedRange = indexOfCharacter + (lineModifier * charsPerLine)
return NSLocationInRange(adjustedRange, targetRange)
}
}
我正在扩展@NAlexN原始的详细解决方案,与@zekel优秀的UITapGestureRecognizer扩展,并在Swift中提供。
Extending UITapGestureRecognizer
extension UITapGestureRecognizer {
func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
let textStorage = NSTextStorage(attributedString: label.attributedText!)
// Configure layoutManager and textStorage
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
// Configure textContainer
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
let labelSize = label.bounds.size
textContainer.size = labelSize
// Find the tapped character location and compare it to the specified range
let locationOfTouchInLabel = self.location(in: label)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let textContainerOffset = CGPoint(
x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y
)
let locationOfTouchInTextContainer = CGPoint(
x: locationOfTouchInLabel.x - textContainerOffset.x,
y: locationOfTouchInLabel.y - textContainerOffset.y
)
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
return NSLocationInRange(indexOfCharacter, targetRange)
}
}
使用
设置UIGestureRecognizer发送动作到tapLabel:,你可以检测目标范围是否在myLabel上被点击。
@IBAction func tapLabel(gesture: UITapGestureRecognizer) {
if gesture.didTapAttributedTextInLabel(myLabel, inRange: targetRange1) {
print("Tapped targetRange1")
} else if gesture.didTapAttributedTextInLabel(myLabel, inRange: targetRange2) {
print("Tapped targetRange2")
} else {
print("Tapped none")
}
}
重要提示:UILabel换行模式必须设置为按word/char换行。以某种方式,只有当换行模式为其他模式时,NSTextContainer才会假定文本为单行。
推荐文章
- 是否有可能更新一个本地化的故事板的字符串?
- 以编程方式获取Bundle Identifier
- 为iPad和iPhone设计输入按钮
- 如何在我的iPhone应用程序中使用NSError ?
- 我的预发行应用已经在iTunes Connect中“处理”了一周多,这是怎么回事?
- Xcode iOS项目只显示“我的Mac 64位”,但不显示模拟器或设备
- Objective-C中的自动引用计数不能防止或减少什么样的泄漏?
- 在Xcode 9中如何从打开的多个模拟器中退出或关闭单个模拟器?
- 如何使用Swift播放声音?
- UINavigationBar自定义返回按钮没有标题
- 如何精确地以毫秒为单位记录方法的执行时间?
- 在整个UIWindow中获取UIView的位置
- 如何解散ViewController在Swift?
- 如何创建在移动设备上呼叫电话号码的超链接?
- 保存字符串到NSUserDefaults?