การตรวจจับการแตะที่ข้อความที่ระบุใน UITextView ใน iOS


122

ฉันมีUITextViewที่แสดงNSAttributedStringไฟล์. สตริงนี้มีคำที่ฉันต้องการทำให้แตะได้ดังนั้นเมื่อมีการแตะฉันจะถูกเรียกกลับเพื่อให้ฉันสามารถดำเนินการได้ ฉันทราบดีว่าUITextViewสามารถตรวจจับการแตะ URL และโทรกลับผู้รับมอบสิทธิ์ของฉันได้ แต่นี่ไม่ใช่ URL

สำหรับฉันแล้วดูเหมือนว่าด้วย iOS 7 และพลังของ TextKit ตอนนี้น่าจะเป็นไปได้ แต่ฉันไม่พบตัวอย่างใด ๆ และฉันไม่แน่ใจว่าจะเริ่มจากตรงไหน

ฉันเข้าใจว่าตอนนี้คุณสามารถสร้างแอตทริบิวต์ที่กำหนดเองในสตริงได้แล้ว (แม้ว่าฉันจะยังไม่ได้ทำก็ตาม) และบางทีสิ่งเหล่านี้อาจเป็นประโยชน์ในการตรวจจับว่ามีการแตะคำวิเศษหรือไม่? ไม่ว่าในกรณีใดฉันยังไม่ทราบวิธีสกัดกั้นการแตะและตรวจจับคำที่เกิดจากการแตะ

โปรดทราบว่าไม่จำเป็นต้องใช้ความเข้ากันได้กับ iOS 6

คำตอบ:


118

ฉันแค่อยากจะช่วยคนอื่นอีกหน่อย จากคำตอบของ 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);

    }
}

ง่ายมากเมื่อคุณรู้วิธี!


คุณจะแก้ปัญหานี้อย่างไรใน IOS 6 คุณช่วยดูคำถามนี้ได้ไหม stackoverflow.com/questions/19837522/…
Steaphann

จริงๆ characterIndexForPoint: inTextContainer: fractionOfDistanceBetweenInsertionPoints พร้อมใช้งานบน iOS 6 ดังนั้นฉันคิดว่ามันน่าจะใช้ได้ แจ้งให้เราทราบ! ดูตัวอย่างโครงการนี้: github.com/laevandus/NSTextFieldHyperlinks/blob/master/…
tarmes

เอกสารระบุว่าใช้ได้เฉพาะใน IOS 7 หรือใหม่กว่า :)
Steaphann

1
ใช่ขอโทษ. ฉันเริ่มสับสนกับ Mac OS! นี่คือ iOS7 เท่านั้น
ทาร์เมส

ดูเหมือนจะไม่ได้ผลเมื่อคุณไม่มี UITextView ที่เลือกไม่ได้
Paul Brewczynski

64

การตรวจจับการแตะข้อความที่มาจากแหล่งข้อมูลด้วย Swift

บางครั้งสำหรับผู้เริ่มต้นมันยากที่จะรู้วิธีตั้งค่าสิ่งต่างๆ (สำหรับฉันแล้วล่ะ) ตัวอย่างนี้จึงค่อนข้างสมบูรณ์

เพิ่มUITextViewในโครงการของคุณ

ทางออก

เชื่อมต่อUITextViewไปยังมีเต้าเสียบที่มีชื่อว่าViewControllertextView

แอตทริบิวต์ที่กำหนดเอง

เรากำลังจะทำให้แอตทริบิวต์ที่กำหนดเองโดยการขยาย

หมายเหตุ:ขั้นตอนนี้จะเป็นตัวเลือกในทางเทคนิค NSAttributedString.Key.foregroundColorแต่ถ้าคุณไม่ทำเช่นนั้นคุณจะต้องแก้ไขโค้ดในส่วนต่อไปที่จะใช้แอตทริบิวต์มาตรฐานเช่น ข้อดีของการใช้แอตทริบิวต์ที่กำหนดเองคือคุณสามารถกำหนดค่าที่คุณต้องการจัดเก็บในช่วงข้อความที่มีการระบุแหล่งที่มา

เพิ่มไฟล์ที่รวดเร็วใหม่ที่มีไฟล์> ใหม่> File ... > iOS> แหล่งที่มา> สวิฟท์ไฟล์ คุณสามารถเรียกมันว่าสิ่งที่คุณต้องการ ฉันกำลังเรียกNSAttributedStringKey + CustomAttribute.swift ของฉัน

วางรหัสต่อไปนี้:

import Foundation

extension NSAttributedString.Key {
    static let myAttributeName = NSAttributedString.Key(rawValue: "MyCustomAttribute")
}

รหัส

แทนที่รหัสใน ViewController.swift ดังต่อไปนี้ หมายเหตุ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 = [ NSAttributedString.Key.myAttributeName: "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)
    }

    @objc 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 = NSAttributedString.Key.myAttributeName
            let attributeValue = myTextView.attributedText?.attribute(attributeName, at: characterIndex, effectiveRange: nil)
            if let value = attributeValue {
                print("You tapped on \(attributeName.rawValue) and the value is: \(value)")
            }

        }
    }
}

