Add click event for some text in ios NSString

I have the following code and you want parts of my text to be interactive and invoke another UIViewController (and not a website).

NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:@"testing it out @clickhere"]; NSInteger length = str.length; [str addAttribute:NSForegroundColorAttributeName value:[UIColor bestTextColor] range:NSMakeRange(0,length)]; 

NSMutableAttributedString gets a UILabel value as follows:

 label.attributedText = str; 

What is the best way to do this? I can't seem to find a great answer.

An example of what I want is to assume that I have a UILabel, for example, with the following text:

 This is my label. Click here to go to UIViewController1 and then go to UIViewController1 by this #tag. 

I want the text "here" to be passed for the first click event, and the word "#tag" to be passed to the same click event.

+5
source share
4 answers

What if you used the value field for transmission at the destination?

 [attributedString addAttribute:NSLinkAttributeName value:[@"destinationController1" stringByAppendingString:username] range:range]; 

Then override the delegate method:

 - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange { if ([URL.scheme isEqualToString:@"destinationController1"]) { // Launch View controller return NO; } return YES; } 
+7
source

My solution requires using a UITextView (which is much simpler, and I highly recommend you use it).

Swift

 class ViewController: UIViewController { @IBOutlet weak var textView:UITextView!; override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let gestureRecognizer = UITapGestureRecognizer(target: self, action: "textViewTapped:"); gestureRecognizer.numberOfTapsRequired = 1; gestureRecognizer.numberOfTouchesRequired = 1; self.textView.addGestureRecognizer(gestureRecognizer); } func textViewTapped(sender: UITapGestureRecognizer) { let wordTarget = "here"; let word = UITextView.getWordAtPosition(sender.locationInView(self.textView), textView: self.textView); if word == wordTarget { let plainString = self.textView.attributedText.string; let substrings = NSMutableArray(); let scanner = NSScanner(string: plainString); scanner.scanUpToString("#", intoString: nil); while !scanner.atEnd { var substring:NSString? = nil; scanner.scanString("#", intoString: nil); let space = " "; if scanner.scanUpToString(space, intoString: &substring) { // If the space immediately followed the #, this will be skipped substrings.addObject(substring!); } scanner.scanUpToString("#", intoString: nil); //Scan all characters before next # } println(substrings.description); //Now you got your substrings in an array, so use those for your data passing (in a segue maybe?) ... } } } extension UITextView { class func getWordAtPosition(position: CGPoint!, textView: UITextView!) -> String? { //Remove scrolloffset let correctedPoint = CGPointMake(position.x, textView.contentOffset.y + position.y); //Get location in text from uitextposition at a certian point let tapPosition = textView.closestPositionToPoint(correctedPoint); //Get word at the position, will return nil if its empty. let wordRange = textView.tokenizer.rangeEnclosingPosition(tapPosition, withGranularity: UITextGranularity.Word, inDirection: UITextLayoutDirection.Right.rawValue); return textView.textInRange(wordRange!); } } 

Objective-c

 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(textViewTapped:)]; gestureRecognizer.numberOfTouchesRequired = 1; gestureRecognizer.numberOfTapsRequired = 1; [self.textView addGestureRecognizer:gestureRecognizer]; } - (void)textViewTapped:(UITapGestureRecognizer *)sender { NSString *wordTarget = @"here"; NSString* word = [self getWordAtPosition:[sender locationInView:self.textView] textView:self.textView]; if ([word isEqualToString:wordTarget]) { NSString *plainString = self.textView.attributedText.string; NSMutableArray* substrings = [[NSMutableArray alloc]init]; NSScanner *scanner = [[NSScanner alloc]initWithString:plainString]; [scanner scanUpToString:@"#" intoString:nil]; while (![scanner isAtEnd]) { NSString* substring = nil; [scanner scanString:@"#" intoString:nil]; NSString* space = @" "; if ([scanner scanUpToString:space intoString:&substring]) { [substrings addObject:substring]; } [scanner scanUpToString:@"#" intoString:nil]; } //Now you got your substrings in an array, so use those for your data passing (in a segue maybe?) ... } } - (NSString*)getWordAtPosition:(CGPoint)position textView:(UITextView *)textView { //remove scrollOffset CGPoint correctedPoint = CGPointMake(position.x, textView.contentOffset.y + position.y); UITextPosition *tapPosition = [textView closestPositionToPoint:correctedPoint]; UITextRange *wordRange = [textView.tokenizer rangeEnclosingPosition:tapPosition withGranularity:UITextGranularityWord inDirection:UITextLayoutDirectionRight]; return [textView textInRange:wordRange]; } 

Basically, you need to add a gesture recognizer to get the transition point in your text view. Then you get the word using the category method specified in the extension area. After that, you check what the word is (where we need the word "here"). Then we collect the hashtags that you provided.

All you have to do is add the performSegueWithIdentifier method and pass it accordingly.

+3
source

In addition to @Nate Lee, answer, update the extension for Swift 4.0:

 extension UITextView { class func getWordAtPosition(position: CGPoint!, textView: UITextView!) -> String? { //Remove scrolloffset let correctedPoint = CGPoint(x: position.x, y: (textView.contentOffset.y + position.y)) //Get location in text from uitextposition at a certian point let tapPosition = textView.closestPosition(to: correctedPoint) //Get word at the position, will return nil if its empty. let wordRange = textView.tokenizer.rangeEnclosingPosition(tapPosition!, with: .word, inDirection: UITextLayoutDirection.right.rawValue) return textView.text(in: wordRange!) } } 
+1
source

Swift 3:

Do not check the URL.scheme attribute. Returned zero for me.

Do it:

attributedString.addAttribute(NSLinkAttributeName, value: "openToViewController", range: range)

Then use the absoluteString attribute in the URL to check this value of your choice:

  func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool{ if (URL.absoluteString == "openToViewController") { let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as! UIViewController self.present(viewController, animated: true, completion: nil) return false } return true } 
0
source

Source: https://habr.com/ru/post/1211451/


All Articles