iOS的UITextView或UILabel与可点击链接的行动

我想用一些带有2个可点击链接的文本来制作UILabel或者UITextView 。 没有链接到网页,但我想链接这样的行动,我会用UIButton做。 我所看到的所有例子都是网页浏览的链接,但我不想要。 同样,文本将被翻译成其他语言,所以职位必须是dynamic的。

想要做到这一点:

在这里输入图像描述

我需要解决这个完全相同的问题:与这两个链接非常相​​似的文本,在多行,需要它能够被翻译成任何语言(包括不同的单词顺序等)。 我刚刚解决了,让我分享我是如何做到的。

最初我以为我应该创build属性文本,然后将水龙头的触摸位置映射到该文本中的区域。 虽然我认为这是可行的,但我也认为这是一个太复杂的方法。

这是我最终做的:

概要:

  • 在你的英文信息中有非常基本的自定义标记,所以你可以parsing出不同的部分
  • 指导您的翻译员离开标记并翻译其余部分
  • 有一个UIView可以作为这个消息的容器
  • 分开打破你的英文信息,从正常的文字和可点击的文字中分离出来
  • 对于每个部分在容器UIView上创build一个UILabel
  • 对于可点击的部分,设置样式,允许用户交互并创build轻击手势识别器
  • 做一些非常基本的簿记,以完美的字眼

详情:

在视图控制器的viewDidLoad我把这个:

 [self buildAgreeTextViewFromString:NSLocalizedString(@"I agree to the #<ts>terms of service# and #<pp>privacy policy#", @"PLEASE NOTE: please translate \"terms of service\" and \"privacy policy\" as well, and leave the #<ts># and #<pp># around your translations just as in the English version of this message.")]; 

我打电话的方法,将build立消息。 请注意我提出的标记。 你当然可以自己创造,但关键是我也标记每个可点击区域的结束,因为它们跨越了多个单词。

以下是将这些信息放在一起的方法 – 请参见下文。 首先我把#字符(或者@"#"string)的英文信息分解出来。 这样我得到我需要分别创build一个标签的每一块。 我循环他们,寻找我的<ts><pp>基本标记来检测哪些片断是什么的链接。 如果我正在处理的文本块是一个链接,那么我稍微修改一下,然后为它设置一个轻击手势识别器。 当然,我也删除标记字符。 我认为这是一个非常简单的方法来做到这一点。

