129

UITextView表示する がありますNSAttributedString。この文字列には、タップ可能にしたい単語が含まれています。タップすると、アクションを実行できるようにコールバックされます。URL のタップを検出してデリゲートをコールバックできることはわかっUITextViewていますが、これらは URL ではありません。

iOS 7 と TextKit の力により、これが可能になったように思えますが、例が見つからず、どこから始めればよいかわかりません。

文字列にカスタム属性を作成できるようになったことは理解しています (まだ行っていませんが)。これは、魔法の言葉の 1 つがタップされたかどうかを検出するのに役立つでしょうか? いずれにせよ、そのタップを傍受し、タップが発生した単語を検出する方法はまだわかりません。

iOS 6 の互換性は必須ではないことに注意してください。

4

12 に答える 12

120

もう少し人の役に立ちたいと思っただけです。シュミットの回答に続いて、元の質問で尋ねたとおりに行うことができます。

1) クリック可能な単語にカスタム属性が適用された属性付き文字列を作成します。例えば。

NSAttributedString* attributedString = [[NSAttributedString alloc] initWithString:@"a clickable word" attributes:@{ @"myCustomTag" : @(YES) }];
[paragraph appendAttributedString:attributedString];

2) その文字列を表示する UITextView を作成し、それに UITapGestureRecognizer を追加します。次に、タップを処理します。

- (void)textTapped:(UITapGestureRecognizer *)recognizer
{
    UITextView *textView = (UITextView *)recognizer.view;

    // Location of the tap in text-container coordinates

    NSLayoutManager *layoutManager = textView.layoutManager;
    CGPoint location = [recognizer locationInView:textView];
    location.x -= textView.textContainerInset.left;
    location.y -= textView.textContainerInset.top;

    // Find the character that's been tapped on

    NSUInteger characterIndex;
    characterIndex = [layoutManager characterIndexForPoint:location
                                           inTextContainer:textView.textContainer
                  fractionOfDistanceBetweenInsertionPoints:NULL];

    if (characterIndex < textView.textStorage.length) {

        NSRange range;
        id value = [textView.attributedText attribute:@"myCustomTag" atIndex:characterIndex effectiveRange:&range];

        // Handle as required...

        NSLog(@"%@, %d, %d", value, range.location, range.length);

    }
}

やり方さえわかれば簡単!

于 2013-10-12T12:47:46.947 に答える
13

WWDC 2013 の例:

NSLayoutManager *layoutManager = textView.layoutManager;
 CGPoint location = [touch locationInView:textView];
 NSUInteger characterIndex;
 characterIndex = [layoutManager characterIndexForPoint:location
inTextContainer:textView.textContainer
fractionOfDistanceBetweenInsertionPoints:NULL];
if (characterIndex < textView.textStorage.length) { 
// valid index
// Find the word range here
// using -enumerateSubstringsInRange:options:usingBlock:
}
于 2013-10-12T09:38:00.903 に答える
11

NSLinkAttributeName でこれをかなり簡単に解決できました

スイフト2

class MyClass: UIViewController, UITextViewDelegate {

  @IBOutlet weak var tvBottom: UITextView!

  override func viewDidLoad() {
      super.viewDidLoad()

     let attributedString = NSMutableAttributedString(string: "click me ok?")
     attributedString.addAttribute(NSLinkAttributeName, value: "cs://moreinfo", range: NSMakeRange(0, 5))
     tvBottom.attributedText = attributedString
     tvBottom.delegate = self

  }

  func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool {
      UtilityFunctions.alert("clicked", message: "clicked")
      return false
  }

}
于 2016-01-07T01:40:17.843 に答える
4

でそれが可能ですcharacterIndexForPoint:inTextContainer:fractionOfDistanceBetweenInsertionPoints:。それはあなたが望んでいたものとは多少異なる動作をします - タップされた文字が魔法の言葉に属しているかどうかをテストする必要があります。しかし、それは複雑であってはなりません。

ところで、WWDC 2013 のテキスト キットの紹介を見ることを強くお勧めします。

于 2013-10-12T09:22:24.123 に答える
3

この拡張機能を Swift に使用します。

import UIKit

extension UITapGestureRecognizer {

    func didTapAttributedTextInTextView(textView: UITextView, inRange targetRange: NSRange) -> Bool {
        let layoutManager = textView.layoutManager
        let locationOfTouch = self.location(in: textView)
        let index = layoutManager.characterIndex(for: locationOfTouch, in: textView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        
        return NSLocationInRange(index, targetRange)
    }
}

次のセレクターを使用してテキスト ビューに追加UITapGestureRecognizerします。

guard let text = textView.attributedText?.string else {
        return
}
let textToTap = "Tap me"
if let range = text.range(of: textToTap),
      tapGesture.didTapAttributedTextInTextView(textView: textTextView, inRange: NSRange(range, in: text)) {
                // Tap recognized
}
于 2019-08-23T15:31:44.077 に答える
1

This one might work OK with short link, multilink in a textview. It work OK with iOS 6,7,8.

- (void)tappedTextView:(UITapGestureRecognizer *)tapGesture {
    if (tapGesture.state != UIGestureRecognizerStateEnded) {
        return;
    }
    UITextView *textView = (UITextView *)tapGesture.view;
    CGPoint tapLocation = [tapGesture locationInView:textView];

    NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink|NSTextCheckingTypePhoneNumber
                                                           error:nil];
    NSArray* resultString = [detector matchesInString:self.txtMessage.text options:NSMatchingReportProgress range:NSMakeRange(0, [self.txtMessage.text length])];
    BOOL isContainLink = resultString.count > 0;

    if (isContainLink) {
        for (NSTextCheckingResult* result in  resultString) {
            CGRect linkPosition = [self frameOfTextRange:result.range inTextView:self.txtMessage];

            if(CGRectContainsPoint(linkPosition, tapLocation) == 1){
                if (result.resultType == NSTextCheckingTypePhoneNumber) {
                    NSString *phoneNumber = [@"telprompt://" stringByAppendingString:result.phoneNumber];
                    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:phoneNumber]];
                }
                else if (result.resultType == NSTextCheckingTypeLink) {
                    [[UIApplication sharedApplication] openURL:result.URL];
                }
            }
        }
    }
}

 - (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView
{
    UITextPosition *beginning = textView.beginningOfDocument;
    UITextPosition *start = [textView positionFromPosition:beginning offset:range.location];
    UITextPosition *end = [textView positionFromPosition:start offset:range.length];
    UITextRange *textRange = [textView textRangeFromPosition:start toPosition:end];
    CGRect firstRect = [textView firstRectForRange:textRange];
    CGRect newRect = [textView convertRect:firstRect fromView:textView.textInputView];
    return newRect;
}
于 2014-11-26T01:20:48.873 に答える