许多应用程序都有文本,文本中是圆角矩形的web超链接,当我点击它们时,UIWebView就会打开。让我困惑的是,他们经常有自定义链接,例如,如果单词以#开头,它也是可点击的,应用程序通过打开另一个视图来响应。我该怎么做呢?是否可以用UILabel或者我需要UITextView或者其他什么?
当前回答
- (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange{
NSLayoutManager *layoutManager = [NSLayoutManager new];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText];
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = label.lineBreakMode;
textContainer.maximumNumberOfLines = label.numberOfLines;
CGSize labelSize = label.bounds.size;
textContainer.size = labelSize;
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);
NSUInteger indexOfCharacter =[layoutManager characterIndexForPoint:locationOfTouchInTextContainer inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:nil];
return NSLocationInRange(indexOfCharacter, targetRange);
}
其他回答
下面是超链接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];
}
这是沙玛林。基于Kedar的答案的iOS c#实现。
MyClickableTextViewWithCustomUrlScheme实现与ShouldInteractWithUrl覆盖:
// Inspired from https://stackoverflow.com/a/44112932/15186
internal class MyClickableTextViewWithCustomUrlScheme : UITextView, IUITextViewDelegate
{
public MyClickableTextViewWithCustomUrlScheme()
{
Initialize();
}
public MyClickableTextViewWithCustomUrlScheme(Foundation.NSCoder coder) : base(coder)
{
Initialize();
}
public MyClickableTextViewWithCustomUrlScheme(Foundation.NSObjectFlag t) : base(t)
{
Initialize();
}
public MyClickableTextViewWithCustomUrlScheme(IntPtr handle) : base(handle)
{
Initialize();
}
public MyClickableTextViewWithCustomUrlScheme(CoreGraphics.CGRect frame) : base(frame)
{
Initialize();
}
public MyClickableTextViewWithCustomUrlScheme(CoreGraphics.CGRect frame, NSTextContainer textContainer) : base(frame, textContainer)
{
Initialize();
}
void Initialize()
{
Delegate = this;
}
[Export("textView:shouldInteractWithURL:inRange:")]
public new bool ShouldInteractWithUrl(UITextView textView, NSUrl URL, NSRange characterRange)
{
if (URL.Scheme.CompareTo(@"username") == 0)
{
// Launch the Activity
return false;
}
// The system will handle the URL
return base.ShouldInteractWithUrl(textView, URL, characterRange);
}
}
在c#中转换的objective-C代码变成:
MyClickableTextViewWithCustomUrlScheme uiHabitTile = new MyClickableTextViewWithCustomUrlScheme();
uiHabitTile.Selectable = true;
uiHabitTile.ScrollEnabled = false;
uiHabitTile.Editable = false;
// https://stackoverflow.com/a/34014655/15186
string wholeTitle = @"This is an example by marcelofabri";
NSMutableAttributedString attributedString = new NSMutableAttributedString(wholeTitle);
attributedString.AddAttribute(UIStringAttributeKey.Link,
new NSString("username://marcelofabri"),
attributedString.Value.RangeOfString(@"marcelofabri")
);
NSMutableDictionary<NSString, NSObject> linkAttributes = new NSMutableDictionary<NSString, NSObject>();
linkAttributes[UIStringAttributeKey.ForegroundColor] = UIColor.Green;
linkAttributes[UIStringAttributeKey.UnderlineColor] = UIColor.LightGray;
linkAttributes[UIStringAttributeKey.UnderlineStyle] = new NSNumber((short)NSUnderlineStyle.PatternSolid);
uiHabitTile.AttributedText = attributedString;
确保将Editable = false和Selectable = true设置为能够单击链接。
同样,ScrollEnabled = true允许textview正确地调整其高度大小。
是的,这是可能的,尽管一开始很困惑。我将进一步向您展示如何甚至可以单击文本中的任何区域。
使用这个方法,你可以有一个UI标签,如下所示:
多行友好 Autoshrink友好 可点击友好型(是的,甚至是单个角色) 斯威夫特5
步骤1:
使UILabel具有'Truncate Tail'的换行属性,并设置最小字体比例。
如果你不熟悉字体比例,请记住以下规则:
minimumFontSize/defaultFontSize = fontscale
在我的例子中,我希望7.2是最小字体大小,而我的起始字体大小是36。因此7.2 / 36 = 0.2
步骤2:
如果你不关心标签是可点击的,只是想要一个工作多行标签你就完成了!
然而,如果你想要标签是可点击阅读…
添加以下扩展我创建
extension UILabel {
func setOptimalFontSize(maxFontSize:CGFloat,text:String){
let width = self.bounds.size.width
var font_size:CGFloat = maxFontSize //Set the maximum font size.
var stringSize = NSString(string: text).size(withAttributes: [.font : self.font.withSize(font_size)])
while(stringSize.width > width){
font_size = font_size - 1
stringSize = NSString(string: text).size(withAttributes: [.font : self.font.withSize(font_size)])
}
self.font = self.font.withSize(font_size)//Forcefully change font to match what it would be graphically.
}
}
它的用法是这样的(只需将<Label>替换为您实际的标签名):
<Label>.setOptimalFontSize(maxFontSize: 36.0, text: formula)
这个扩展是需要的,因为自动收缩不会改变标签的'字体'属性后,它自动收缩,所以你必须通过计算它通过使用.size(withAttributes)函数模拟它的大小将与特定的字体。
这是必要的,因为检测在标签上单击的位置的解决方案需要知道确切的字体大小。
步骤3:
添加以下扩展名:
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 mutableAttribString = NSMutableAttributedString(attributedString: label.attributedText!)
mutableAttribString.addAttributes([NSAttributedString.Key.font: label.font!], range: NSRange(location: 0, length: label.attributedText!.length))
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 6
paragraphStyle.lineBreakMode = .byTruncatingTail
paragraphStyle.alignment = .center
mutableAttribString.addAttributes([.paragraphStyle: paragraphStyle], range: NSMakeRange(0, mutableAttribString.string.count))
let textStorage = NSTextStorage(attributedString: mutableAttribString)
// Configure textContainer
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
// Configure layoutManager and textStorage
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
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 = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
//(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
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 = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
// locationOfTouchInLabel.y - textContainerOffset.y);
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
print("IndexOfCharacter=",indexOfCharacter)
print("TargetRange=",targetRange)
return NSLocationInRange(indexOfCharacter, targetRange)
}
}
您将需要修改这个扩展为您的特定多行情况。在我的例子中,您将注意到我使用了段落样式。
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 6
paragraphStyle.lineBreakMode = .byTruncatingTail
paragraphStyle.alignment = .center
mutableAttribString.addAttributes([.paragraphStyle: paragraphStyle], range: NSMakeRange(0, mutableAttribString.string.count))
确保在扩展中将此更改为您实际使用的行间距,以便一切计算正确。
步骤4:
添加gestureRecognizer到标签在viewDidLoad或你认为合适的地方,就像这样(只需替换< label >与你的标签名再次:
<Label>.addGestureRecognizer(UITapGestureRecognizer(target:self, action: #selector(tapLabel(gesture:))))
这是我的tapLabel函数的一个简化示例(只需将<Label>替换为您的UILabel名称):
@IBAction func tapLabel(gesture: UITapGestureRecognizer) {
guard let text = <Label>.attributedText?.string else {
return
}
let click_range = text.range(of: "(α/β)")
if gesture.didTapAttributedTextInLabel(label: <Label>, inRange: NSRange(click_range!, in: text)) {
print("Tapped a/b")
}else {
print("Tapped none")
}
}
在我的例子中,我的字符串是BED = N * d * [RBE + (d / (α/β))],所以我只是在这种情况下得到α/β的范围。您可以在字符串中添加“\n”以添加换行符和任何您想要的文本,并测试此以在下一行中找到字符串,它仍然会找到它并正确地检测点击!
就是这样!你完成了。享受多行可点击标签。
老问题,但如果任何人都可以使用UITextView而不是UILabel,那就很容易了。标准网址,电话号码等将自动检测(并可点击)。
然而,如果你需要自定义检测,也就是说,如果你想在用户点击一个特定的单词后能够调用任何自定义方法,你需要使用NSAttributedStrings和一个NSLinkAttributeName属性,它将指向一个自定义URL方案(而不是在默认情况下使用http URL方案)。雷·温德里奇在这里报道
引用上述链接中的代码:
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;
要检测这些链接点击,实现这个:
- (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
}
PS:确保你的UITextView是可选的。
我扩展了@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)
}
}
推荐文章
- 是否有可能更新一个本地化的故事板的字符串?
- 以编程方式获取Bundle Identifier
- 为iPad和iPhone设计输入按钮
- 如何在我的iPhone应用程序中使用NSError ?
- 我的预发行应用已经在iTunes Connect中“处理”了一周多,这是怎么回事?
- Xcode iOS项目只显示“我的Mac 64位”,但不显示模拟器或设备
- Objective-C中的自动引用计数不能防止或减少什么样的泄漏?
- 在Xcode 9中如何从打开的多个模拟器中退出或关闭单个模拟器?
- 如何使用Swift播放声音?
- UINavigationBar自定义返回按钮没有标题
- 如何精确地以毫秒为单位记录方法的执行时间?
- 在整个UIWindow中获取UIView的位置
- 如何解散ViewController在Swift?
- 如何创建在移动设备上呼叫电话号码的超链接?
- 保存字符串到NSUserDefaults?