请注意一些细节,例如我如何处理空格:我只是从(本地化)string中获取空格。 如果没有空格(中文,日文),那么块之间也不会有空格。 如果有空格,则根据需要自动将空格分隔出来(例如英文)。 当我必须在下一行的开始处放置一个单词时,我确实需要确保从该文本中删除任何空格前缀,否则它将无法正确alignment。

 - (void)buildAgreeTextViewFromString:(NSString *)localizedString { // 1. Split the localized string on the # sign: NSArray *localizedStringPieces = [localizedString componentsSeparatedByString:@"#"]; // 2. Loop through all the pieces: NSUInteger msgChunkCount = localizedStringPieces ? localizedStringPieces.count : 0; CGPoint wordLocation = CGPointMake(0.0, 0.0); for (NSUInteger i = 0; i < msgChunkCount; i++) { NSString *chunk = [localizedStringPieces objectAtIndex:i]; if ([chunk isEqualToString:@""]) { continue; // skip this loop if the chunk is empty } // 3. Determine what type of word this is: BOOL isTermsOfServiceLink = [chunk hasPrefix:@"<ts>"]; BOOL isPrivacyPolicyLink = [chunk hasPrefix:@"<pp>"]; BOOL isLink = (BOOL)(isTermsOfServiceLink || isPrivacyPolicyLink); // 4. Create label, styling dependent on whether it's a link: UILabel *label = [[UILabel alloc] init]; label.font = [UIFont systemFontOfSize:15.0f]; label.text = chunk; label.userInteractionEnabled = isLink; if (isLink) { label.textColor = [UIColor colorWithRed:110/255.0f green:181/255.0f blue:229/255.0f alpha:1.0]; label.highlightedTextColor = [UIColor yellowColor]; // 5. Set tap gesture for this clickable text: SEL selectorAction = isTermsOfServiceLink ? @selector(tapOnTermsOfServiceLink:) : @selector(tapOnPrivacyPolicyLink:); UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:selectorAction]; [label addGestureRecognizer:tapGesture]; // Trim the markup characters from the label: if (isTermsOfServiceLink) label.text = [label.text stringByReplacingOccurrencesOfString:@"<ts>" withString:@""]; if (isPrivacyPolicyLink) label.text = [label.text stringByReplacingOccurrencesOfString:@"<pp>" withString:@""]; } else { label.textColor = [UIColor whiteColor]; } // 6. Lay out the labels so it forms a complete sentence again: // If this word doesn't fit at end of this line, then move it to the next // line and make sure any leading spaces are stripped off so it aligns nicely: [label sizeToFit]; if (self.agreeTextContainerView.frame.size.width < wordLocation.x + label.bounds.size.width) { wordLocation.x = 0.0; // move this word all the way to the left... wordLocation.y += label.frame.size.height; // ...on the next line // And trim of any leading white space: NSRange startingWhiteSpaceRange = [label.text rangeOfString:@"^\\s*" options:NSRegularExpressionSearch]; if (startingWhiteSpaceRange.location == 0) { label.text = [label.text stringByReplacingCharactersInRange:startingWhiteSpaceRange withString:@""]; [label sizeToFit]; } } // Set the location for this label: label.frame = CGRectMake(wordLocation.x, wordLocation.y, label.frame.size.width, label.frame.size.height); // Show this label: [self.agreeTextContainerView addSubview:label]; // Update the horizontal position for the next word: wordLocation.x += label.frame.size.width; } } 

这里是我的方法来处理这些链接上检测到的水龙头。

 - (void)tapOnTermsOfServiceLink:(UITapGestureRecognizer *)tapGesture { if (tapGesture.state == UIGestureRecognizerStateEnded) { NSLog(@"User tapped on the Terms of Service link"); } } - (void)tapOnPrivacyPolicyLink:(UITapGestureRecognizer *)tapGesture { if (tapGesture.state == UIGestureRecognizerStateEnded) { NSLog(@"User tapped on the Privacy Policy link"); } } 

希望这可以帮助。 我相信有更多更聪明,更优雅的方式来做到这一点,但这是我能够想出来的,而且很好地工作。

以下是它在应用中的外观:

模拟器截图的最终结果

祝你好运! 🙂

埃里克

如何为UITextView实现自定义文本操作(如button):

关键原则:

  1. 使用NSAttributedString作为定义点击链接的一种方式。
  2. 使用UITextViewDelegate来捕捉链接的按下。

定义一个URLstring:

 private let kURLString = "myapp://fakeActionUrl" 

添加一个链接到你的属性string:

 let range = NSMakeRange(0, actionString.characters.count) mutableAttributedString.addAttribute(NSLinkAttributeName, value: kURLString, range: range) 

将属性string分配给文本视图:

 textView.attributedText = attributedString 

实现UITextViewDelegate (这实际上是关键部分,防止URL打开某个网站,而您可以在其中定义自定义操作):

 func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool { if (URL.absoluteString == kURLString) { // Do whatever you want here as the action to the user pressing your 'actionString' } return false } 

您还可以自定义您的链接属性:

 textView.linkTextAttributes = [NSForegroundColorAttributeName : UIColor.red, NSUnderlineStyleAttributeName : NSUnderlineStyle.styleSingle] 

我如何实现UILabel自定义操作:

我通常最终使用TTTAttributedLabel 。

这里是一个完整的例子在Swift 2没有豆荚。

 import UIKit class SomeViewController: UIViewController, UITextViewDelegate { @IBOutlet weak var terms: UITextView! let termsAndConditionsURL = "http://www.example.com/terms"; let privacyURL = "http://www.example.com/privacy"; override func viewDidLoad() { super.viewDidLoad() self.terms.delegate = self let str = "By using this app you agree to our Terms and Conditions and Privacy Policy" let attributedString = NSMutableAttributedString(string: str) var foundRange = attributedString.mutableString.rangeOfString("Terms and Conditions") attributedString.addAttribute(NSLinkAttributeName, value: termsAndConditionsURL, range: foundRange) foundRange = attributedString.mutableString.rangeOfString("Privacy Policy") attributedString.addAttribute(NSLinkAttributeName, value: privacyURL, range: foundRange) terms.attributedText = attributedString } func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool { if (URL.absoluteString == termsAndConditionsURL) { let myAlert = UIAlertController(title: "Terms", message: nil, preferredStyle: UIAlertControllerStyle.Alert) myAlert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)) self.presentViewController(myAlert, animated: true, completion: nil) } else if (URL.absoluteString == privacyURL) { let myAlert = UIAlertController(title: "Conditions", message: nil, preferredStyle: UIAlertControllerStyle.Alert) myAlert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)) self.presentViewController(myAlert, animated: true, completion: nil) } return false } } 

