IOSUITextView 或 UILabel,带有指向操作的可点击链接

我想使一个 UILabelUITextView与一些文字与2点击链接在它。不链接到网页,但我想链接这2个链接的行动喜欢我会做一个 UIButton。所有的例子我已经看到的是链接到网络视图,但我不希望这一点。同时,文本将被翻译成其他语言,所以位置必须是动态的。

想做的是:

enter image description here

74524 次浏览

您可以使用多个重叠的 UILabeluserInteractionEnabled = YES上,并添加一个 UITapGestureRecognizer上的标签与不同的加粗字体。

这里 就是这样做的一个例子。

某些 像这样也可以尝试。

如果你想要一个工作的解决方案,然后你可以尝试 “华丽标签”。在该链接中搜索文本“这是我的实现”并单击它。你会准备好使用产品。不要忘记点击“开关”按钮上的应用程序,你运行使用上述示例。

我希望这对你有很大帮助。

检查这个 UILabel 类,这肯定会对你有帮助。

属性标签

单击 Here 了解如何为 textView 设置监听器

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

控件中编写要执行的操作

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

将侦听器添加到视图

      [self.view addGestureRecognizer:listener];

我需要解决这个完全相同的问题: 非常相似的文本,其中的两个链接,在多行,并需要它能够被翻译成任何语言(包括不同的语序,等)。我刚刚解决了,所以让我分享一下我是怎么做到的。

最初,我认为我应该创建属性文本,然后映射点击的触摸位置到该文本内的区域。虽然我认为这是可行的,但我也认为这是一个过于复杂的方法。

这就是我最后所做的:

总结:

  • 在您的英文信息中有非常基本的自定义标记,这样您就可以分析出不同的部分
  • 指示您的翻译人员保留标记并翻译其余部分
  • 有一个可以用作此消息容器的 UIView
  • 将您的英文信息分成几部分,以便将常规文本与可点击文本分开
  • 对于每个部件,在容器 UIView 上创建一个 UILabel
  • 对于可点击的部分,设置您的样式,允许用户交互,并创建您的点击手势识别器
  • 做一些非常基本的记账工作,把单词完美地放在横线上

详情:

在视图控制器的 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.")];

我正在调用一个方法来构建消息。注意我提出的标记。你当然可以发明你自己的,但关键是我还标记了每个可点击区域的末端,因为它们跨越了多个单词。

下面是将消息放在一起的方法——见下文。首先,我将英文消息分解为 #字符(或者更确切地说是 @"#"字符串)。这样我就可以得到我需要单独创建一个标签的每件作品。我循环遍历它们,寻找 <ts><pp>的基本标记,以检测哪些片段是链接到哪些片段的链接。如果我正在处理的文本块是一个链接,那么我设置一个点击手势识别器。当然,我还去掉了标记字符。我觉得这个方法很简单。

注意一些微妙的地方,比如我如何处理空格: 我只是从(本地化的)字符串中获取空格。如果没有空格(中文,日文) ,那么块之间也不会有空格。如果有空格,那么这些空格会根据需要自动将块隔开(例如,对于英语)。当我必须在下一行的开头放置一个单词时,我确实需要确保从该文本中去除任何空白前缀,因为否则它不能正确对齐。

- (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");
}
}

希望这个能帮上忙。我相信有更聪明和更优雅的方法来做到这一点,但这是我能够想出来的,它工作得很好。

下面是它在应用程序中的外观:

Simulator screenshot of the end result

祝你好运! : -)

艾瑞克

下面是针对 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;
}
}
}

我用了艾瑞克的方法,但需要和斯威夫特一起。转换后,我发现一个小问题,如果你有很多文本(多于一行)之前,你得到了一个链接,然后它没有得到正确的包装,所以我添加了一个函数,以适应文本。

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)


}

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

主要原则:

  1. 使用 NSAttributedString作为定义要点击的链接的方法。
  2. 使用 UITextViewDelegate来捕捉链接的压力。

定义一个 URL 字符串:

private let kURLString = "https://www.mywebsite.com"

添加属性化字符串的链接:

let originalText = "Please visit the website for more information."
let attributedOriginalText = NSMutableAttributedString(string: originalText)


let linkRange = attributedOriginalText.mutableString.range(of: "website")
attributedOriginalText.addAttribute(.link, value: kURLString, range: linkRange)

将属性化字符串分配给文本视图:

textView.attributedText = attributedOriginalText

实现 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 = [
NSAttributedStringKey.foregroundColor.rawValue : UIColor.red,
NSAttributedStringKey.underlineStyle.rawValue : NSUnderlineStyle.styleSingle]

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

我通常最终使用 属性标签

您可以使用下面的代码在 UILable 上添加点击手势:-

第一步:

Delegate "UIGestureRecognizerDelegate" to your viewcontroller.h


for example:
@interface User_mail_List : UIViewController<UIGestureRecognizerDelegate>

第二步:

//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];

第三步:

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];

第四步:

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

谢谢,

这里是一个完整的例子,在 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
}
}

我对于可点击链接的解决方案是这样的,

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