Добавить событие щелчка к некоторому тексту в 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.

Я хочу, чтобы текст «здесь» передавался для первого события клика, а слово «#tag» — для того же события клика.


person cdub    schedule 19.01.2015    source источник
comment
Посмотрите, поможет ли это: a uilabel uitextview"> stackoverflow.com/questions/8811909/ Также попробуйте следующее: stackoverflow.com/questions/15293426/   -  person iOSAaronDavid    schedule 20.01.2015
comment
Зачем дублировать: stackoverflow.com/questions/28018707/?   -  person Larme    schedule 20.01.2015


Ответы (4)


Что, если вы использовали поле значения для передачи адресата?

[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;
}
person scott    schedule 19.01.2015
comment
Важной частью здесь является использование UITextView, а не UILabel. И вы можете настроить некоторые свойства textview, чтобы сделать его более похожим на простую метку, например, отключить редактирование, выбор и прокрутку. - person Daniel Rinser; 20.01.2015
comment
Да, оба в файле .m, но убедитесь, что вы подписаны на UITextViewDelegate в вашем файле .h, чтобы метод вызывался в первую очередь. - person scott; 20.01.2015
comment
Если в моем контроллере просмотра есть жест, то это будет работать или нет? - person Dimple Shah; 18.01.2017
comment
не добавляйте пробел при добавлении значения в атрибутированный текст, иначе shouldInteractWithURL не обнаружит пробел. И если вы передаете строковое значение ant, которое не содержит никакого URL-адреса, тогда сравните URL.absoluteString, а не URL.scheme - person Parthpatel1105; 14.04.2018

Мое решение требует использования UITextView (что значительно проще, и я призываю вас использовать его вместо этого).

Свифт

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!);
    }
}

Цель-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];
}

В основном вам нужно добавить распознаватель жестов, чтобы получить точку касания в вашем текстовом представлении. Затем вы получаете слово, используя метод категории, представленный в области расширения. После этого вы проверяете, что это за слово (там, где нам нужно слово «здесь»). Затем мы собираем предоставленные вами хэштеги.

Все, что вам нужно сделать, это добавить метод PerformSegueWithIdentifier и передать его соответствующим образом.

person Nate Lee    schedule 20.01.2015
comment
есть ли способ сделать это, но получить всю строку, а не только одно слово, которое вы щелкаете? - person sabo; 22.04.2015
comment
Я считаю, что вместо этого замените here в let wordTarget = "here"; предложением. - person Nate Lee; 22.04.2015
comment
Я сделал это, и это не сработало. Он подбирает только одно слово, поэтому, если мне нужно одно и то же слово в тексте, он не сможет определить разницу. - person sabo; 22.04.2015
comment
textView.closestPositionToPoint(correctedPoint) всегда возвращает ноль - person Gaurav Sharma; 28.01.2016

В дополнение к ответу @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!)
    }
}
person Soumen    schedule 21.12.2017

Свифт 3:

Не проверяйте атрибут URL.scheme. Вернул ноль для меня.

Сделай это:

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

Затем используйте атрибут absoluteString в URL-адресе, чтобы проверить это значение на ваш выбор:

  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
}
person kneuben_studdard    schedule 28.06.2017