Lop*_*ope 220 hyperlink nsattributedstring uilabel ios uitapgesturerecognizer
我一直在搜索这几个小时,但我失败了.我可能甚至不知道我应该寻找什么.
许多应用程序都有文本,在本文中是圆角矩形的Web超链接.当我点击它们UIWebView打开.让我感到困惑的是,他们经常有自定义链接,例如,如果单词以#开头,它也是可点击的,应用程序通过打开另一个视图来响应.我怎样才能做到这一点?是否有可能UILabel或我需要UITextView或其他什么?
nal*_*exn 196
一般来说,如果我们想在UILabel显示的文本中有一个可点击的链接,我们需要解决两个独立的任务:
第一个很容易.从iOS 6开始,UILabel支持显示属性字符串.您需要做的就是创建和配置NSMutableAttributedString的实例:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"String with a link" attributes:nil];
NSRange linkRange = NSMakeRange(14, 4); // for the word "link" in the string above
NSDictionary *linkAttributes = @{ NSForegroundColorAttributeName : [UIColor colorWithRed:0.05 green:0.4 blue:0.65 alpha:1.0],
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) };
[attributedString setAttributes:linkAttributes range:linkRange];
// Assign attributedText to UILabel
label.attributedText = attributedString;
Run Code Online (Sandbox Code Playgroud)
而已!上面的代码使UILabel显示带有链接的 String
现在我们应该检测此链接上的触摸.我们的想法是捕获UILabel内的所有水龙头,并确定水龙头的位置是否足够接近链路.为了捕捉触摸,我们可以为标签添加点击手势识别器.确保为标签启用userInteraction,默认情况下它已关闭:
label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];
Run Code Online (Sandbox Code Playgroud)
现在最复杂的东西:找出水龙头是在显示链接的位置而不是标签的任何其他部分.如果我们有单行UILabel,通过硬编码显示链接的区域边界可以相对容易地解决这个任务,但让我们更优雅地解决这个问题,对于一般情况 - 多线UILabel没有关于链接布局的初步知识.
其中一种方法是使用iOS 7中引入的Text Kit API的功能:
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
// Configure layoutManager and textStorage
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
// Configure textContainer
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = label.lineBreakMode;
textContainer.maximumNumberOfLines = label.numberOfLines;
Run Code Online (Sandbox Code Playgroud)
在类中的属性中保存已创建和配置的NSLayoutManager,NSTextContainer和NSTextStorage实例(很可能是UIViewController的后代) - 我们将在其他方法中使用它们.
现在,每次标签更改其框架时,请更新textContainer的大小:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.textContainer.size = self.label.bounds.size;
}
Run Code Online (Sandbox Code Playgroud)
最后,检测点击是否恰好在链接上:
- (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture
{
CGPoint locationOfTouchInLabel = [tapGesture locationInView:tapGesture.view];
CGSize labelSize = tapGesture.view.bounds.size;
CGRect textBoundingBox = [self.layoutManager usedRectForTextContainer:self.textContainer];
CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
CGPoint locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
locationOfTouchInLabel.y - textContainerOffset.y);
NSInteger indexOfCharacter = [self.layoutManager characterIndexForPoint:locationOfTouchInTextContainer
inTextContainer:self.textContainer
fractionOfDistanceBetweenInsertionPoints:nil];
NSRange linkRange = NSMakeRange(14, 4); // it's better to save the range somewhere when it was originally used for marking link in attributed string
if (NSLocationInRange(indexOfCharacter, linkRange)) {
// Open an URL, or handle the tap on the link in any other way
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://stackoverflow.com/"]];
}
}
Run Code Online (Sandbox Code Playgroud)
sam*_*ize 54
我正在扩展@NAlexN原始的详细解决方案,使用@zekel的优秀扩展UITapGestureRecognizer,并在Swift中提供.
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)
}
}
Run Code Online (Sandbox Code Playgroud)
设置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")
}
}
Run Code Online (Sandbox Code Playgroud)
重要信息:UILabel换行模式必须设置为按word/char换行.不知何故,NSTextContainer只有在换行模式下,才会假设文本是单行.
Ked*_*ape 44
老问题,但如果任何人都可以使用一个UITextView而不是一个UILabel,那么这很容易.将自动检测标准URL,电话号码等(并且可以点击).
但是,如果您需要自定义检测,也就是说,如果您希望在用户单击特定单词后能够调用任何自定义方法,则需要使用指向自定义URL方案NSAttributedStrings的NSLinkAttributeName属性(而不是默认情况下具有http url方案).Ray Wenderlich在这里介绍了它
引用上述链接中的代码:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"This is an example by @marcelofabri_"];
[attributedString addAttribute:NSLinkAttributeName
value:@"username://marcelofabri_"
range:[[attributedString string] rangeOfString:@"@marcelofabri_"]];
NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor greenColor],
NSUnderlineColorAttributeName: [UIColor lightGrayColor],
NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};
// assume that textView is a UITextView previously created (either by code or Interface Builder)
textView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;
textView.delegate = self;
Run Code Online (Sandbox Code Playgroud)
要检测这些链接点击,请执行以下操作:
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
if ([[URL scheme] isEqualToString:@"username"]) {
NSString *username = [URL host];
// do something with this username
// ...
return NO;
}
return YES; // let the system open this URL
}
Run Code Online (Sandbox Code Playgroud)
PS:确保你UITextView的selectable.
zek*_*kel 33
(我的回答建立在@ NAlexN的优秀答案之上.我不会在这里重复他对每一步的详细解释.)
我发现将UTabel文本作为UITapGestureRecognizer的类别添加支持是最方便和直接的.(你不具备使用的UITextView的数据检测,如一些答案建议.)
将以下方法添加到您的UITapGestureRecognizer类别:
/**
Returns YES if the tap gesture was within the specified range of the attributed text of the label.
*/
- (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange {
NSParameterAssert(label != nil);
CGSize labelSize = label.bounds.size;
// create instances of NSLayoutManager, NSTextContainer and NSTextStorage
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText];
// configure layoutManager and textStorage
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
// configure textContainer for the label
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = label.lineBreakMode;
textContainer.maximumNumberOfLines = label.numberOfLines;
textContainer.size = labelSize;
// find the tapped character location and compare it to the specified range
CGPoint locationOfTouchInLabel = [self locationInView:label];
CGRect textBoundingBox = [layoutManager usedRectForTextContainer:textContainer];
CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
CGPoint locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
locationOfTouchInLabel.y - textContainerOffset.y);
NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInTextContainer
inTextContainer:textContainer
fractionOfDistanceBetweenInsertionPoints:nil];
if (NSLocationInRange(indexOfCharacter, targetRange)) {
return YES;
} else {
return NO;
}
}
Run Code Online (Sandbox Code Playgroud)
// (in your view controller)
// create your label, gesture recognizer, attributed text, and get the range of the "link" in your label
myLabel.userInteractionEnabled = YES;
[myLabel addGestureRecognizer:
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleTapOnLabel:)]];
// create your attributed text and keep an ivar of your "link" text range
NSAttributedString *plainText;
NSAttributedString *linkText;
plainText = [[NSMutableAttributedString alloc] initWithString:@"Add label links with UITapGestureRecognizer"
attributes:nil];
linkText = [[NSMutableAttributedString alloc] initWithString:@" Learn more..."
attributes:@{
NSForegroundColorAttributeName:[UIColor blueColor]
}];
NSMutableAttributedString *attrText = [[NSMutableAttributedString alloc] init];
[attrText appendAttributedString:plainText];
[attrText appendAttributedString:linkText];
// ivar -- keep track of the target range so you can compare in the callback
targetRange = NSMakeRange(plainText.length, linkText.length);
Run Code Online (Sandbox Code Playgroud)
// handle the gesture recognizer callback and call the category method
- (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture {
BOOL didTapLink = [tapGesture didTapAttributedTextInLabel:myLabel
inRange:targetRange];
NSLog(@"didTapLink: %d", didTapLink);
}
Run Code Online (Sandbox Code Playgroud)
Cha*_*ble 20
UITextView支持OS3.0中的数据检测器,而UILabel不是.
如果您启用了数据检测器,UITextView并且您的文本包含URL,电话号码等,则它们将显示为链接.
Her*_*ber 13
将@ samwize的扩展转换为Swift 4:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
设置识别器(一旦你给文本和东西着色):
lblTermsOfUse.isUserInteractionEnabled = true
lblTermsOfUse.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTapOnLabel(_:))))
Run Code Online (Sandbox Code Playgroud)
...然后是手势识别器:
@objc func handleTapOnLabel(_ recognizer: UITapGestureRecognizer) {
guard let text = lblAgreeToTerms.attributedText?.string else {
return
}
if let range = text.range(of: NSLocalizedString("_onboarding_terms", comment: "terms")),
recognizer.didTapAttributedTextInLabel(label: lblAgreeToTerms, inRange: NSRange(range, in: text)) {
goToTermsAndConditions()
} else if let range = text.range(of: NSLocalizedString("_onboarding_privacy", comment: "privacy")),
recognizer.didTapAttributedTextInLabel(label: lblAgreeToTerms, inRange: NSRange(range, in: text)) {
goToPrivacyPolicy()
}
}
Run Code Online (Sandbox Code Playgroud)
Vla*_*mir 10
最简单可靠的方法是像Kedar Paranjape推荐的那样使用 UITextView 。基于Karl Nosworthy 的回答,我终于想出了一个简单的 UITextView 子类:
class LinkTextView: UITextView, UITextViewDelegate {
typealias Links = [String: String]
typealias OnLinkTap = (URL) -> Bool
var onLinkTap: OnLinkTap?
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
isEditable = false
isSelectable = true
isScrollEnabled = false //to have own size and behave like a label
delegate = self
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func addLinks(_ links: Links) {
guard attributedText.length > 0 else {
return
}
let mText = NSMutableAttributedString(attributedString: attributedText)
for (linkText, urlString) in links {
if linkText.count > 0 {
let linkRange = mText.mutableString.range(of: linkText)
mText.addAttribute(.link, value: urlString, range: linkRange)
}
}
attributedText = mText
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
return onLinkTap?(URL) ?? true
}
// to disable text selection
func textViewDidChangeSelection(_ textView: UITextView) {
textView.selectedTextRange = nil
}
Run Code Online (Sandbox Code Playgroud)
}
用法很简单:
let linkTextView = LinkTextView()
let tu = "Terms of Use"
let pp = "Privacy Policy"
linkTextView.text = "Please read the Some Company \(tu) and \(pp)"
linkTextView.addLinks([
tu: "https://some.com/tu",
pp: "https://some.com/pp"
])
linkTextView.onLinkTap = { url in
print("url: \(url)")
return true
}
Run Code Online (Sandbox Code Playgroud)
请注意,isScrollEnabled默认情况下为 false,因为在大多数情况下,我们需要具有自己大小且无需滚动的小型标签式视图。如果您想要可滚动的文本视图,只需将其设置为 true。
另请注意,与 UILabel 不同的 UITextView 具有默认文本填充。要删除它并使布局与 UILabel 相同,只需添加:linkTextView.textContainerInset = .zero
实现onLinkTap闭包是没有必要的,没有它,UIApplication 会自动打开 URL。
由于文本选择在大多数情况下是不可取的,但不能关闭它在委托方法中被关闭(感谢 Carson Vo)
正如我在这篇文章中提到的,这里是一个轻量级的库,我专门为UILabel FRHyperLabel中的链接创建.
为了达到这样的效果:
Lorem ipsum dolor坐下来,精致的adipistur elit.Pellentesque quis blandit eros,坐在amet vehicula justo.在urna neque的Nam.Maecenas ac sem eu sem porta dictum nec vel tellus.
使用代码:
//Step 1: Define a normal attributed string for non-link texts
NSString *string = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque quis blandit eros, sit amet vehicula justo. Nam at urna neque. Maecenas ac sem eu sem porta dictum nec vel tellus.";
NSDictionary *attributes = @{NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]};
label.attributedText = [[NSAttributedString alloc]initWithString:string attributes:attributes];
//Step 2: Define a selection handler block
void(^handler)(FRHyperLabel *label, NSString *substring) = ^(FRHyperLabel *label, NSString *substring){
NSLog(@"Selected: %@", substring);
};
//Step 3: Add link substrings
[label setLinksForSubstrings:@[@"Lorem", @"Pellentesque", @"blandit", @"Maecenas"] withLinkHandler:handler];
Run Code Online (Sandbox Code Playgroud)
小智 7
我创建了名为ResponsiveLabel的 UILabel子类,它基于iOS 7中引入的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];
Run Code Online (Sandbox Code Playgroud)
如果要使字符串可单击,可以这样做.此代码将属性应用于字符串"text"的每次出现.
PatternTapResponder tapResponder = ^(NSString *string) {
NSLog(@"tapped = %@",string);
};
[self.customLabel enableStringDetection:@"text" withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor],
RLTapResponderAttributeName: tapResponder}];
Run Code Online (Sandbox Code Playgroud)
这是 NAlexN 答案的快速版本。
class TapabbleLabel: UILabel {
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
var textStorage = NSTextStorage() {
didSet {
textStorage.addLayoutManager(layoutManager)
}
}
var onCharacterTapped: ((label: UILabel, characterIndex: Int) -> Void)?
let tapGesture = UITapGestureRecognizer()
override var attributedText: NSAttributedString? {
didSet {
if let attributedText = attributedText {
textStorage = NSTextStorage(attributedString: attributedText)
} else {
textStorage = NSTextStorage()
}
}
}
override var lineBreakMode: NSLineBreakMode {
didSet {
textContainer.lineBreakMode = lineBreakMode
}
}
override var numberOfLines: Int {
didSet {
textContainer.maximumNumberOfLines = numberOfLines
}
}
/**
Creates a new view with the passed coder.
:param: aDecoder The a decoder
:returns: the created new view.
*/
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
/**
Creates a new view with the passed frame.
:param: frame The frame
:returns: the created new view.
*/
override init(frame: CGRect) {
super.init(frame: frame)
setUp()
}
/**
Sets up the view.
*/
func setUp() {
userInteractionEnabled = true
layoutManager.addTextContainer(textContainer)
textContainer.lineFragmentPadding = 0
textContainer.lineBreakMode = lineBreakMode
textContainer.maximumNumberOfLines = numberOfLines
tapGesture.addTarget(self, action: #selector(TapabbleLabel.labelTapped(_:)))
addGestureRecognizer(tapGesture)
}
override func layoutSubviews() {
super.layoutSubviews()
textContainer.size = bounds.size
}
func labelTapped(gesture: UITapGestureRecognizer) {
guard gesture.state == .Ended else {
return
}
let locationOfTouch = gesture.locationInView(gesture.view)
let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)
let textContainerOffset = CGPoint(x: (bounds.width - textBoundingBox.width) / 2 - textBoundingBox.minX,
y: (bounds.height - textBoundingBox.height) / 2 - textBoundingBox.minY)
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouch.x - textContainerOffset.x,
y: locationOfTouch.y - textContainerOffset.y)
let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer,
inTextContainer: textContainer,
fractionOfDistanceBetweenInsertionPoints: nil)
onCharacterTapped?(label: self, characterIndex: indexOfCharacter)
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以在您的viewDidLoad方法中创建该类的实例,如下所示:
let label = TapabbleLabel()
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[view]-|",
options: [], metrics: nil, views: ["view" : label]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[view]-|",
options: [], metrics: nil, views: ["view" : label]))
let attributedString = NSMutableAttributedString(string: "String with a link", attributes: nil)
let linkRange = NSMakeRange(14, 4); // for the word "link" in the string above
let linkAttributes: [String : AnyObject] = [
NSForegroundColorAttributeName : UIColor.blueColor(), NSUnderlineStyleAttributeName : NSUnderlineStyle.StyleSingle.rawValue,
NSLinkAttributeName: "http://www.apple.com"]
attributedString.setAttributes(linkAttributes, range:linkRange)
label.attributedText = attributedString
label.onCharacterTapped = { label, characterIndex in
if let attribute = label.attributedText?.attribute(NSLinkAttributeName, atIndex: characterIndex, effectiveRange: nil) as? String,
let url = NSURL(string: attribute) {
UIApplication.sharedApplication().openURL(url)
}
}
Run Code Online (Sandbox Code Playgroud)
最好有一个自定义属性在点击角色时使用。现在,它是NSLinkAttributeName, 但可以是任何东西,除了打开 url 之外,您还可以使用该值来做其他事情,您可以执行任何自定义操作。
有些答案对我没有预期的效果。这是支持textAlignment多行和多行的Swift 解决方案。不需要子类化,只需要这个UITapGestureRecognizer扩展:
import UIKit
extension UITapGestureRecognizer {
func didTapAttributedString(_ string: String, in label: UILabel) -> Bool {
guard let text = label.text else {
return false
}
let range = (text as NSString).range(of: string)
return self.didTapAttributedText(label: label, inRange: range)
}
private func didTapAttributedText(label: UILabel, inRange targetRange: NSRange) -> Bool {
guard let attributedText = label.attributedText else {
assertionFailure("attributedText must be set")
return false
}
let textContainer = createTextContainer(for: label)
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(textContainer)
let textStorage = NSTextStorage(attributedString: attributedText)
if let font = label.font {
textStorage.addAttribute(NSAttributedString.Key.font, value: font, range: NSMakeRange(0, attributedText.length))
}
textStorage.addLayoutManager(layoutManager)
let locationOfTouchInLabel = location(in: label)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let alignmentOffset = aligmentOffset(for: label)
let xOffset = ((label.bounds.size.width - textBoundingBox.size.width) * alignmentOffset) - textBoundingBox.origin.x
let yOffset = ((label.bounds.size.height - textBoundingBox.size.height) * alignmentOffset) - textBoundingBox.origin.y
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - xOffset, y: locationOfTouchInLabel.y - yOffset)
let characterTapped = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
let lineTapped = Int(ceil(locationOfTouchInLabel.y / label.font.lineHeight)) - 1
let rightMostPointInLineTapped = CGPoint(x: label.bounds.size.width, y: label.font.lineHeight * CGFloat(lineTapped))
let charsInLineTapped = layoutManager.characterIndex(for: rightMostPointInLineTapped, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
return characterTapped < charsInLineTapped ? targetRange.contains(characterTapped) : false
}
private func createTextContainer(for label: UILabel) -> NSTextContainer {
let textContainer = NSTextContainer(size: label.bounds.size)
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
return textContainer
}
private func aligmentOffset(for label: UILabel) -> CGFloat {
switch label.textAlignment {
case .left, .natural, .justified:
return 0.0
case .center:
return 0.5
case .right:
return 1.0
@unknown default:
return 0.0
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
class ViewController: UIViewController {
@IBOutlet var label : UILabel!
let selectableString1 = "consectetur"
let selectableString2 = "cupidatat"
override func viewDidLoad() {
super.viewDidLoad()
let text = "Lorem ipsum dolor sit amet, \(selectableString1) adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat \(selectableString2) non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
label.attributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: text))
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(labelTapped))
label.addGestureRecognizer(tapGesture)
label.isUserInteractionEnabled = true
}
@objc func labelTapped(gesture: UITapGestureRecognizer) {
if gesture.didTapAttributedString(selectableString1, in: label) {
print("\(selectableString1) tapped")
} else if gesture.didTapAttributedString(selectableString2, in: label) {
print("\(selectableString2) tapped")
} else {
print("Text tapped")
}
}
}
Run Code Online (Sandbox Code Playgroud)
以下是超链接UILabel的示例代码:来源:http://sickprogrammersarea.blogspot.in/2014/03/adding-links-to-uilabel.html
#import "ViewController.h"
#import "TTTAttributedLabel.h"
@interface ViewController ()
@end
@implementation ViewController
{
UITextField *loc;
TTTAttributedLabel *data;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(5, 20, 80, 25) ];
[lbl setText:@"Text:"];
[lbl setFont:[UIFont fontWithName:@"Verdana" size:16]];
[lbl setTextColor:[UIColor grayColor]];
loc=[[UITextField alloc] initWithFrame:CGRectMake(4, 20, 300, 30)];
//loc.backgroundColor = [UIColor grayColor];
loc.borderStyle=UITextBorderStyleRoundedRect;
loc.clearButtonMode=UITextFieldViewModeWhileEditing;
//[loc setText:@"Enter Location"];
loc.clearsOnInsertion = YES;
loc.leftView=lbl;
loc.leftViewMode=UITextFieldViewModeAlways;
[loc setDelegate:self];
[self.view addSubview:loc];
[loc setRightViewMode:UITextFieldViewModeAlways];
CGRect frameimg = CGRectMake(110, 70, 70,30);
UIButton *srchButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
srchButton.frame=frameimg;
[srchButton setTitle:@"Go" forState:UIControlStateNormal];
[srchButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
srchButton.backgroundColor=[UIColor clearColor];
[srchButton addTarget:self action:@selector(go:) forControlEvents:UIControlEventTouchDown];
[self.view addSubview:srchButton];
data = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(5, 120,self.view.frame.size.width,200) ];
[data setFont:[UIFont fontWithName:@"Verdana" size:16]];
[data setTextColor:[UIColor blackColor]];
data.numberOfLines=0;
data.delegate = self;
data.enabledTextCheckingTypes=NSTextCheckingTypeLink|NSTextCheckingTypePhoneNumber;
[self.view addSubview:data];
}
- (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url
{
NSString *val=[[NSString alloc]initWithFormat:@"%@",url];
if ([[url scheme] hasPrefix:@"mailto"]) {
NSLog(@" mail URL Selected : %@",url);
MFMailComposeViewController *comp=[[MFMailComposeViewController alloc]init];
[comp setMailComposeDelegate:self];
if([MFMailComposeViewController canSendMail])
{
NSString *recp=[[val substringToIndex:[val length]] substringFromIndex:7];
NSLog(@"Recept : %@",recp);
[comp setToRecipients:[NSArray arrayWithObjects:recp, nil]];
[comp setSubject:@"From my app"];
[comp setMessageBody:@"Hello bro" isHTML:NO];
[comp setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentViewController:comp animated:YES completion:nil];
}
}
else{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:val]];
}
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
if(error)
{
UIAlertView *alrt=[[UIAlertView alloc]initWithTitle:@"Erorr" message:@"Some error occureed" delegate:nil cancelButtonTitle:@"" otherButtonTitles:nil, nil];
[alrt show];
[self dismissViewControllerAnimated:YES completion:nil];
}
else{
[self dismissViewControllerAnimated:YES completion:nil];
}
}
- (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithPhoneNumber:(NSString *)phoneNumber
{
NSLog(@"Phone Number Selected : %@",phoneNumber);
UIDevice *device = [UIDevice currentDevice];
if ([[device model] isEqualToString:@"iPhone"] ) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tel:%@",phoneNumber]]];
} else {
UIAlertView *Notpermitted=[[UIAlertView alloc] initWithTitle:@"Alert" message:@"Your device doesn't support this feature." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[Notpermitted show];
}
}
-(void)go:(id)sender
{
[data setText:loc.text];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"Reached");
[loc resignFirstResponder];
}
Run Code Online (Sandbox Code Playgroud)
在Swift 3中工作,在这里粘贴整个代码
//****Make sure the textview 'Selectable' = checked, and 'Editable = Unchecked'
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
@IBOutlet var theNewTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
//****textview = Selectable = checked, and Editable = Unchecked
theNewTextView.delegate = self
let theString = NSMutableAttributedString(string: "Agree to Terms")
let theRange = theString.mutableString.range(of: "Terms")
theString.addAttribute(NSLinkAttributeName, value: "ContactUs://", range: theRange)
let theAttribute = [NSForegroundColorAttributeName: UIColor.blue, NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue] as [String : Any]
theNewTextView.linkTextAttributes = theAttribute
theNewTextView.attributedText = theString
theString.setAttributes(theAttribute, range: theRange)
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
if (URL.scheme?.hasPrefix("ContactUs://"))! {
return false //interaction not allowed
}
//*** Set storyboard id same as VC name
self.navigationController!.pushViewController((self.storyboard?.instantiateViewController(withIdentifier: "TheLastViewController"))! as UIViewController, animated: true)
return true
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
189257 次 |
| 最近记录: |