ใส่คำอธิบายภาพที่นี่

ตอนนี้ถ้าคุณแตะที่ "w" ของ "Swift" คุณจะได้รับผลลัพธ์ดังต่อไปนี้:

character index: 1
character at index: w
You tapped on MyCustomAttribute and the value is: some value

หมายเหตุ

  • ที่นี่ฉันใช้แอตทริบิวต์ที่กำหนดเอง แต่อาจมีNSAttributedString.Key.foregroundColor(สีข้อความ) ที่มีค่าเป็นUIColor.green.
  • เดิมมุมมองข้อความไม่สามารถแก้ไขหรือเลือกได้ แต่ในคำตอบที่อัปเดตของฉันสำหรับ Swift 4.2 ดูเหมือนว่าจะทำงานได้ดีไม่ว่าจะเลือกสิ่งเหล่านี้หรือไม่ก็ตาม

ศึกษาเพิ่มเติม

คำตอบนี้มาจากคำตอบอื่น ๆ สำหรับคำถามนี้ นอกจากนี้ให้ดูด้วย


ใช้myTextView.textStorageแทน myTextView.attributedText.string
fatihyildizhan

การตรวจจับการแตะผ่านท่าทางการแตะใน iOS 9 ใช้ไม่ได้สำหรับการแตะต่อเนื่อง มีการอัปเดตเกี่ยวกับเรื่องนี้หรือไม่?
Dheeraj Jami

1
@WaqasMahmood ฉันเริ่มคำถามใหม่สำหรับปัญหานี้ คุณสามารถติดดาวและกลับมาดูคำตอบในภายหลังได้ อย่าลังเลที่จะแก้ไขคำถามนั้นหรือเพิ่มความคิดเห็นหากมีรายละเอียดที่เกี่ยวข้องเพิ่มเติม
Suragch

1
@dejix ฉันแก้ไขปัญหาโดยการเพิ่มทุกครั้งที่มีสตริงว่าง "" อื่นต่อท้าย TextView ของฉัน วิธีนี้จะหยุดการตรวจจับหลังจากคำสุดท้ายของคุณ หวังว่าจะช่วยได้นะ
PoolHallJunkie

1
ใช้งานได้อย่างสมบูรณ์แบบกับการแตะหลาย ๆ ครั้งฉันเพิ่งทำกิจวัตรสั้น ๆ เพื่อพิสูจน์สิ่งนี้: if characterIndex <12 {textView.textColor = UIColor.magenta} else {textView.textColor = UIColor.blue} รหัสที่ชัดเจนและเรียบง่าย
Jeremy Andrews

32

นี่เป็นเวอร์ชันที่ปรับเปลี่ยนเล็กน้อยโดยสร้างจากคำตอบของ @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

    }
}

มีปัญหาเดียวกันกับ textView.attributedText! ขอบคุณสำหรับคำแนะนำ textView.textStorage!
Kai Burghardt

การตรวจจับการแตะผ่านท่าทางการแตะใน iOS 9 ใช้ไม่ได้สำหรับการแตะต่อเนื่อง
Dheeraj Jami

25

การสร้างลิงก์ที่กำหนดเองและทำสิ่งที่คุณต้องการบนการแตะกลายเป็นเรื่องง่ายขึ้นมากด้วย iOS 7 มีตัวอย่างที่ดีมากที่Ray Wenderlich


นี่เป็นวิธีแก้ปัญหาที่สะอาดกว่าการพยายามคำนวณตำแหน่งสตริงที่สัมพันธ์กับมุมมองคอนเทนเนอร์
Chris C

2
ปัญหาคือต้องเลือก textView และฉันไม่ต้องการพฤติกรรมนี้
Thomás Calmon

@ ThomásC +1 สำหรับตัวชี้ว่าเหตุใดฉันUITextViewจึงตรวจไม่พบลิงก์แม้ว่าฉันจะตั้งค่าให้ตรวจพบผ่าน IB ก็ตาม (ฉันยังทำให้ไม่สามารถเลือกได้)
Kedar Paranjape

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:
}

ขอบคุณ! ฉันจะดูวิดีโอ WWDC ด้วย
ทาร์เมส

@Suragch "เค้าโครงข้อความขั้นสูงและเอฟเฟกต์พร้อมชุดข้อความ"
Shmidt

10

ฉันสามารถแก้ปัญหานี้ได้อย่างง่ายดายด้วย 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
  }

}

คุณควรตรวจสอบว่า URL ของคุณถูกแตะและไม่ใช่ URL อื่นที่มีif URL.scheme == "cs"และreturn trueอยู่นอกifคำสั่งเพื่อให้UITextViewสามารถจัดการhttps://ลิงก์ปกติที่มีการแตะได้
Daniel Storm

ฉันทำเช่นนั้นและทำงานได้ดีพอสมควรบน iPhone 6 และ 6+ แต่ไม่ได้ผลเลยบน iPhone 5 ไปกับโซลูชัน Suragch ด้านบนซึ่งใช้งานได้ดี ไม่เคยพบว่าทำไม iPhone 5 ถึงมีปัญหากับสิ่งนี้ไม่มีเหตุผล
13

