iOS UITextView или UILabel с кликабельными ссылками на действия [дубликат]
этот вопрос уже есть ответ здесь:
- Создать tap-способные "ссылки" в NSAttributedString UILabel? 26 ответов
Я хочу сделать UILabel
или UITextView
с некоторым текстом с 2 кликабельными ссылками в нем. Не ссылки на веб-страницы, но я хочу связать эти 2 ссылки с действиями, как я бы сделала с UIButton
. Все примеры я видел ссылки на этого, но я не хочу этого. Также этот текст будет переведен на другие языки, поэтому позиции должны быть динамичными.
хотят сделать так:
10 ответов:
мне нужно было решить эту точно такую же проблему: очень похожий текст с этими двумя ссылками в нем, по нескольким строкам, и он должен быть переведен на любой язык (включая разные порядки слов и т. д.). Я просто решил это, так что позвольте мне поделиться, как я это сделал.
Первоначально я думал, что я должен создать приписанный текст, а затем сопоставить местоположение касания крана с регионами в этом тексте. Хотя я думаю, что это выполнимо, я также думаю, что это слишком сложно подход.
это то, что я в конечном итоге сделал вместо этого:
резюме:
- есть очень простой пользовательской разметки в вашем английском сообщении, так что вы можете разобрать различные части
- попросите ваших переводчиков оставить разметку и перевести остальные
- есть UIView, который может служить контейнером этого сообщения
- разбейте ваше английское сообщение на части, чтобы отделить обычный текст от кликабельный текст
- для каждой части создайте UILabel на контейнере UIView
- для кликабельных частей установите свой стиль, разрешите взаимодействие с пользователем и создайте распознаватель жестов крана
- сделайте некоторые очень простой бухгалтерии, чтобы разместить слова идеально через линии
деталь:
в контроллере вида
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>
to определите, какие части являются ссылками на то, что. Если кусок текста, с которым я работаю, является ссылкой, то я немного стилизую и настраиваю распознаватель жестов для него. Я также вычеркиваю символы разметки, конечно. Я думаю, что это очень простой способ сделать это.обратите внимание на некоторые тонкости, такие как то, как я обрабатываю пробелы: я просто беру пробелы из (локализованной) строки. Если нет пробелов (китайский, японский), то между кусками также не будет пробелов. Если есть пробелы, то те автоматически пробел из кусков по мере необходимости (например, для английского языка). Когда мне нужно поместить слово в начале следующей строки, Мне нужно убедиться, что я удаляю любой префикс пробела из этого текста, потому что в противном случае он не выравнивается должным образом.
- (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
:основные принципы:
- использовать
NSAttributedString
как способ определения ссылки на кран.- использовать
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)
присвоить строку атрибутов a просмотр текста:
textView.attributedText = attributedOriginalText
реализовать
UITextViewDelegate
(это действительно ключевая часть a предотвращает открытие 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
:обычно я использую 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 } }
вот переведенная версия принятого ответа на C# для Xamarin для всех, кто найдет его полезным:
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
и добавить
UITapGestureRecognizer *listener = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)];
напишите действие, которое вы хотите сделать в пределах
- (void)tapAction:(UITapGestureRecognizer *)sender { }
добавить слушателя в представление с помощью
[self.view addGestureRecognizer:listener];
я использовал решение Эрика, но нужно было сделать это с помощью 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 }
спасибо,
мое решение для кликабельных ссылок на действие это,
myLabel.automaticLinkDetectionEnabled = YES; myLabel.urlLinkTapHandler = ^(KILabel *myLabel, NSString *string, NSRange range) { [self attemptOpenURL:[NSURL URLWithString:string]]; NSLog(@"URL tapped %@", string); };
проверьте этот класс UILabel тоже, это поможет вам.
вы можете использовать несколько перекрывающихся
UILabel
сuserInteractionEnabled = YES
на нем и добавитьUITapGestureRecognizer
на этой этикетке с разными жирными шрифтами.здесь является одним из таких примеров этого.
что-то такой также можно попробовать.
Если вы хотите рабочее решение этого, то вы можете попробовать "Fancy-Label". Найдите в этой ссылке текст "вот моя реализация" и нажмите на него. Вы получите готовый к использованию продукт. Не забудьте нажмите кнопку "переключатель" на приложение, которое вы запускаете с помощью приведенного выше примера.
Я надеюсь, что это поможет вам много.