检查这个UILabel类,这一定会帮助你。 我用这个做了同样的事情。

TTTAttributedLabel

下面是Xamarin C#接受的答案的翻译版本,供任何人使用:

  var str = "Or, #<li>log in# to see your orders."; var strParts = str.Split('#'); var ptWordLocation = new PointF (0, 0); if (strParts.Length > 1) { //Loop the parts of the string foreach (var s in strParts) { //Check for empty string if (!String.IsNullOrEmpty (s)) { var lbl = new UILabel (); lbl.Font = lbl.Font.WithSize (15); lbl.TextColor = cpAppConstants.TextColorMessage; lbl.UserInteractionEnabled = s.Contains ("<li>"); lbl.Text = s.Replace ("<li>", ""); if (s.Contains ("<li>")) { lbl.TextColor = UIColor.FromRGB (200, 95, 40); //Set tap gesture for this clickable text: var gesture = new UITapGestureRecognizer (); gesture.AddTarget(() => buildLoginLabel_onTap(gesture)); lbl.AddGestureRecognizer (gesture); } lbl.SizeToFit (); //Lay out the labels so it forms a complete sentence again if (vw.Frame.Width < ptWordLocation.X + lbl.Bounds.Size.Width) { ptWordLocation.X = 0f; ptWordLocation.Y += lbl.Frame.Size.Height; lbl.Text.Trim (); } lbl.Frame = new RectangleF (ptWordLocation.X, ptWordLocation.Y, lbl.Frame.Size.Width, lbl.Frame.Size.Height); vw.AddSubview (lbl); //Update the horizontal width ptWordLocation.X += lbl.Frame.Size.Width; } } } 

点击这里了解如何为textView设置Listener

和Add

  UITapGestureRecognizer *listener = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)]; 

写下你想要做的动作

  - (void)tapAction:(UITapGestureRecognizer *)sender { } 

将侦听器添加到视图

  [self.view addGestureRecognizer:listener]; 

