问题
I have the following code and want to make parts of my text be clickable and call another UIViewController (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)];
The NSMutableAttributedString gets set to a UILabel like so:
label.attributedText = str;
Whats the best way to do this? I can't seem to find a great answer.
An example of what I want is suppose I have a UILabel like so 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.
回答1:
What if you used the value field to pass in 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;
}
回答2:
My solution requires the use of a UITextView
(which is significantly easier, and I urge that you use it instead).
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 tap point in your textview. Then, you get the word using the category method provided in the extension area. After, you check what the word is (where we want the word "here"). Then, we collect the hashtags you have provided.
All you have to do is add a performSegueWithIdentifier method, and pass it accordingly.
回答3:
In addition to @Nate Lee answer, updated the extension for Swift 4.0 version:
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!)
}
}
回答4:
Swift 3:
Don't check on the URL.scheme
attribute. Returned nil for me.
Do this:
attributedString.addAttribute(NSLinkAttributeName, value: "openToViewController", range: range)
Then use the absoluteString
attribute on the URL to check on that value to your view of 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
}
来源:https://stackoverflow.com/questions/28034592/add-a-click-event-to-some-text-in-ios-nsstring