9

ตัวอย่างที่สมบูรณ์สำหรับการตรวจจับการดำเนินการกับข้อความที่ระบุแหล่งที่มาด้วย Swift 3

let termsAndConditionsURL = TERMS_CONDITIONS_URL;
let privacyURL            = PRIVACY_URL;

override func viewDidLoad() {
    super.viewDidLoad()

    self.txtView.delegate = self
    let str = "By continuing, you accept the Terms of use and Privacy policy"
    let attributedString = NSMutableAttributedString(string: str)
    var foundRange = attributedString.mutableString.range(of: "Terms of use") //mention the parts of the attributed text you want to tap and get an custom action
    attributedString.addAttribute(NSLinkAttributeName, value: termsAndConditionsURL, range: foundRange)
    foundRange = attributedString.mutableString.range(of: "Privacy policy")
    attributedString.addAttribute(NSLinkAttributeName, value: privacyURL, range: foundRange)
    txtView.attributedText = attributedString
}

จากนั้นคุณสามารถจับการกระทำด้วย shouldInteractWith URLวิธีการมอบสิทธิ์ UITextViewDelegate ดังนั้นตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่าผู้รับมอบสิทธิ์อย่างถูกต้อง

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: "WebView") as! SKWebViewController

        if (URL.absoluteString == termsAndConditionsURL) {
            vc.strWebURL = TERMS_CONDITIONS_URL
            self.navigationController?.pushViewController(vc, animated: true)
        } else if (URL.absoluteString == privacyURL) {
            vc.strWebURL = PRIVACY_URL
            self.navigationController?.pushViewController(vc, animated: true)
        }
        return false
    }

เช่นเดียวกับที่ชาญฉลาดคุณสามารถดำเนินการใด ๆ ตามความต้องการของคุณ

ไชโย !!


ขอบคุณ! คุณช่วยวันของฉัน!
Dmih

4

characterIndexForPoint:inTextContainer:fractionOfDistanceBetweenInsertionPoints:มันเป็นไปได้ที่จะทำอย่างนั้นด้วย มันจะทำงานค่อนข้างแตกต่างจากที่คุณต้องการ - คุณจะต้องทดสอบว่าอักขระที่แตะเป็นของไฟล์คำวิเศษหรือไม่ แต่ไม่น่าจะซับซ้อน

BTW ฉันขอแนะนำให้ดูแนะนำ Text Kitจาก WWDC 2013


4

กับสวิฟท์ 5 และ iOS 12 คุณสามารถสร้าง subclass ของUITextViewและแทนที่point(inside:with:)ด้วยการดำเนินการบาง TextKit เพื่อให้เพียงบางส่วนNSAttributedStringsในนั้น tappable


รหัสต่อไปนี้แสดงวิธีการสร้างUITextViewที่ตอบสนองต่อการแตะที่ขีดเส้นใต้NSAttributedStringเท่านั้น:

InteractiveUnderlinedTextView.swift

import UIKit

class InteractiveUnderlinedTextView: UITextView {

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        configure()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }

    func configure() {
        isScrollEnabled = false
        isEditable = false
        isSelectable = false
        isUserInteractionEnabled = true
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let superBool = super.point(inside: point, with: event)

        let characterIndex = layoutManager.characterIndex(for: point, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        guard characterIndex < textStorage.length else { return false }
        let attributes = textStorage.attributes(at: characterIndex, effectiveRange: nil)

        return superBool && attributes[NSAttributedString.Key.underlineStyle] != nil
    }

}

ViewController.swift

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let linkTextView = InteractiveUnderlinedTextView()
        linkTextView.backgroundColor = .orange

        let mutableAttributedString = NSMutableAttributedString(string: "Some text\n\n")
        let attributes = [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue]
        let underlinedAttributedString = NSAttributedString(string: "Some other text", attributes: attributes)
        mutableAttributedString.append(underlinedAttributedString)
        linkTextView.attributedText = mutableAttributedString

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(underlinedTextTapped))
        linkTextView.addGestureRecognizer(tapGesture)

        view.addSubview(linkTextView)
        linkTextView.translatesAutoresizingMaskIntoConstraints = false
        linkTextView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        linkTextView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        linkTextView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor).isActive = true

    }

    @objc func underlinedTextTapped(_ sender: UITapGestureRecognizer) {
        print("Hello")
    }

}

สวัสดีมีวิธีใดบ้างที่จะทำให้สิ่งนี้เป็นไปตามแอตทริบิวต์หลายรายการแทนที่จะเป็นเพียงหนึ่งเดียว
David Lintin

1

อันนี้อาจใช้ได้กับลิงค์แบบสั้นมัลติลิงค์ใน textview ทำงานได้ดีกับ 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;
}

การตรวจจับการแตะผ่านท่าทางการแตะใน iOS 9 ใช้ไม่ได้สำหรับการแตะต่อเนื่อง
Dheeraj Jami

1

ใช้ส่วนขยายนี้สำหรับ 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: tapableText),
      tapGesture.didTapAttributedTextInTextView(textView: textTextView, inRange: NSRange(range, in: text)) {
                // Tap recognized
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.