向 ios NSString 中的某些文本添加点击事件
Add a click event to some text in ios NSString
我有以下代码,想让我的部分文本可以点击并调用另一个 UIViewController(不是网站)。
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:@"testing it out @clickhere"];
NSInteger length = str.length;
[str addAttribute:NSForegroundColorAttributeName value:[UIColor bestTextColor] range:NSMakeRange(0,length)];
NSMutableAttributedString 被设置为 UILabel,如下所示:
label.attributedText = str;
最好的方法是什么?我似乎找不到很好的答案。
我想要的一个示例是假设我有一个 UILabel,其中包含以下文本:
This is my label. Click here to go to UIViewController1 and then go to UIViewController1 by this #tag.
我希望为第一个点击事件传递文本 "here",并将单词“#tag”传递给同一个点击事件。
如果使用值字段传入目的地会怎么样?
[attributedString addAttribute:NSLinkAttributeName
value:[@"destinationController1" stringByAppendingString:username]
range:range];
然后覆盖委托方法:
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
if ([URL.scheme isEqualToString:@"destinationController1"]) {
// Launch View controller
return NO;
}
return YES;
}
我的解决方案需要使用 UITextView
(这要容易得多,我强烈建议您改用它)。
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];
}
基本上你需要添加一个手势识别器来获取文本视图中的点击点。然后,您使用扩展区域中提供的类别方法获取单词。之后,您检查单词是什么(我们想要单词 "here" 的地方)。然后,我们收集您提供的主题标签。
您所要做的就是添加一个 performSegueWithIdentifier 方法,并相应地传递它。
Swift 3:
不要检查 URL.scheme
属性。为我返回零。
这样做:
attributedString.addAttribute(NSLinkAttributeName, value: "openToViewController", range: range)
然后使用 URL 上的 absoluteString
属性根据您选择的视图检查该值:
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
}
除了@Nate Lee 的回答,更新了 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!)
}
}
我有以下代码,想让我的部分文本可以点击并调用另一个 UIViewController(不是网站)。
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:@"testing it out @clickhere"];
NSInteger length = str.length;
[str addAttribute:NSForegroundColorAttributeName value:[UIColor bestTextColor] range:NSMakeRange(0,length)];
NSMutableAttributedString 被设置为 UILabel,如下所示:
label.attributedText = str;
最好的方法是什么?我似乎找不到很好的答案。
我想要的一个示例是假设我有一个 UILabel,其中包含以下文本:
This is my label. Click here to go to UIViewController1 and then go to UIViewController1 by this #tag.
我希望为第一个点击事件传递文本 "here",并将单词“#tag”传递给同一个点击事件。
如果使用值字段传入目的地会怎么样?
[attributedString addAttribute:NSLinkAttributeName
value:[@"destinationController1" stringByAppendingString:username]
range:range];
然后覆盖委托方法:
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
if ([URL.scheme isEqualToString:@"destinationController1"]) {
// Launch View controller
return NO;
}
return YES;
}
我的解决方案需要使用 UITextView
(这要容易得多,我强烈建议您改用它)。
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];
}
基本上你需要添加一个手势识别器来获取文本视图中的点击点。然后,您使用扩展区域中提供的类别方法获取单词。之后,您检查单词是什么(我们想要单词 "here" 的地方)。然后,我们收集您提供的主题标签。
您所要做的就是添加一个 performSegueWithIdentifier 方法,并相应地传递它。
Swift 3:
不要检查 URL.scheme
属性。为我返回零。
这样做:
attributedString.addAttribute(NSLinkAttributeName, value: "openToViewController", range: range)
然后使用 URL 上的 absoluteString
属性根据您选择的视图检查该值:
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
}
除了@Nate Lee 的回答,更新了 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!)
}
}