在iOS中检测UITextView中的属性文本的点击

我有一个显示NSAttributedString的UITextView。 这个字符串包含了我想要点击的单词,这样,当它们被点击时,我会被回叫,以便我可以执行一个动作。 我意识到UITextView可以检测URL上的点击并回拨我的委托,但这些不是URL。

在我看来,使用iOS7和TextKit的强大功能现在应该可以实现,但是我找不到任何示例,我不确定从哪里开始。

我明白,现在可以在字符串中创建自定义属性(尽管我还没有这样做),也许这些将有助于检测是否有一个魔术字已被挖掘? 在任何情况下,我仍然不知道如何拦截那个水龙头,并检测水龙头发生在哪个单词上。

请注意,iOS 6兼容性不是必需的。


我只是想帮助别人。 继Shmidt的回应之后,我可以完全按照我在原始问题中所提出的做法。

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

    }
}

当你知道如何很容易!


更新了Swift 3

用Swift检测属性文本的点击

有时候对于初学者来说,知道如何去做事情有点困难(无论如何都是这样),所以这个例子更加完整,并且使用了Swift 3。

UITextView添加到您的项目中。

在这里输入图像描述

设置

在“属性”检查器中使用以下设置:

出口

使用名为textView的插座将UITextView连接到ViewController

将代码添加到您的View Controller中以检测水龙头。 请注意UIGestureRecognizerDelegate

import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate {

    @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Create an attributed string
        let myString = NSMutableAttributedString(string: "Swift attributed text")

        // Set an attribute on part of the string
        let myRange = NSRange(location: 0, length: 5) // range of "Swift"
        let myCustomAttribute = [ "MyCustomAttributeName": "some value"]
        myString.addAttributes(myCustomAttribute, range: myRange)

        textView.attributedText = myString

        // Add tap gesture recognizer to Text View
        let tap = UITapGestureRecognizer(target: self, action: #selector(myMethodToHandleTap(_:)))
        tap.delegate = self
        textView.addGestureRecognizer(tap)
    }

    func myMethodToHandleTap(_ sender: UITapGestureRecognizer) {

        let myTextView = sender.view as! UITextView
        let layoutManager = myTextView.layoutManager

        // location of tap in myTextView coordinates and taking the inset into account
        var location = sender.location(in: myTextView)
        location.x -= myTextView.textContainerInset.left;
        location.y -= myTextView.textContainerInset.top;

        // character index at tap location
        let characterIndex = layoutManager.characterIndex(for: location, in: myTextView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

        // if index is valid then do something.
        if characterIndex < myTextView.textStorage.length {

            // print the character index
            print("character index: (characterIndex)")

            // print the character at the index
            let myRange = NSRange(location: characterIndex, length: 1)
            let substring = (myTextView.attributedText.string as NSString).substring(with: myRange)
            print("character at index: (substring)")

            // check if the tap location has a certain attribute
            let attributeName = "MyCustomAttributeName"
            let attributeValue = myTextView.attributedText.attribute(attributeName, at: characterIndex, effectiveRange: nil) as? String
            if let value = attributeValue {
                print("You tapped on (attributeName) and the value is: (value)")
            }

        }
    }
}

在这里输入图像描述

现在,如果你点击“Swift”的“w”,你应该得到以下结果:

在这里输入图像描述

笔记

  • 在这里,我使用了自定义属性,但它可能很容易被NSForegroundColorAttributeName (文本颜色),有值UIColor.greenColor()
  • 这仅适用于文本视图设置为不可编辑且不可选的情况,如上面的“设置”部分所述。 使其可编辑和可选是下面评论中讨论的问题的原因。
  • 进一步研究

    这个答案是基于这个问题的其他几个答案。 除此之外,另请参阅

  • 高级文本布局和文本工具包效果(WWDC 2013视频)
  • “属性字符串编程指南”
  • 我如何使用Swift创建一个属性字符串?

  • 这是一个稍微修改过的版本,基于@tarmes的答案。 如果没有下面的调整,我无法让value变量返回任何内容,而是返回null 。 此外,我需要返回完整的属性字典才能确定最终的操作。 我会把这个在评论中,但似乎没有代表这样做。 如果我违反了协议,请提前道歉。

    具体的调整是使用textView.textStorage而不是textView.attributedText 。 作为一名仍在学习iOS的程序员,我不太确定这是为什么,但也许别人可以启发我们。

    抽头处理方法中的特定修改:

        NSDictionary *attributesOfTappedText = [textView.textStorage attributesAtIndex:characterIndex effectiveRange:&range];
    

    在我的视图控制器完整的代码

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        self.textView.attributedText = [self attributedTextViewString];
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(textTapped:)];
    
        [self.textView addGestureRecognizer:tap];
    }  
    
    - (NSAttributedString *)attributedTextViewString
    {
        NSMutableAttributedString *paragraph = [[NSMutableAttributedString alloc] initWithString:@"This is a string with " attributes:@{NSForegroundColorAttributeName:[UIColor blueColor]}];
    
        NSAttributedString* attributedString = [[NSAttributedString alloc] initWithString:@"a tappable string"
                                                                           attributes:@{@"tappable":@(YES),
                                                                                        @"networkCallRequired": @(YES),
                                                                                        @"loadCatPicture": @(NO)}];
    
        NSAttributedString* anotherAttributedString = [[NSAttributedString alloc] initWithString:@" and another tappable string"
                                                                                  attributes:@{@"tappable":@(YES),
                                                                                               @"networkCallRequired": @(NO),
                                                                                               @"loadCatPicture": @(YES)}];
        [paragraph appendAttributedString:attributedString];
        [paragraph appendAttributedString:anotherAttributedString];
    
        return [paragraph copy];
    }
    
    - (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;
    
        NSLog(@"location: %@", NSStringFromCGPoint(location));
    
        // 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;
            NSDictionary *attributes = [textView.textStorage attributesAtIndex:characterIndex effectiveRange:&range];
            NSLog(@"%@, %@", attributes, NSStringFromRange(range));
    
            //Based on the attributes, do something
            ///if ([attributes objectForKey:...)] //make a network call, load a cat Pic, etc
    
        }
    }
    
    链接地址: http://www.djcxy.com/p/30673.html

    上一篇: Detecting taps on attributed text in a UITextView in iOS

    下一篇: Simulate a 2 finger single tap on the simulator