我使用Erik的解决scheme,但需要用Swift来完成。 转换后,我发现一个小问题,如果你有一个很多的文本(多于一个单一的行)之前,你有一个链接,那么它没有得到正确包装,所以我添加了一个函数,以适应文本。

 func setText(newText:String){ // 1. Split the localized string on the # sign: let localizedStringPieces:NSArray = newText.componentsSeparatedByString("#") // 2. Loop through all the pieces: var msgChunkCount:Int = localizedStringPieces.count var wordLocation:CGPoint = CGPointMake(0.0, 0.0) for (var i:Int = 0; i < msgChunkCount; i++){ let chunk:String = localizedStringPieces[i] as! String if chunk == ""{ continue; // skip this loop if the chunk is empty } // 3. Determine what type of word this is: let isTermsOfServiceLink:Bool = chunk.hasPrefix("<ts>") let isPrivacyPolicyLink:Bool = chunk.hasPrefix("<pp>") let isLink:Bool = (Bool)(isTermsOfServiceLink || isPrivacyPolicyLink) var remainingText:String = chunk while count(remainingText)>0{ // 4. Create label, styling dependent on whether it's a link: let label:UILabel = UILabel() label.font = UIFont.systemFontOfSize(methodFontSize) label.text = remainingText label.userInteractionEnabled = isLink if (isLink){ label.textColor = UIColor(red: 110/255, green: 181/255, blue: 229/255, alpha: 1.0) label.highlightedTextColor = UIColor.yellowColor() // 5. Set tap gesture for this clickable text: var selectorAction:Selector = isTermsOfServiceLink ? "tapOnTermsOfServiceLink" : "tapOnPrivacyPolicyLink" let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: selectorAction) label.addGestureRecognizer(tapGesture) // Trim the markup characters from the label: if (isTermsOfServiceLink){ label.text = label.text?.stringByReplacingOccurrencesOfString("<ts>", withString: "", options: nil, range: nil) } if (isPrivacyPolicyLink){ label.text = label.text?.stringByReplacingOccurrencesOfString("<pp>", withString: "", options: nil, range: nil) } }else{ label.textColor = UIColor.whiteColor() } // If this chunk of text doesn't fit at end of this line, then move it to the next // line and make sure any leading spaces are stripped off so it aligns nicely: label.sizeToFit() let labelHeight = label.frame.size.height var leftOverText:String = fitLabelToWidth(label, width: self.textContainer.frame.size.width - wordLocation.x) // if we can't fit anything onto this line then drop down if label.text == "" { //drop to a new line wordLocation.x = 0.0 // move this word all the way to the left... wordLocation.y += labelHeight; // ...on the next line. (Have to use a constant here because if label has no text it also has no height) // refit the text label.text = remainingText leftOverText = fitLabelToWidth(label, width: self.textContainer.frame.size.width - wordLocation.x) //NB WE ARE ASSUMING HERE THAT AFTER DROPPING DOWN AT LEAST SOME OF THIS TEXT WILL FIT // IF THIS ISN'T THE CASE THEN THE LINE WOULD ALWAYS BE TOO BIG AND WE WOULD NEVER BE ABLE TO FIT IT ON ANYWAY! } // Set the location for this label: label.frame = CGRectMake(wordLocation.x, wordLocation.y, label.frame.size.width, label.frame.size.height) // Show this label: self.textContainer.addSubview(label) // Update the horizontal position for the next word: wordLocation.x += label.frame.size.width; // update our remaining text and get ready to go again remainingText = leftOverText } } } // fit the text label (formatted externally) to the desired with, chopping off text to make it so // return the remaining text that didn't make the cut as a string func fitLabelToWidth(label:UILabel, width:CGFloat)->String{ let startingText:String = label.text! println("Trying to fit ::\(startingText)::") // if the string is null then we are done if startingText == ""{ return "" } // if this fits already then we are done label.sizeToFit() if label.frame.size.width <= width{ return "" } // so now we have to loop round trying to get this to fit var cutRange:Range<String.Index> = Range<String.Index>(start: startingText.startIndex, end: startingText.startIndex) var searchRange:Range<String.Index> var startSearchIndex:String.Index = startingText.startIndex var lastSearchIndex:String.Index = startSearchIndex var testText:String = "" var lastText:String = "" label.text = testText label.sizeToFit() while label.frame.size.width <= width{ // store off the last used text as this might be as far as we go lastText = testText lastSearchIndex = startSearchIndex // set up the search range so we look for spaces missing out any previous ones searchRange = Range<String.Index>(start: startSearchIndex, end: startingText.endIndex) // cut out a range with the next occurrence of spaces cutRange = startingText.rangeOfString(" ", options: NSStringCompareOptions.CaseInsensitiveSearch, range: searchRange, locale: nil)! // get some text from the start of the string to our cut point (start) testText = startingText.substringToIndex(cutRange.startIndex) // move the search start to the point after the end of the spaces we just found startSearchIndex = cutRange.endIndex // try this in our label to see if it sizes ok label.text = testText label.sizeToFit() } // we leave the while when the string gets too big label.text = lastText label.sizeToFit() return startingText.substringFromIndex(lastSearchIndex) } 

您可以使用以下代码在UILable上添加点按手势: –

步骤1:

 Delegate "UIGestureRecognizerDelegate" to your viewcontroller.h for example: @interface User_mail_List : UIViewController<UIGestureRecognizerDelegate> 

第2步:

 //create you UILable UILabel *title_lbl= [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)]; [title_lbl setText:@"u&me"]; [title_lbl setUserInteractionEnabled:YES]; [yourView addSubview:title_lbl]; 

第3步:

 UITapGestureRecognizer *tap= [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(Prof_lbl_Pressed:)];//your action selector [tap setNumberOfTapsRequired:1]; title_lbl.userInteractionEnabled= YES; [title_lbl addGestureRecognizer:tap]; 

步骤4:

 -(void)Prof_lbl_Pressed:(id)sender{ //write your code action } 

谢谢,

我的点击链接的行动的解决scheme是这样的,

 myLabel.automaticLinkDetectionEnabled = YES; myLabel.urlLinkTapHandler = ^(KILabel *myLabel, NSString *string, NSRange range) { [self attemptOpenURL:[NSURL URLWithString:string]]; NSLog(@"URL tapped %@", string); }; 

检查这个UILabel类,这将帮助你。

https://github.com/Krelborn/KILabel

你可以使用多重UILabeluserInteractionEnabled = YES ,并用不同的粗体字在该标签上添加一个UITapGestureRecognizer

这是一个这样做的例子。

像这样的东西也可以尝试。

如果你想要一个工作的解决scheme,那么你可以尝试“花式标签” 。 在该链接中search文本“这是我的实施”并点击它。 你会准备好使用产品。 不要忘了点击使用上面的示例运行的应用程序上的“开关”button。

我希望能帮到你很多。