यूआईबेल के NSAttributedString में टैप-सक्षम "लिंक" बनाएं?


233

मैं इसे घंटों से खोज रहा हूं लेकिन मैं असफल रहा हूं। मैं शायद यह भी नहीं जानता कि मुझे क्या देखना चाहिए।

कई एप्लिकेशन में टेक्स्ट होता है और इस टेक्स्ट में राउंडेड रेक्ट में वेब हाइपरलिंक होते हैं। जब मैं क्लिक करता हूं तो वे UIWebViewखुलते हैं। मेरे लिए क्या पहेली है कि उनके पास अक्सर कस्टम लिंक होते हैं, उदाहरण के लिए यदि शब्द # के साथ शुरू होता है तो यह क्लिक करने योग्य भी है और एप्लिकेशन एक अन्य दृश्य खोलकर प्रतिक्रिया करता है। मैं उसे कैसे कर सकता हूँ? क्या यह UILabelमेरे साथ संभव है या मुझे UITextViewकुछ और चाहिए?


देखें: पूरी तरह से काम कर रहे समाधान के लिए stackoverflow.com/questions/50505334/…Swift 4 । यह उपयोग करता है UITextViewलेकिन यह एक की तरह व्यवहार करता है UILabel। मैंने यहां समाधानों की कोशिश की, और सटीक लिंक का पता लगाने में विफल रहा।
दान ब्रे

जवाबों:


208

सामान्य तौर पर, यदि हम UILabel द्वारा प्रदर्शित पाठ में एक क्लिक करने योग्य लिंक चाहते हैं, तो हमें दो स्वतंत्र कार्यों को हल करने की आवश्यकता होगी:

  1. लिंक की तरह दिखने के लिए पाठ के एक हिस्से की उपस्थिति को बदलना
  2. लिंक पर स्पर्श का पता लगाना और संभालना (URL खोलना एक विशेष मामला है)

पहला आसान है। आईओएस 6 से शुरू यूआईलेबेल जिम्मेदार तार के प्रदर्शन का समर्थन करता है। NSMutableAttributedString की एक आवृत्ति बनाने और कॉन्फ़िगर करने के लिए आपको बस इतना करना है:

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"String with a link" attributes:nil];
NSRange linkRange = NSMakeRange(14, 4); // for the word "link" in the string above

NSDictionary *linkAttributes = @{ NSForegroundColorAttributeName : [UIColor colorWithRed:0.05 green:0.4 blue:0.65 alpha:1.0],
                                  NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) };
[attributedString setAttributes:linkAttributes range:linkRange];

// Assign attributedText to UILabel
label.attributedText = attributedString;

बस! उपरोक्त कोड एक लिंक के साथ स्ट्रिंग प्रदर्शित करने के लिए UILabel बनाता है

अब हमें इस लिंक पर स्पर्श का पता लगाना चाहिए। विचार यह है कि यूआईलैब के भीतर सभी नल को पकड़ना है और यह पता लगाना है कि क्या नल का स्थान लिंक के काफी करीब था। स्पर्शों को पकड़ने के लिए हम लेबल पर टैप जेस्चर पहचानकर्ता जोड़ सकते हैं। लेबल के लिए userInteraction सक्षम करना सुनिश्चित करें, यह डिफ़ॉल्ट रूप से बंद है:

label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]]; 

अब सबसे अधिक परिष्कृत सामान: यह पता लगाना कि नल कहां था, जहां लिंक प्रदर्शित किया गया है और लेबल के किसी अन्य हिस्से पर नहीं। यदि हमारे पास एकल-पंक्तिवाला यूलेबेल होता है, तो यह कार्य उस क्षेत्र की सीमा को हार्डकॉन्ड करके अपेक्षाकृत आसान तरीके से हल किया जा सकता है जहां लिंक प्रदर्शित होता है, लेकिन आइए इस समस्या को अधिक सुरुचिपूर्ण ढंग से और सामान्य स्थिति के लिए हल करें - लिंक लेआउट के बारे में प्रारंभिक जानकारी के बिना बहु-यूआईबेल।

IOS 7 में प्रस्तुत पाठ किट एपीआई की क्षमताओं का उपयोग करने के लिए एक दृष्टिकोण है:

// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];

// Configure layoutManager and textStorage
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];

// Configure textContainer
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = label.lineBreakMode;
textContainer.maximumNumberOfLines = label.numberOfLines;

अपनी कक्षा में गुणों में NSLayoutManager, NSTextContainer और NSTextStorage के बनाए और कॉन्फ़िगर किए गए इंस्टेंस को सहेजें (सबसे अधिक संभावना UIViewController के वंशज) - हमें उन्हें अन्य तरीकों की आवश्यकता होगी।

अब, हर बार लेबल अपना फ़्रेम बदलता है, टेक्स्ट कॉन्टेनर का आकार अपडेट करें:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    self.textContainer.size = self.label.bounds.size;
}

और अंत में, पता लगाएँ कि क्या नल ठीक लिंक पर था:

- (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture
{
    CGPoint locationOfTouchInLabel = [tapGesture locationInView:tapGesture.view];
    CGSize labelSize = tapGesture.view.bounds.size;
    CGRect textBoundingBox = [self.layoutManager usedRectForTextContainer:self.textContainer];
    CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                                              (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
    CGPoint locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
                                                         locationOfTouchInLabel.y - textContainerOffset.y);
    NSInteger indexOfCharacter = [self.layoutManager characterIndexForPoint:locationOfTouchInTextContainer
                                                            inTextContainer:self.textContainer
                                   fractionOfDistanceBetweenInsertionPoints:nil];
    NSRange linkRange = NSMakeRange(14, 4); // it's better to save the range somewhere when it was originally used for marking link in attributed string
    if (NSLocationInRange(indexOfCharacter, linkRange)) {
        // Open an URL, or handle the tap on the link in any other way
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://stackoverflow.com/"]];
    }
}

1
मैं इसे कैसे व्यवस्थित करूंगा cellForRowAtIndexPath? मैं इंस्टेंस बना रहा हूं और इसके अंदर फ़ंक्शन को cellForRowAtIndexPathहोस्ट कर रहा हूं handleTapOnLabel। पर cell.textLabel.addGestureRecognizer(UITapGestureRecognizer(target: cell, action: "handleTapOnLabel:")), मुझे मिल रहा है unrecognized selector
स्लाइडर

13
यह समाधान मानता है कि लेबल की textAlignmentविशेषता के लिए सेट है NSTextAlignmentCenter। यदि आप गैर-केंद्रित पाठ का उपयोग कर रहे हैं, तो आपको textContainerOffsetउपरोक्त कोड में अपनी गणना समायोजित करनी होगी ।
ब्रैडबी

18
@AndreyM। के xमूल्य की गणना करते समय textContainerOffset, स्थिरांक 0.5का उपयोग किया जाता है। इसके लिए सही स्थिति की गणना करेगा NSTextAlignmentCent‌er। बाईं ओर, प्राकृतिक या उचित संरेखित करने के लिए, के मान का उपयोग करें 0.0। सही संरेखित करने के लिए, का उपयोग करें 1.0
ब्रेडब

5
यह मेरे लिए भी काम करता है, लेकिन केवल लेबल की एक पंक्ति के लिए। यदि लेबल में 1 से अधिक पंक्ति है तो यह विधि उचित काम नहीं कर रही है। क्या कोई बता सकता है कि वह एक ही कार्य को कई लाइन के साथ कर सकता है
क्रेजी डेवलपर

1
@CrazyDeveloper self.textContainer.size = self.label.bounds.size जोड़ें; हैंडलटैपऑनलैबेल में। मेरे लिए यह काम किया
RadioLog

58

मैं @zekel के उत्कृष्ट विस्तार और स्विफ्ट में उपलब्ध कराने के साथ, @NAlexN मूल विस्तृत समाधान का विस्तार कर रहा हूं ।UITapGestureRecognizer

UITapGestureRecognizer का विस्तार

extension UITapGestureRecognizer {

    func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
        // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: CGSize.zero)
        let textStorage = NSTextStorage(attributedString: label.attributedText!)

        // Configure layoutManager and textStorage
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)

        // Configure textContainer
        textContainer.lineFragmentPadding = 0.0
        textContainer.lineBreakMode = label.lineBreakMode
        textContainer.maximumNumberOfLines = label.numberOfLines
        let labelSize = label.bounds.size
        textContainer.size = labelSize

        // Find the tapped character location and compare it to the specified range
        let locationOfTouchInLabel = self.location(in: label)
        let textBoundingBox = layoutManager.usedRect(for: textContainer)
        let textContainerOffset = CGPoint(
            x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
            y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y
        )
        let locationOfTouchInTextContainer = CGPoint(
            x: locationOfTouchInLabel.x - textContainerOffset.x,
            y: locationOfTouchInLabel.y - textContainerOffset.y
        )
        let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

        return NSLocationInRange(indexOfCharacter, targetRange)
    }

}

प्रयोग

UIGestureRecognizerकरने के लिए कार्रवाई भेजने के लिए सेटअप tapLabel:, और आप पता लगा सकते हैं कि लक्ष्य पर्वतमाला में टेप किया जा रहा है या नहीं myLabel

@IBAction func tapLabel(gesture: UITapGestureRecognizer) {
    if gesture.didTapAttributedTextInLabel(myLabel, inRange: targetRange1) {
        print("Tapped targetRange1")
    } else if gesture.didTapAttributedTextInLabel(myLabel, inRange: targetRange2) {
        print("Tapped targetRange2")
    } else {
        print("Tapped none")
    }
}

महत्वपूर्ण: UILabelलाइन ब्रेक मोड को वर्ड / चार द्वारा रैप करने के लिए सेट किया जाना चाहिए। किसी तरह, NSTextContainerमान लेंगे कि पाठ केवल एक पंक्ति है यदि लाइन ब्रेक मोड अन्यथा है।



@Koen यह कई लिंक के साथ काम करता है। उदाहरण के लिए targetRange1और के साथ उपयोग देखें targetRange2
samwize

2
किसी को अब भी कई पंक्तियों या गलत सीमा मुद्दों के साथ मुद्दों कर के लिए, करने के लिए अपने UILabel सेट जिम्मेदार ठहराया अनुमति दें, फिर शब्द रैप , और करने के लिए लेबल के लिए जिम्मेदार ठहराया पाठ सेट NSMutableAttributedString(attributedString: text)जहां 'पाठ' एक हैNSAttributedString
Mofe Ejegi

@ Mofe-hendyEjegi मैं अभी भी बहु लाइन पाठ के साथ समस्याएँ हैं। मैं यूलेबेल चौड़ाई पर बाधाओं के साथ ऑटो लेआउट का उपयोग कर रहा हूं। क्या वह बात होगी?
केनो

मुझे मैन्युअल रूप से textContainerOffset.x को 0 पर सेट करना था, क्योंकि गणना की गई सामग्री textAligner के लिए काम नहीं कर रही थी। क्या यह आप लोगों के लिए काम कर रहा है? मुझे लगता है कि यदि संरेखण केंद्र था तो गणना मूल्य सही है।
बीके

51

पुराना सवाल लेकिन अगर कोई UITextViewइसके बजाय इस्तेमाल कर सकता है UILabel, तो यह आसान है। मानक URL, फ़ोन नंबर आदि का स्वचालित रूप से पता लगाया जाएगा (और क्लिक करने योग्य)।

हालाँकि, यदि आपको कस्टम पहचान की आवश्यकता है, अर्थात यदि आप किसी विशेष शब्द पर उपयोगकर्ता के क्लिक करने के बाद किसी भी कस्टम विधि को कॉल करने में सक्षम होना चाहते हैं, तो आपको NSAttributedStringsएक NSLinkAttributeNameविशेषता के साथ उपयोग करने की आवश्यकता है जो एक कस्टम URL स्कीम की ओर इशारा करेगा (जैसा कि विरोध किया गया है) डिफ़ॉल्ट रूप से HTTP यूआरएल योजना) होने। रे वेंडरलिच ने इसे यहां कवर किया है

उपरोक्त लिंक से कोड उद्धृत करना:

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"This is an example by @marcelofabri_"];
[attributedString addAttribute:NSLinkAttributeName
                     value:@"username://marcelofabri_"
                     range:[[attributedString string] rangeOfString:@"@marcelofabri_"]];

NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor greenColor],
                             NSUnderlineColorAttributeName: [UIColor lightGrayColor],
                             NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};

// assume that textView is a UITextView previously created (either by code or Interface Builder)
textView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;
textView.delegate = self;

उन लिंक क्लिकों का पता लगाने के लिए, इसे लागू करें:

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
    if ([[URL scheme] isEqualToString:@"username"]) {
        NSString *username = [URL host]; 
        // do something with this username
        // ...
        return NO;
    }
    return YES; // let the system open this URL
}

पुनश्च: सुनिश्चित करें कि आपका UITextViewहै selectable


इसे स्वीकार किया जाना चाहिए। मैंने @NAlexN द्वारा कोड प्राप्त करने की कोशिश में काफी समय बिताया और फिर इसे 5 मिनट में UITextView के साथ लागू किया।
charlag

इसके साथ समस्या यह है कि यदि आप इसे विभिन्न लिंक के लिए सामान्य बनाना चाहते हैं, तो आपको यह जांचना होगा कि उपयुक्त कार्रवाई करने के लिए URL क्या है
hariszaman

33

यदि आप इसके लिए कोई चित्र सेट नहीं करते हैं तो UIButtonTypeCustom एक क्लिक करने योग्य लेबल है।


22
केवल अगर पूरा पाठ क्लिक करने योग्य है और केवल एक लिंक है।
जॉन पैंग

33

(मेरा उत्तर @ NAlexN के उत्कृष्ट उत्तर पर बनता है । मैं यहां प्रत्येक चरण के उनके विस्तृत विवरण की नकल नहीं करूंगा।)

मैंने इसे UITapGestureRecognizer की श्रेणी के रूप में टैप-सक्षम UILabel पाठ के लिए समर्थन जोड़ने के लिए सबसे सुविधाजनक और सीधा पाया। (आपको UITextView के डेटा डिटेक्टरों का उपयोग करने की आवश्यकता नहीं है , जैसा कि कुछ उत्तर बताते हैं।)

निम्न विधि को अपने UITapGestureRecognizer श्रेणी में जोड़ें:

/**
 Returns YES if the tap gesture was within the specified range of the attributed text of the label.
 */
- (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange {
    NSParameterAssert(label != nil);

    CGSize labelSize = label.bounds.size;
    // create instances of NSLayoutManager, NSTextContainer and NSTextStorage
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText];

    // configure layoutManager and textStorage
    [layoutManager addTextContainer:textContainer];
    [textStorage addLayoutManager:layoutManager];

    // configure textContainer for the label
    textContainer.lineFragmentPadding = 0.0;
    textContainer.lineBreakMode = label.lineBreakMode;
    textContainer.maximumNumberOfLines = label.numberOfLines;
    textContainer.size = labelSize;

    // find the tapped character location and compare it to the specified range
    CGPoint locationOfTouchInLabel = [self locationInView:label];
    CGRect textBoundingBox = [layoutManager usedRectForTextContainer:textContainer];
    CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                                              (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
    CGPoint locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
                                                         locationOfTouchInLabel.y - textContainerOffset.y);
    NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInTextContainer
                                                            inTextContainer:textContainer
                                   fractionOfDistanceBetweenInsertionPoints:nil];
    if (NSLocationInRange(indexOfCharacter, targetRange)) {
        return YES;
    } else {
        return NO;
    }
}

उदाहरण कोड

// (in your view controller)    
// create your label, gesture recognizer, attributed text, and get the range of the "link" in your label
myLabel.userInteractionEnabled = YES;
[myLabel addGestureRecognizer:
   [[UITapGestureRecognizer alloc] initWithTarget:self 
                                           action:@selector(handleTapOnLabel:)]]; 

// create your attributed text and keep an ivar of your "link" text range
NSAttributedString *plainText;
NSAttributedString *linkText;
plainText = [[NSMutableAttributedString alloc] initWithString:@"Add label links with UITapGestureRecognizer"
                                                   attributes:nil];
linkText = [[NSMutableAttributedString alloc] initWithString:@" Learn more..."
                                                  attributes:@{
                                                      NSForegroundColorAttributeName:[UIColor blueColor]
                                                  }];
NSMutableAttributedString *attrText = [[NSMutableAttributedString alloc] init];
[attrText appendAttributedString:plainText];
[attrText appendAttributedString:linkText];

// ivar -- keep track of the target range so you can compare in the callback
targetRange = NSMakeRange(plainText.length, linkText.length);

इशारे से कॉलबैक

// handle the gesture recognizer callback and call the category method
- (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture {
    BOOL didTapLink = [tapGesture didTapAttributedTextInLabel:myLabel
                                            inRange:targetRange];
    NSLog(@"didTapLink: %d", didTapLink);

}

1
बस के बारे में यह काम कर रहा है - लेकिन मुझे linkText.location से परेशानी हो रही है - मेरे NSAttributedString में यह संपत्ति नहीं है?
मैट बोल्ट

1
@MattBolt ओह, यह एक गलती थी। यह लिंक टेक्स्ट का स्टार्ट इंडेक्स होना चाहिए, इस उदाहरण में यह होना चाहिए plainText.length
ज़ेकेल

CGPoint स्थान में त्रुटि हुईOfTouchInLabel = [self locationInView: लेबल];
मोनिका पटेल

@zekel इस समाधान के लिए बहुत धन्यवाद। लेकिन क्या आप बता सकते हैं कि "UITapGestureRecognizer श्रेणी में निम्नलिखित विधि जोड़ें" से आपका वास्तव में क्या मतलब है? मुझे यकीन नहीं है कि मुझे यहां क्या करना चाहिए।
eivindml

@eivindml आप मौजूदा वर्गों के तरीकों को जोड़ने के लिए श्रेणियों का उपयोग कर सकते हैं, जो उन कक्षाओं के साथ काम करने के लिए उपयोगी है जिन्हें आपने नहीं लिखा था UITapGestureRecognizer। श्रेणियों को जोड़ने के बारे में कुछ जानकारी यहाँ दी गई है ।
ज़ेकेल z ’

20

UITextViewOS3.0 में डेटा-डिटेक्टर का समर्थन UILabelकरता है , जबकि ऐसा नहीं करता है।

यदि आप डेटा-डिटेक्टरों को सक्षम करते हैं UITextViewऔर आपके पाठ में URL, फ़ोन नंबर आदि हैं, तो वे लिंक के रूप में दिखाई देंगे।


हाँ, मुझे इस बारे में पता है, लेकिन मुझे कस्टम डिटेक्शन की भी आवश्यकता है, उदाहरण #some_word जैसा कि मेरे प्रश्न में उल्लेख किया गया है
लोप

@ लोप आप अभी भी कर सकते हैं, बस उन्हें कस्टम यूआरएल-स्कीम जैसे hashtag://या कुछ और असाइन करें , फिर textView(_:shouldInteractWith:in:interaction:)उसका पता लगाने के लिए उपयोग करें। नीचे उत्तर देखें: stackoverflow.com/a/34014655/1161906
bcattle

14

4 स्विफ्ट के लिए @ samwize के एक्सटेंशन का अनुवाद:

extension UITapGestureRecognizer {
    func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
        guard let attrString = label.attributedText else {
            return false
        }

        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: .zero)
        let textStorage = NSTextStorage(attributedString: attrString)

        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)

        textContainer.lineFragmentPadding = 0
        textContainer.lineBreakMode = label.lineBreakMode
        textContainer.maximumNumberOfLines = label.numberOfLines
        let labelSize = label.bounds.size
        textContainer.size = labelSize

        let locationOfTouchInLabel = self.location(in: label)
        let textBoundingBox = layoutManager.usedRect(for: textContainer)
        let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y)
        let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)
        let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        return NSLocationInRange(indexOfCharacter, targetRange)
    }
}

पहचानकर्ता सेट करने के लिए (एक बार जब आप टेक्स्ट और सामान को रंगीन करते हैं):

lblTermsOfUse.isUserInteractionEnabled = true
lblTermsOfUse.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTapOnLabel(_:))))

... तो इशारा पहचानने वाला:

@objc func handleTapOnLabel(_ recognizer: UITapGestureRecognizer) {
    guard let text = lblAgreeToTerms.attributedText?.string else {
        return
    }

    if let range = text.range(of: NSLocalizedString("_onboarding_terms", comment: "terms")),
        recognizer.didTapAttributedTextInLabel(label: lblAgreeToTerms, inRange: NSRange(range, in: text)) {
        goToTermsAndConditions()
    } else if let range = text.range(of: NSLocalizedString("_onboarding_privacy", comment: "privacy")),
        recognizer.didTapAttributedTextInLabel(label: lblAgreeToTerms, inRange: NSRange(range, in: text)) {
        goToPrivacyPolicy()
    }
}

6
मेरे लिए काम नहीं कर रहा है। एक तर्क के रूप में की didTapAttributedTextInLabelजरूरत है, NSRangeलेकिन rangeTermsकुछ अलग देता है। इसके अलावा handleTapOnLabelसमारोह को @objcस्विफ्ट 4 में चिह्नित किया जाना चाहिए
पीकसेटाइप

10

जैसा कि मैंने इस पोस्ट में उल्लेख किया है , यहां एक लाइट-वेटेड लाइब्रेरी है जिसे मैंने विशेष रूप से UILabel FRHyperLabel में लिंक के लिए बनाया है ।

इस तरह एक प्रभाव प्राप्त करने के लिए:

लोरम इप्सम डोलर अमेट, कंसेटेटुर एडिपिसिंग एलीट। Pellentesque Quis blandit इरोस, बैठने अमेट vehicula जूस्तो। Urna नेक पर नाम। मेकेनस एसी सेम यूरोप वी पोर्टा डिक्टम एनईसी वेल टेलस।

उपयोग कोड:

//Step 1: Define a normal attributed string for non-link texts
NSString *string = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque quis blandit eros, sit amet vehicula justo. Nam at urna neque. Maecenas ac sem eu sem porta dictum nec vel tellus.";
NSDictionary *attributes = @{NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]};

label.attributedText = [[NSAttributedString alloc]initWithString:string attributes:attributes];


//Step 2: Define a selection handler block
void(^handler)(FRHyperLabel *label, NSString *substring) = ^(FRHyperLabel *label, NSString *substring){
    NSLog(@"Selected: %@", substring);
};


//Step 3: Add link substrings
[label setLinksForSubstrings:@[@"Lorem", @"Pellentesque", @"blandit", @"Maecenas"] withLinkHandler:handler];

1
क्या होगा यदि लेबल पाठ एपीआई से गतिशील है और आप पाठ की लंबाई नहीं जानते हैं तो लिंक कैसे बनाएं।
सुभाष शर्मा

स्विफ्ट 4 पर भी ठीक काम करता है।
होला

7

मैंने उत्तरदायीलेबेल नाम का यूआईबेल उपवर्ग बनाया, जो आईओएस 7. में शुरू की गई टेक्स्टकिट एपीआई पर आधारित है। यह नैलेक्सन द्वारा सुझाए गए उसी दृष्टिकोण का उपयोग करता है । यह पाठ में खोज करने के लिए एक पैटर्न निर्दिष्ट करने के लिए लचीलापन प्रदान करता है। कोई उन पैटर्नों पर लागू होने वाली शैलियों को निर्दिष्ट कर सकता है और साथ ही पैटर्नों के दोहन पर की जाने वाली कार्रवाई भी कर सकता है।

//Detects email in text

 NSString *emailRegexString = @"[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}";
 NSError *error;
 NSRegularExpression *regex = [[NSRegularExpression alloc]initWithPattern:emailRegexString options:0 error:&error];
 PatternDescriptor *descriptor = [[PatternDescriptor alloc]initWithRegex:regex withSearchType:PatternSearchTypeAll withPatternAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];
 [self.customLabel enablePatternDetection:descriptor];

यदि आप एक स्ट्रिंग को क्लिक करने योग्य बनाना चाहते हैं, तो आप इस तरह से कर सकते हैं। यह कोड स्ट्रिंग "टेक्स्ट" की प्रत्येक घटना के लिए विशेषताओं को लागू करता है।

PatternTapResponder tapResponder = ^(NSString *string) {
    NSLog(@"tapped = %@",string);
};

[self.customLabel enableStringDetection:@"text" withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor],
                                                                 RLTapResponderAttributeName: tapResponder}];

ResponsiveLabel के साथ काम करने के लिए अच्छे घटक लगते हैं, लेकिन किसी कारण से मैं क्लिक करने योग्य पाठ के लिए रंग सेट नहीं कर सकता, और क्लिकिंग स्ट्रिंग्स की सरणी सेट नहीं कर सकता।
मटरू अलेक्जेंडर

@MatrosovAlexander अभी, ResponsiveLabel में वह विधि नहीं है जो स्ट्रिंग की एक सरणी लेता है और उन्हें क्लिक करने योग्य बनाता है। आप जीथब पर एक मुद्दा बना सकते हैं और मैं इसे जल्द ही लागू करूंगा।
hsusmita

हाँ, यह एक मुद्दा नहीं है, लेकिन इस पद्धति के लिए अच्छा है जो सरणी लेता है।
मटरू अलेक्जेंडर

6

स्विफ्ट 3 में काम किया, पूरे कोड को यहां पेस्ट किया

    //****Make sure the textview 'Selectable' = checked, and 'Editable = Unchecked'

import UIKit

class ViewController: UIViewController, UITextViewDelegate {

    @IBOutlet var theNewTextView: UITextView!
    override func viewDidLoad() {
        super.viewDidLoad()

        //****textview = Selectable = checked, and Editable = Unchecked

        theNewTextView.delegate = self

        let theString = NSMutableAttributedString(string: "Agree to Terms")
        let theRange = theString.mutableString.range(of: "Terms")

        theString.addAttribute(NSLinkAttributeName, value: "ContactUs://", range: theRange)

        let theAttribute = [NSForegroundColorAttributeName: UIColor.blue, NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue] as [String : Any]

        theNewTextView.linkTextAttributes = theAttribute

     theNewTextView.attributedText = theString             

theString.setAttributes(theAttribute, range: theRange)

    }

    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {

        if (URL.scheme?.hasPrefix("ContactUs://"))! {

            return false //interaction not allowed
        }

        //*** Set storyboard id same as VC name
        self.navigationController!.pushViewController((self.storyboard?.instantiateViewController(withIdentifier: "TheLastViewController"))! as UIViewController, animated: true)

        return true
    }

}

यह नया एपीआई है, बस स्विफ्ट 10 और ऊपर से अनुमति दें :(
t4nhpt

1
@ t4nhpt आपका मतलब है iOS 10 ;-)
इनकार करते हैं

6

यहाँ उदाहरण हाइपरलिंक UILabel के लिए कोड है: स्रोत: http://sickprogrammersarea.blogspot.in/2014/03/adding-links-to-uilabel.html

#import "ViewController.h"
#import "TTTAttributedLabel.h"

@interface ViewController ()
@end

@implementation ViewController
{
    UITextField *loc;
    TTTAttributedLabel *data;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(5, 20, 80, 25) ];
    [lbl setText:@"Text:"];
    [lbl setFont:[UIFont fontWithName:@"Verdana" size:16]];
    [lbl setTextColor:[UIColor grayColor]];
    loc=[[UITextField alloc] initWithFrame:CGRectMake(4, 20, 300, 30)];
    //loc.backgroundColor = [UIColor grayColor];
    loc.borderStyle=UITextBorderStyleRoundedRect;
    loc.clearButtonMode=UITextFieldViewModeWhileEditing;
    //[loc setText:@"Enter Location"];
    loc.clearsOnInsertion = YES;
    loc.leftView=lbl;
    loc.leftViewMode=UITextFieldViewModeAlways;
    [loc setDelegate:self];
    [self.view addSubview:loc];
    [loc setRightViewMode:UITextFieldViewModeAlways];
    CGRect frameimg = CGRectMake(110, 70, 70,30);
    UIButton *srchButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    srchButton.frame=frameimg;
    [srchButton setTitle:@"Go" forState:UIControlStateNormal];
    [srchButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    srchButton.backgroundColor=[UIColor clearColor];
    [srchButton addTarget:self action:@selector(go:) forControlEvents:UIControlEventTouchDown];
    [self.view addSubview:srchButton];
    data = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(5, 120,self.view.frame.size.width,200) ];
    [data setFont:[UIFont fontWithName:@"Verdana" size:16]];
    [data setTextColor:[UIColor blackColor]];
    data.numberOfLines=0;
    data.delegate = self;
    data.enabledTextCheckingTypes=NSTextCheckingTypeLink|NSTextCheckingTypePhoneNumber;
    [self.view addSubview:data];
}
- (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url
{
    NSString *val=[[NSString alloc]initWithFormat:@"%@",url];
    if ([[url scheme] hasPrefix:@"mailto"]) {
              NSLog(@" mail URL Selected : %@",url);
        MFMailComposeViewController *comp=[[MFMailComposeViewController alloc]init];
        [comp setMailComposeDelegate:self];
        if([MFMailComposeViewController canSendMail])
        {
            NSString *recp=[[val substringToIndex:[val length]] substringFromIndex:7];
            NSLog(@"Recept : %@",recp);
            [comp setToRecipients:[NSArray arrayWithObjects:recp, nil]];
            [comp setSubject:@"From my app"];
            [comp setMessageBody:@"Hello bro" isHTML:NO];
            [comp setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
            [self presentViewController:comp animated:YES completion:nil];
        }
    }
    else{
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:val]];
    }
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
    if(error)
    {
        UIAlertView *alrt=[[UIAlertView alloc]initWithTitle:@"Erorr" message:@"Some error occureed" delegate:nil cancelButtonTitle:@"" otherButtonTitles:nil, nil];
        [alrt show];
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    else{
        [self dismissViewControllerAnimated:YES completion:nil];
    }
}

- (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithPhoneNumber:(NSString *)phoneNumber
{
    NSLog(@"Phone Number Selected : %@",phoneNumber);
    UIDevice *device = [UIDevice currentDevice];
    if ([[device model] isEqualToString:@"iPhone"] ) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tel:%@",phoneNumber]]];
    } else {
        UIAlertView *Notpermitted=[[UIAlertView alloc] initWithTitle:@"Alert" message:@"Your device doesn't support this feature." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [Notpermitted show];
    }
}
-(void)go:(id)sender
{
    [data setText:loc.text];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"Reached");
    [loc resignFirstResponder];
}

6

यहाँ NAlexN के उत्तर का एक स्विफ्ट संस्करण है।

class TapabbleLabel: UILabel {

let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
var textStorage = NSTextStorage() {
    didSet {
        textStorage.addLayoutManager(layoutManager)
    }
}

var onCharacterTapped: ((label: UILabel, characterIndex: Int) -> Void)?

let tapGesture = UITapGestureRecognizer()

override var attributedText: NSAttributedString? {
    didSet {
        if let attributedText = attributedText {
            textStorage = NSTextStorage(attributedString: attributedText)
        } else {
            textStorage = NSTextStorage()
        }
    }
}

override var lineBreakMode: NSLineBreakMode {
    didSet {
        textContainer.lineBreakMode = lineBreakMode
    }
}

override var numberOfLines: Int {
    didSet {
        textContainer.maximumNumberOfLines = numberOfLines
    }
}

/**
 Creates a new view with the passed coder.

 :param: aDecoder The a decoder

 :returns: the created new view.
 */
required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setUp()
}

/**
 Creates a new view with the passed frame.

 :param: frame The frame

 :returns: the created new view.
 */
override init(frame: CGRect) {
    super.init(frame: frame)
    setUp()
}

/**
 Sets up the view.
 */
func setUp() {
    userInteractionEnabled = true
    layoutManager.addTextContainer(textContainer)
    textContainer.lineFragmentPadding = 0
    textContainer.lineBreakMode = lineBreakMode
    textContainer.maximumNumberOfLines = numberOfLines
    tapGesture.addTarget(self, action: #selector(TapabbleLabel.labelTapped(_:)))
    addGestureRecognizer(tapGesture)
}

override func layoutSubviews() {
    super.layoutSubviews()
    textContainer.size = bounds.size
}

func labelTapped(gesture: UITapGestureRecognizer) {
    guard gesture.state == .Ended else {
        return
    }

    let locationOfTouch = gesture.locationInView(gesture.view)
    let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)
    let textContainerOffset = CGPoint(x: (bounds.width - textBoundingBox.width) / 2 - textBoundingBox.minX,
                                      y: (bounds.height - textBoundingBox.height) / 2 - textBoundingBox.minY)        
    let locationOfTouchInTextContainer = CGPoint(x: locationOfTouch.x - textContainerOffset.x,
                                                 y: locationOfTouch.y - textContainerOffset.y)
    let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer,
                                                                inTextContainer: textContainer,
                                                                fractionOfDistanceBetweenInsertionPoints: nil)

    onCharacterTapped?(label: self, characterIndex: indexOfCharacter)
}
}

फिर आप अपनी viewDidLoadपद्धति के अंदर उस वर्ग का एक उदाहरण इस प्रकार बना सकते हैं :

let label = TapabbleLabel()
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[view]-|",
                                               options: [], metrics: nil, views: ["view" : label]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[view]-|",
                                               options: [], metrics: nil, views: ["view" : label]))

let attributedString = NSMutableAttributedString(string: "String with a link", attributes: nil)
let linkRange = NSMakeRange(14, 4); // for the word "link" in the string above

let linkAttributes: [String : AnyObject] = [
    NSForegroundColorAttributeName : UIColor.blueColor(), NSUnderlineStyleAttributeName : NSUnderlineStyle.StyleSingle.rawValue,
    NSLinkAttributeName: "http://www.apple.com"]
attributedString.setAttributes(linkAttributes, range:linkRange)

label.attributedText = attributedString

label.onCharacterTapped = { label, characterIndex in
    if let attribute = label.attributedText?.attribute(NSLinkAttributeName, atIndex: characterIndex, effectiveRange: nil) as? String,
        let url = NSURL(string: attribute) {
        UIApplication.sharedApplication().openURL(url)
    }
}

जब कोई चरित्र टैप किया जाता है, तो कस्टम विशेषता का उपयोग करना बेहतर होता है। अब, यह है NSLinkAttributeName, लेकिन कुछ भी हो सकता है और आप उस मूल्य का उपयोग एक यूआरएल खोलने के अलावा अन्य चीजों को करने के लिए कर सकते हैं, आप कोई भी कस्टम क्रिया कर सकते हैं।


यह भी खूब रही! मैंने TapGestureRecognizer को LongPressRecognizer के साथ बदल दिया और यह टेबलव्यू स्क्रॉलिंग को तोड़ देता है। टेबलव्यू स्क्रॉलिंग को तोड़ने से जेस्चरक्राइज़र को कैसे रोका जाए, इसके लिए कोई सुझाव? धन्यवाद!!!
ल्यूसियस डेजियर

आप का उपयोग कर सकते हैं। पहचानें। डेवलपर डेवलपर
documentation

4

मेरे पास इससे निपटने के लिए एक कठिन समय था ... UILabel ने इसके लिए जिम्मेदार टेक्स्ट पर लिंक दिया ... यह सिर्फ एक सिरदर्द है इसलिए मैंने ZSWTappableLabel का उपयोग करके समाप्त किया ।


धन्यवाद। यह वास्तव में मेरे मामले में काम करता है। यह ईमेल आईडी, फोन नंबर और लिंक का पता लगाएगा।
हिलज

4

जैसा कि पहले उत्तर में बताया गया है कि UITextView लिंक पर टच को संभालने में सक्षम है। इसे पाठ के अन्य भागों को लिंक के रूप में बनाकर आसानी से बढ़ाया जा सकता है। AttributedTextView लाइब्रेरी एक UITextView उपवर्ग है जो इनको संभालना बहुत आसान बनाता है। अधिक जानकारी के लिए देखें: https://github.com/evermeer/AttributedTextView

आप पाठ के किसी भी भाग को इस तरह से इंटरैक्ट कर सकते हैं (जहाँ textView1 एक UITextView IBOutlet है):

textView1.attributer =
    "1. ".red
    .append("This is the first test. ").green
    .append("Click on ").black
    .append("evict.nl").makeInteract { _ in
        UIApplication.shared.open(URL(string: "http://evict.nl")!, options: [:], completionHandler: { completed in })
    }.underline
    .append(" for testing links. ").black
    .append("Next test").underline.makeInteract { _ in
        print("NEXT")
    }
    .all.font(UIFont(name: "SourceSansPro-Regular", size: 16))
    .setLinkColor(UIColor.purple) 

और हैशटैग और उल्लेख को संभालने के लिए आप इस तरह कोड का उपयोग कर सकते हैं:

textView1.attributer = "@test: What #hashtags do we have in @evermeer #AtributedTextView library"
    .matchHashtags.underline
    .matchMentions
    .makeInteract { link in
        UIApplication.shared.open(URL(string: "https://twitter.com\(link.replacingOccurrences(of: "@", with: ""))")!, options: [:], completionHandler: { completed in })
    }

3

मैं मल्टी-लाइन UILabel को संभालने के लिए @ samwize का जवाब दे रहा हूं और UIButton के लिए उपयोग करने पर एक उदाहरण देता हूं

extension UITapGestureRecognizer {

    func didTapAttributedTextInButton(button: UIButton, inRange targetRange: NSRange) -> Bool {
        guard let label = button.titleLabel else { return false }
        return didTapAttributedTextInLabel(label, inRange: targetRange)
    }

    func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
        // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: CGSize.zero)
        let textStorage = NSTextStorage(attributedString: label.attributedText!)

        // Configure layoutManager and textStorage
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)

        // Configure textContainer
        textContainer.lineFragmentPadding = 0.0
        textContainer.lineBreakMode = label.lineBreakMode
        textContainer.maximumNumberOfLines = label.numberOfLines
        let labelSize = label.bounds.size
        textContainer.size = labelSize

        // Find the tapped character location and compare it to the specified range
        let locationOfTouchInLabel = self.locationInView(label)
        let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)
        let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                                              (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
        let locationOfTouchInTextContainer = CGPointMake((locationOfTouchInLabel.x - textContainerOffset.x),
                                                         0 );
        // Adjust for multiple lines of text
        let lineModifier = Int(ceil(locationOfTouchInLabel.y / label.font.lineHeight)) - 1
        let rightMostFirstLinePoint = CGPointMake(labelSize.width, 0)
        let charsPerLine = layoutManager.characterIndexForPoint(rightMostFirstLinePoint, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

        let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        let adjustedRange = indexOfCharacter + (lineModifier * charsPerLine)

        return NSLocationInRange(adjustedRange, targetRange)
    }

}

मैंने आपके समाधान के लिए एक बहु लाइन UILabel की कोशिश की, और यह वास्तव में मेरे लिए काम नहीं करता है। स्पर्श हमेशा मेरे UILabel की अंतिम पंक्ति में पंजीकृत होता है।
क्रिश्चियन शॉबर

1
@ChristianSchober आपके पास कस्टम फोंट या लाइन हाइट है?
टाइमब्रडर

वास्तव में नहीं, हम फ़ॉन्ट का उपयोग करते हैं HelveticaNeue और मानक ऊंचाइयों
क्रिश्चियन Schober

1
जब लेबल के दाहिने किनारे पर लाइन ब्रेक न हो तो काम न करें
zgjie

मेरे पास डिफ़ॉल्ट फोंट हैं लेकिन लाइन रिक्ति और काम नहीं किया, कोई विचार?
जोसेफ एस्ट्राहन

3

मैं इस संस्करण का पालन करता हूं,

स्विफ्ट 4:

import Foundation

class AELinkedClickableUILabel: UILabel {

    typealias YourCompletion = () -> Void

    var linkedRange: NSRange!
    var completion: YourCompletion?

    @objc func linkClicked(sender: UITapGestureRecognizer){

        if let completionBlock = completion {

            let textView = UITextView(frame: self.frame)
            textView.text = self.text
            textView.attributedText = self.attributedText
            let index = textView.layoutManager.characterIndex(for: sender.location(in: self),
                                                              in: textView.textContainer,
                                                              fractionOfDistanceBetweenInsertionPoints: nil)

            if linkedRange.lowerBound <= index && linkedRange.upperBound >= index {

                completionBlock()
            }
        }
    }

/**
 *  This method will be used to set an attributed text specifying the linked text with a
 *  handler when the link is clicked
 */
    public func setLinkedTextWithHandler(text:String, link: String, handler: @escaping ()->()) -> Bool {

        let attributextText = NSMutableAttributedString(string: text)
        let foundRange = attributextText.mutableString.range(of: link)

        if foundRange.location != NSNotFound {
            self.linkedRange = foundRange
            self.completion = handler
            attributextText.addAttribute(NSAttributedStringKey.link, value: text, range: foundRange)
            self.isUserInteractionEnabled = true
            self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(linkClicked(sender:))))
            return true
        }
        return false
    }
}

कॉल उदाहरण:

button.setLinkedTextWithHandler(text: "This website (stackoverflow.com) is awesome", link: "stackoverflow.com") 
{
    // show popup or open to link
}

3

मुझे एक अन्य समाधान मिला:

मुझे HTML पाठ में लिंक का पता लगाने का एक तरीका मिल गया है जो आपको उस इंटरनेट से मिलता है जिसे आप इसे nsattributeString में बदल देते हैं:

func htmlAttributedString(fontSize: CGFloat = 17.0) -> NSAttributedString? {
            let fontName = UIFont.systemFont(ofSize: fontSize).fontName
            let string = self.appending(String(format: "<style>body{font-family: '%@'; font-size:%fpx;}</style>", fontName, fontSize))
            guard let data = string.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }

            guard let html = try? NSMutableAttributedString (
                data: data,
                options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html],
                documentAttributes: nil) else { return nil }
            return html
        }

मेरी विधि आपको उन्हें निर्दिष्ट किए बिना हाइपरलिंक का पता लगाने की अनुमति देती है।

  • सबसे पहले आप टेपेस्टेरोकेरोगिग्नर का एक एक्सटेंशन बनाते हैं:

    extension UITapGestureRecognizer {
    func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
        guard let attrString = label.attributedText else {
            return false
        }
    
        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: .zero)
        let textStorage = NSTextStorage(attributedString: attrString)
    
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)
    
        textContainer.lineFragmentPadding = 0
        textContainer.lineBreakMode = label.lineBreakMode
        textContainer.maximumNumberOfLines = label.numberOfLines
        let labelSize = label.bounds.size
        textContainer.size = labelSize
    
        let locationOfTouchInLabel = self.location(in: label)
        let textBoundingBox = layoutManager.usedRect(for: textContainer)
        let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y)
        let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)
        let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        return NSLocationInRange(indexOfCharacter, targetRange)
    }

    }

तब आप कंट्रोलर में आपने url की एक लिस्ट बनाई थी और सभी लिंक्स और रेंज को स्टोर करने के लिए रेंज की थी जिसमें टेक्स्ट की विशेषता थी:

var listurl : [String] = []
    var listURLRange : [NSRange] = []

URL और URL का पता लगाने के लिए जिसका आप उपयोग कर सकते हैं:

    fun findLinksAndRange(attributeString : NSAttributeString){
        notification.enumerateAttribute(NSAttributedStringKey.link , in: NSMakeRange(0, notification.length), options: [.longestEffectiveRangeNotRequired]) { value, range, isStop in
                    if let value = value {
                        print("\(value) found at \(range.location)")
                        let stringValue = "\(value)"
                        listurl.append(stringValue)
                        listURLRange.append(range)
                    }
                }

            westlandNotifcationLabel.addGestureRecognizer(UITapGestureRecognizer(target : self, action: #selector(handleTapOnLabel(_:))))

    }

तब आप हैंडल टैप को कार्यान्वित कर रहे हैं:

@objc func handleTapOnLabel(_ recognizer: UITapGestureRecognizer) {
        for index in 0..<listURLRange.count{
            if recognizer.didTapAttributedTextInLabel(label: westlandNotifcationLabel, inRange: listURLRange[index]) {
                goToWebsite(url : listurl[index])
            }
        }
    }

    func goToWebsite(url : String){
        if let websiteUrl = URL(string: url){
            if #available(iOS 10, *) {
                UIApplication.shared.open(websiteUrl, options: [:],
                                          completionHandler: {
                                            (success) in
                                            print("Open \(websiteUrl): \(success)")
                })
            } else {
                let success = UIApplication.shared.openURL(websiteUrl)
                print("Open \(websiteUrl): \(success)")
            }
        }
    }

और अब हम चले!

मुझे उम्मीद है कि यह समाधान आपकी मदद करेगा जैसे यह मेरी मदद करता है।


2

पूरी तरह से कस्टम लिंक के लिए, आपको UIWebView का उपयोग करना होगा - आप कॉल को बीच में रोक सकते हैं, ताकि आप लिंक दबाए जाने के बजाय अपने ऐप के किसी अन्य हिस्से में जा सकें।


3
आवंटन करते समय UIWebViews इतनी तेजी से नहीं होते हैं, इसलिए फैंसीलेबेल या टीटीटीएटीट्यूटेडलेबेल जैसे यूआईबेल या यूआईटैक्सफ़िल्ड लाइब्रेरी का उपयोग करना बेहतर है यदि आप इसके साथ दूर हो सकते हैं। यह विशेष रूप से उचित है अगर आपको टेबलव्यू सेल्स आदि में शामिल क्लिक करने योग्य लिंक की आवश्यकता होती है
Niall Mccormack

2

यहां एक ड्रॉप-इन ऑब्जेक्टिव-सी श्रेणी है जो मौजूदा UILabel.attributedTextस्ट्रिंग्स में क्लिक करने योग्य लिंक को सक्षम करता है , जो मौजूदा NSLinkAttributeNameविशेषता का शोषण करता है।

@interface UILabel (GSBClickableLinks) <UIGestureRecognizerDelegate>
@property BOOL enableLinks;
@end

#import <objc/runtime.h>
static const void *INDEX;
static const void *TAP;

@implementation UILabel (GSBClickableLinks)

- (void)setEnableLinks:(BOOL)enableLinks
{
    UITapGestureRecognizer *tap = objc_getAssociatedObject(self, &TAP); // retreive tap
    if (enableLinks && !tap) { // add a gestureRegonzier to the UILabel to detect taps
        tap = [UITapGestureRecognizer.alloc initWithTarget:self action:@selector(openLink)];
        tap.delegate = self;
        [self addGestureRecognizer:tap];
        objc_setAssociatedObject(self, &TAP, tap, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // save tap
    }
    self.userInteractionEnabled = enableLinks; // note - when false UILAbel wont receive taps, hence disable links
}

- (BOOL)enableLinks
{
    return (BOOL)objc_getAssociatedObject(self, &TAP); // ie tap != nil
}

// First check whether user tapped on a link within the attributedText of the label.
// If so, then the our label's gestureRecogizer will subsequently fire, and open the corresponding NSLinkAttributeName.
// If not, then the tap will get passed along, eg to the enclosing UITableViewCell...
// Note: save which character in the attributedText was clicked so that we dont have to redo everything again in openLink.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer != objc_getAssociatedObject(self, &TAP)) return YES; // dont block other gestures (eg swipe)

    // Re-layout the attributedText to find out what was tapped
    NSTextContainer *textContainer = [NSTextContainer.alloc initWithSize:self.frame.size];
    textContainer.lineFragmentPadding = 0;
    textContainer.maximumNumberOfLines = self.numberOfLines;
    textContainer.lineBreakMode = self.lineBreakMode;
    NSLayoutManager *layoutManager = NSLayoutManager.new;
    [layoutManager addTextContainer:textContainer];
    NSTextStorage *textStorage = [NSTextStorage.alloc initWithAttributedString:self.attributedText];
    [textStorage addLayoutManager:layoutManager];

    NSUInteger index = [layoutManager characterIndexForPoint:[gestureRecognizer locationInView:self]
                                             inTextContainer:textContainer
                    fractionOfDistanceBetweenInsertionPoints:NULL];
    objc_setAssociatedObject(self, &INDEX, @(index), OBJC_ASSOCIATION_RETAIN_NONATOMIC); // save index

    return (BOOL)[self.attributedText attribute:NSLinkAttributeName atIndex:index effectiveRange:NULL]; // tapped on part of a link?
}

- (void)openLink
{
    NSUInteger index = [objc_getAssociatedObject(self, &INDEX) unsignedIntegerValue]; // retrieve index
    NSURL *url = [self.attributedText attribute:NSLinkAttributeName atIndex:index effectiveRange:NULL];
    if (url && [UIApplication.sharedApplication canOpenURL:url]) [UIApplication.sharedApplication openURL:url];
}

@end 

यह एक UILabel उपवर्ग (अर्थात कोई भी objc_getAssociatedObject मेस में से कोई भी) के माध्यम से किया जाने वाला क्लीनर होगा, लेकिन अगर आप मेरे जैसे हैं तो आप अनावश्यक (3rd पार्टी) उपवर्ग को केवल मौजूदा UIKit कक्षाओं में कुछ अतिरिक्त फ़ंक्शन जोड़ने से बचने के लिए पसंद करते हैं। इसके अलावा, यह सौंदर्य है कि यह किसी भी मौजूदा UILabel जैसे क्लिक-लिंक जोड़ता है , जैसे मौजूदा UITableViewCells!

मैंने इसे NSLinkAttributeNameNSAttributedString में पहले से उपलब्ध मौजूदा विशेषता सामग्री का उपयोग करके यथासंभव न्यूनतम इनवेसिव बनाने का प्रयास किया है । तो यह एक सरल है:

NSURL *myURL = [NSURL URLWithString:@"http://www.google.com"];
NSMutableAttributedString *myString = [NSMutableAttributedString.alloc initWithString:@"This string has a clickable link: "];
[myString appendAttributedString:[NSAttributedString.alloc initWithString:@"click here" attributes:@{NSLinkAttributeName:myURL}]];
...
myLabel.attributedText = myString;
myLabel.enableLinks = YES; // yes, that's all! :-)

मूल रूप से, यह UIGestureRecognizerआपके UILabel में एक जोड़कर काम करता है । इसमें कड़ी मेहनत की जाती है gestureRecognizerShouldBegin:, जो कि यह पता लगाने के लिए कि किस चरित्र पर टैप किया गया था, इसके लिए एट्रिब्यूटेड स्ट्रिंग को री-लेआउट करता है। यदि यह वर्ण NSLinkAttributeName का हिस्सा था, तो जेस्चर रिकॉग्निज़र बाद में आग लगाएगा, संबंधित URL (NSLinkAttributeName मान से) को पुनः प्राप्त करें, और सामान्य [UIApplication.sharedApplication openURL:url]प्रक्रिया के अनुसार लिंक खोलें ।

नोट - gestureRecognizerShouldBegin:यदि आप लेबल में किसी लिंक पर टैप करने के लिए नहीं होते हैं, तो यह सब करके , ईवेंट को पास कर दिया जाता है। इसलिए, उदाहरण के लिए, आपका UITableViewCell लिंक पर टैप कैप्चर करेगा, लेकिन अन्यथा सामान्य रूप से व्यवहार करें (सेल का चयन करें, अचयनित करें, स्क्रॉल करें ...)।

मैंने इसे यहाँ GitHub रिपॉजिटरी में रखा है । काई बरगद्ट के एसओ के यहाँ पोस्टिंग से ।


1

निम्न .h और .m फ़ाइलों के साथ वर्ग बनाएँ। .M फ़ाइल में निम्न फ़ंक्शन है

 - (void)linkAtPoint:(CGPoint)location

इस फ़ंक्शन के अंदर हम उन सबस्ट्रिंग्स की सीमाओं की जांच करेंगे जिनके लिए हमें कार्रवाई करने की आवश्यकता है। अपनी सीमाओं का उपयोग करने के लिए अपने स्वयं के तर्क का उपयोग करें।

और निम्नलिखित उपवर्ग का उपयोग है

TaggedLabel *label = [[TaggedLabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
[self.view addSubview:label];
label.numberOfLines = 0;
NSMutableAttributedString *attributtedString = [[NSMutableAttributedString alloc] initWithString : @"My name is @jjpp" attributes : @{ NSFontAttributeName : [UIFont systemFontOfSize:10],}];                                                                                                                                                                              
//Do not forget to add the font attribute.. else it wont work.. it is very important
[attributtedString addAttribute:NSForegroundColorAttributeName
                        value:[UIColor redColor]
                        range:NSMakeRange(11, 5)];//you can give this range inside the .m function mentioned above

निम्नलिखित .h फ़ाइल है

#import <UIKit/UIKit.h>

@interface TaggedLabel : UILabel<NSLayoutManagerDelegate>

@property(nonatomic, strong)NSLayoutManager *layoutManager;
@property(nonatomic, strong)NSTextContainer *textContainer;
@property(nonatomic, strong)NSTextStorage *textStorage;
@property(nonatomic, strong)NSArray *tagsArray;
@property(readwrite, copy) tagTapped nameTagTapped;

@end   

निम्नलिखित .m फ़ाइल है

#import "TaggedLabel.h"
@implementation TaggedLabel

- (id)initWithFrame:(CGRect)frame
{
 self = [super initWithFrame:frame];
 if (self)
 {
  self.userInteractionEnabled = YES;
 }
return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
 self = [super initWithCoder:aDecoder];
if (self)
{
 self.userInteractionEnabled = YES;
}
return self;
}

- (void)setupTextSystem
{
 _layoutManager = [[NSLayoutManager alloc] init];
 _textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
 _textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedText];
 // Configure layoutManager and textStorage
 [_layoutManager addTextContainer:_textContainer];
 [_textStorage addLayoutManager:_layoutManager];
 // Configure textContainer
 _textContainer.lineFragmentPadding = 0.0;
 _textContainer.lineBreakMode = NSLineBreakByWordWrapping;
 _textContainer.maximumNumberOfLines = 0;
 self.userInteractionEnabled = YES;
 self.textContainer.size = self.bounds.size;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
 if (!_layoutManager)
 {
  [self setupTextSystem];
 }
 // Get the info for the touched link if there is one
 CGPoint touchLocation = [[touches anyObject] locationInView:self];
 [self linkAtPoint:touchLocation];
}

- (void)linkAtPoint:(CGPoint)location
{
 // Do nothing if we have no text
 if (_textStorage.string.length == 0)
 {
  return;
 }
 // Work out the offset of the text in the view
 CGPoint textOffset = [self calcGlyphsPositionInView];
 // Get the touch location and use text offset to convert to text cotainer coords
 location.x -= textOffset.x;
 location.y -= textOffset.y;
 NSUInteger touchedChar = [_layoutManager glyphIndexForPoint:location inTextContainer:_textContainer];
 // If the touch is in white space after the last glyph on the line we don't
 // count it as a hit on the text
 NSRange lineRange;
 CGRect lineRect = [_layoutManager lineFragmentUsedRectForGlyphAtIndex:touchedChar effectiveRange:&lineRange];
 if (CGRectContainsPoint(lineRect, location) == NO)
 {
  return;
 }
 // Find the word that was touched and call the detection block
    NSRange range = NSMakeRange(11, 5);//for this example i'm hardcoding the range here. In a real scenario it should be iterated through an array for checking all the ranges
    if ((touchedChar >= range.location) && touchedChar < (range.location + range.length))
    {
     NSLog(@"range-->>%@",self.tagsArray[i][@"range"]);
    }
}

- (CGPoint)calcGlyphsPositionInView
{
 CGPoint textOffset = CGPointZero;
 CGRect textBounds = [_layoutManager usedRectForTextContainer:_textContainer];
 textBounds.size.width = ceil(textBounds.size.width);
 textBounds.size.height = ceil(textBounds.size.height);

 if (textBounds.size.height < self.bounds.size.height)
 {
  CGFloat paddingHeight = (self.bounds.size.height - textBounds.size.height) / 2.0;
  textOffset.y = paddingHeight;
 }

 if (textBounds.size.width < self.bounds.size.width)
 {
  CGFloat paddingHeight = (self.bounds.size.width - textBounds.size.width) / 2.0;
  textOffset.x = paddingHeight;
 }
 return textOffset;
 }

@end

1

मैं दृढ़ता से एक पुस्तकालय का उपयोग करने की सलाह दूंगा जो स्वचालित रूप से पाठ में URL का पता लगाता है और उन्हें लिंक में परिवर्तित करता है। प्रयत्न:

दोनों एमआईटी लाइसेंस के तहत हैं।


आप पिछले उत्तरों की नकल कर रहे हैं।
C --ur

1

चार्ल्स गैंबल के जवाब के आधार पर, यह जो मैंने इस्तेमाल किया (मैंने कुछ पंक्तियों को हटा दिया जो मुझे भ्रमित करते हैं और मुझे गलत अनुक्रमित करते हैं):

- (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange TapGesture:(UIGestureRecognizer*) gesture{
    NSParameterAssert(label != nil);

    // create instances of NSLayoutManager, NSTextContainer and NSTextStorage
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText];

    // configure layoutManager and textStorage
    [textStorage addLayoutManager:layoutManager];

    // configure textContainer for the label
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(label.frame.size.width, label.frame.size.height)];

    textContainer.lineFragmentPadding = 0.0;
    textContainer.lineBreakMode = label.lineBreakMode;
    textContainer.maximumNumberOfLines = label.numberOfLines;

    // find the tapped character location and compare it to the specified range
    CGPoint locationOfTouchInLabel = [gesture locationInView:label];
    [layoutManager addTextContainer:textContainer]; //(move here, not sure it that matter that calling this line after textContainer is set

    NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInLabel
                                                           inTextContainer:textContainer
                                  fractionOfDistanceBetweenInsertionPoints:nil];
    if (NSLocationInRange(indexOfCharacter, targetRange)) {
        return YES;
    } else {
        return NO;
    }
}

1

एक श्रेणी के रूप में ड्रॉप-इन समाधान UILabel(यह आपके द्वारा UILabelकुछ NSLinkAttributeNameविशेषताओं के साथ एक जिम्मेदार स्ट्रिंग का उपयोग करता है) मानता है :

@implementation UILabel (Support)

- (BOOL)openTappedLinkAtLocation:(CGPoint)location {
  CGSize labelSize = self.bounds.size;

  NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
  textContainer.lineFragmentPadding = 0.0;
  textContainer.lineBreakMode = self.lineBreakMode;
  textContainer.maximumNumberOfLines = self.numberOfLines;
  textContainer.size = labelSize;

  NSLayoutManager* layoutManager = [[NSLayoutManager alloc] init];
  [layoutManager addTextContainer:textContainer];

  NSTextStorage* textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedText];
  [textStorage addAttribute:NSFontAttributeName value:self.font range:NSMakeRange(0, textStorage.length)];
  [textStorage addLayoutManager:layoutManager];

  CGRect textBoundingBox = [layoutManager usedRectForTextContainer:textContainer];
  CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                                            (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
  CGPoint locationOfTouchInTextContainer = CGPointMake(location.x - textContainerOffset.x, location.y - textContainerOffset.y);
  NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInTextContainer inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:nullptr];
  if (indexOfCharacter >= 0) {
    NSURL* url = [textStorage attribute:NSLinkAttributeName atIndex:indexOfCharacter effectiveRange:nullptr];
    if (url) {
      [[UIApplication sharedApplication] openURL:url];
      return YES;
    }
  }
  return NO;
}

@end

1

यहां एक स्विफ्ट कार्यान्वयन है जो लगभग कम से कम संभव है जिसमें स्पर्श प्रतिक्रिया भी शामिल है। चेतावनियां:

  1. आपको अपने NSAttributedStrings में फ़ॉन्ट सेट करना होगा
  2. आप केवल NSAttributedStrings का उपयोग कर सकते हैं!
  3. आपको सुनिश्चित करना होगा कि आपके लिंक लपेट नहीं कर सकते हैं (गैर तोड़ने रिक्त स्थान का उपयोग करें: "\u{a0}")
  4. आप टेक्स्ट को सेट करने के बाद लाइनब्रीकोड या नंबरऑफलाइन को नहीं बदल सकते
  5. आप .linkकुंजियों के साथ विशेषताएँ जोड़कर लिंक बनाते हैं

public class LinkLabel: UILabel {
    private var storage: NSTextStorage?
    private let textContainer = NSTextContainer()
    private let layoutManager = NSLayoutManager()
    private var selectedBackgroundView = UIView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        textContainer.lineFragmentPadding = 0
        layoutManager.addTextContainer(textContainer)
        textContainer.layoutManager = layoutManager
        isUserInteractionEnabled = true
        selectedBackgroundView.isHidden = true
        selectedBackgroundView.backgroundColor = UIColor(white: 0, alpha: 0.3333)
        selectedBackgroundView.layer.cornerRadius = 4
        addSubview(selectedBackgroundView)
    }

    public required convenience init(coder: NSCoder) {
        self.init(frame: .zero)
    }

    public override func layoutSubviews() {
        super.layoutSubviews()
        textContainer.size = frame.size
    }

    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        setLink(for: touches)
    }

    public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
        setLink(for: touches)
    }

    private func setLink(for touches: Set<UITouch>) {
        if let pt = touches.first?.location(in: self), let (characterRange, _) = link(at: pt) {
            let glyphRange = layoutManager.glyphRange(forCharacterRange: characterRange, actualCharacterRange: nil)
            selectedBackgroundView.frame = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer).insetBy(dx: -3, dy: -3)
            selectedBackgroundView.isHidden = false
        } else {
            selectedBackgroundView.isHidden = true
        }
    }

    public override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesCancelled(touches, with: event)
        selectedBackgroundView.isHidden = true
    }

    public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        selectedBackgroundView.isHidden = true

        if let pt = touches.first?.location(in: self), let (_, url) = link(at: pt) {
            UIApplication.shared.open(url)
        }
    }

    private func link(at point: CGPoint) -> (NSRange, URL)? {
        let touchedGlyph = layoutManager.glyphIndex(for: point, in: textContainer)
        let touchedChar = layoutManager.characterIndexForGlyph(at: touchedGlyph)
        var range = NSRange()
        let attrs = attributedText!.attributes(at: touchedChar, effectiveRange: &range)
        if let urlstr = attrs[.link] as? String {
            return (range, URL(string: urlstr)!)
        } else {
            return nil
        }
    }

    public override var attributedText: NSAttributedString? {
        didSet {
            textContainer.maximumNumberOfLines = numberOfLines
            textContainer.lineBreakMode = lineBreakMode
            if let txt = attributedText {
                storage = NSTextStorage(attributedString: txt)
                storage!.addLayoutManager(layoutManager)
                layoutManager.textStorage = storage
                textContainer.size = frame.size
            }
        }
    }
}

1

यह सामान्य तरीका भी काम करता है!

func didTapAttributedTextInLabel(gesture: UITapGestureRecognizer, inRange targetRange: NSRange) -> Bool {

        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: CGSize.zero)
        guard let strAttributedText = self.attributedText else {
            return false
        }

        let textStorage = NSTextStorage(attributedString: strAttributedText)

        // Configure layoutManager and textStorage
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)

        // Configure textContainer
        textContainer.lineFragmentPadding = Constants.lineFragmentPadding
        textContainer.lineBreakMode = self.lineBreakMode
        textContainer.maximumNumberOfLines = self.numberOfLines
        let labelSize = self.bounds.size
        textContainer.size = CGSize(width: labelSize.width, height: CGFloat.greatestFiniteMagnitude)

        // Find the tapped character location and compare it to the specified range
        let locationOfTouchInLabel = gesture.location(in: self)

        let xCordLocationOfTouchInTextContainer = locationOfTouchInLabel.x
        let yCordLocationOfTouchInTextContainer = locationOfTouchInLabel.y
        let locOfTouch = CGPoint(x: xCordLocationOfTouchInTextContainer ,
                                 y: yCordLocationOfTouchInTextContainer)

        let indexOfCharacter = layoutManager.characterIndex(for: locOfTouch, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

        guard let strLabel = text else {
            return false
        }

        let charCountOfLabel = strLabel.count

        if indexOfCharacter < (charCountOfLabel - 1) {
            return NSLocationInRange(indexOfCharacter, targetRange)
        } else {
            return false
        }
    }

और आप विधि को कॉल कर सकते हैं

let text = yourLabel.text
let termsRange = (text as NSString).range(of: fullString)
if yourLabel.didTapAttributedTextInLabel(gesture: UITapGestureRecognizer, inRange: termsRange) {
            showCorrespondingViewController()
        }

अपने कोड का उपयोग करने के अपने उदाहरण में, कहां UITapGestureRecognizerसे आता है? क्या यह एक आउटलेट है? एक संपत्ति जिसे आपने सेटअप किया है?
मार्क मेयकेन्स

1

यहाँ मेरा जवाब @Luca Davanzo के उत्तर पर आधारित है , touchesBeganजो एक नल के इशारे के बजाय घटना को ओवरराइड करता है :

import UIKit

public protocol TapableLabelDelegate: NSObjectProtocol {
   func tapableLabel(_ label: TapableLabel, didTapUrl url: String, atRange range: NSRange)
}

public class TapableLabel: UILabel {

private var links: [String: NSRange] = [:]
private(set) var layoutManager = NSLayoutManager()
private(set) var textContainer = NSTextContainer(size: CGSize.zero)
private(set) var textStorage = NSTextStorage() {
    didSet {
        textStorage.addLayoutManager(layoutManager)
    }
}

public weak var delegate: TapableLabelDelegate?

public override var attributedText: NSAttributedString? {
    didSet {
        if let attributedText = attributedText {
            textStorage = NSTextStorage(attributedString: attributedText)
        } else {
            textStorage = NSTextStorage()
            links = [:]
        }
    }
}

public override var lineBreakMode: NSLineBreakMode {
    didSet {
        textContainer.lineBreakMode = lineBreakMode
    }
}

public override var numberOfLines: Int {
    didSet {
        textContainer.maximumNumberOfLines = numberOfLines
    }
}


public override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
}

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

public override func layoutSubviews() {
    super.layoutSubviews()
    textContainer.size = bounds.size
}


/// addLinks
///
/// - Parameters:
///   - text: text of link
///   - url: link url string
public func addLink(_ text: String, withURL url: String) {
    guard let theText = attributedText?.string as? NSString else {
        return
    }

    let range = theText.range(of: text)

    guard range.location !=  NSNotFound else {
        return
    }

    links[url] = range
}

private func setup() {
    isUserInteractionEnabled = true
    layoutManager.addTextContainer(textContainer)
    textContainer.lineFragmentPadding = 0
    textContainer.lineBreakMode = lineBreakMode
    textContainer.maximumNumberOfLines  = numberOfLines
}

public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let locationOfTouch = touches.first?.location(in: self) else {
        return
    }

    textContainer.size = bounds.size
    let indexOfCharacter = layoutManager.glyphIndex(for: locationOfTouch, in: textContainer)

    for (urlString, range) in links {
        if NSLocationInRange(indexOfCharacter, range), let url = URL(string: urlString) {
            delegate?.tapableLabel(self, didTapUrl: urlString, atRange: range)
        }
    }
}}

0

TAGS # स्विफ्ट 2.0

मैं प्रेरणा लेता हूं - उत्कृष्ट - @ NAlexN के जवाब और मैं खुद को UILabel के एक रैपर द्वारा लिखने का फैसला करता हूं।
मैंने भी TTTAttributedLabel की कोशिश की, लेकिन मैं इसे काम नहीं कर सकता।

आशा है कि आप इस कोड की सराहना कर सकते हैं, किसी भी सुझाव का स्वागत है!

import Foundation

@objc protocol TappableLabelDelegate {
    optional func tappableLabel(tabbableLabel: TappableLabel, didTapUrl: NSURL, atRange: NSRange)
}

/// Represent a label with attributed text inside.
/// We can add a correspondence between a range of the attributed string an a link (URL)
/// By default, link will be open on the external browser @see 'openLinkOnExternalBrowser'

class TappableLabel: UILabel {

    // MARK: - Public properties -

    var links: NSMutableDictionary = [:]
    var openLinkOnExternalBrowser = true
    var delegate: TappableLabelDelegate?

    // MARK: - Constructors -

    override func awakeFromNib() {
        super.awakeFromNib()
        self.enableInteraction()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.enableInteraction()
    }

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

    private func enableInteraction() {
        self.userInteractionEnabled = true
        self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: Selector("didTapOnLabel:")))
    }

    // MARK: - Public methods -

    /**
    Add correspondence between a range and a link.

    - parameter url:   url.
    - parameter range: range on which couple url.
    */
    func addLink(url url: String, atRange range: NSRange) {
        self.links[url] = range
    }

    // MARK: - Public properties -

    /**
    Action rised on user interaction on label.

    - parameter tapGesture: gesture.
    */
    func didTapOnLabel(tapGesture: UITapGestureRecognizer) {
        let labelSize = self.bounds.size;

        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: CGSizeZero)
        let textStorage = NSTextStorage(attributedString: self.attributedText!)

        // configure textContainer for the label
        textContainer.lineFragmentPadding = 0
        textContainer.lineBreakMode = self.lineBreakMode
        textContainer.maximumNumberOfLines = self.numberOfLines
        textContainer.size = labelSize;

        // configure layoutManager and textStorage
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)

        // find the tapped character location and compare it to the specified range
        let locationOfTouchInLabel = tapGesture.locationInView(self)

        let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)
        let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
            (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y)
        let locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
            locationOfTouchInLabel.y - textContainerOffset.y)
        let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer,
            inTextContainer:textContainer,
            fractionOfDistanceBetweenInsertionPoints: nil)

        for (url, value) in self.links {
            if let range = value as? NSRange {
                if NSLocationInRange(indexOfCharacter, range) {
                    let url = NSURL(string: url as! String)!
                    if self.openLinkOnExternalBrowser {
                        UIApplication.sharedApplication().openURL(url)
                    }
                    self.delegate?.tappableLabel?(self, didTapUrl: url, atRange: range)
                }
            }
        }
    }

}

मेरे मामले में, चरित्र के सूचकांक की गणना करने के लिए केवल एक पंक्ति पाठ के साथ एक अजीब परिणाम था, यह हमेशा वापसी का 0कारण locationOfTouchInTextContainer.x था जो नकारात्मक था। मैं let indexOfCharacter = layoutManager.glyphIndex(for: locationOfTouch, in: textContainer)इसके बजाय उपयोग करने की कोशिश करता हूं , और अच्छी तरह से काम करता हूं ।
HamGuy

0
- (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange{
    NSLayoutManager *layoutManager = [NSLayoutManager new];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText];

    [layoutManager addTextContainer:textContainer];
    [textStorage addLayoutManager:layoutManager];

    textContainer.lineFragmentPadding = 0.0;
    textContainer.lineBreakMode = label.lineBreakMode;
    textContainer.maximumNumberOfLines = label.numberOfLines;
    CGSize labelSize = label.bounds.size;
    textContainer.size = labelSize;

    CGPoint locationOfTouchInLabel = [self locationInView:label];
    CGRect textBoundingBox = [layoutManager usedRectForTextContainer:textContainer];
    CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                                              (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
    CGPoint locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
                                                         locationOfTouchInLabel.y - textContainerOffset.y);
    NSUInteger indexOfCharacter =[layoutManager characterIndexForPoint:locationOfTouchInTextContainer inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:nil];

    return NSLocationInRange(indexOfCharacter, targetRange);
}

0

स्विफ्ट 4.2 के लिए सही ढंग से कई लाइन को संभालने के लिए संशोधित @timbroder कोड

extension UITapGestureRecognizer {

    func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
        // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: CGSize.zero)
        let textStorage = NSTextStorage(attributedString: label.attributedText!)

        // Configure layoutManager and textStorage
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)

        // Configure textContainer
        textContainer.lineFragmentPadding = 0.0
        textContainer.lineBreakMode = label.lineBreakMode
        textContainer.maximumNumberOfLines = label.numberOfLines
        let labelSize = label.bounds.size
        textContainer.size = labelSize

        // Find the tapped character location and compare it to the specified range
        let locationOfTouchInLabel = self.location(in: label)
        let textBoundingBox = layoutManager.usedRect(for: textContainer)
        let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                                          y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
        let locationOfTouchInTextContainer = CGPoint(x: (locationOfTouchInLabel.x - textContainerOffset.x),
                                                     y: 0 );
        // Adjust for multiple lines of text
        let lineModifier = Int(ceil(locationOfTouchInLabel.y / label.font.lineHeight)) - 1
        let rightMostFirstLinePoint = CGPoint(x: labelSize.width, y: 0)
        let charsPerLine = layoutManager.characterIndex(for: rightMostFirstLinePoint, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

        let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        let adjustedRange = indexOfCharacter + (lineModifier * charsPerLine)
        var newTargetRange = targetRange
        if lineModifier > 0 {
            newTargetRange.location = targetRange.location+(lineModifier*Int(ceil(locationOfTouchInLabel.y)))
        }
        return NSLocationInRange(adjustedRange, newTargetRange)
    }
}

उइलाबेल कोड

let tapAction = UITapGestureRecognizer(target: self, action: #selector(self.tapLabel(gesture:)))

let quote = "For full details please see our privacy policy and cookie policy."
let attributedString = NSMutableAttributedString(string: quote)

let string1: String = "privacy policy", string2: String = "cookie policy"

// privacy policy
let rangeString1 = quote.range(of: string1)!
let indexString1: Int = quote.distance(from: quote.startIndex, to: rangeString1.lowerBound)
attributedString.addAttributes(
            [.font: <UIfont>,
             .foregroundColor: <UI Color>,
             .underlineStyle: 0, .underlineColor:UIColor.clear
        ], range: NSRange(location: indexString1, length: string1.count));

// cookie policy
let rangeString2 = quote.range(of: string2)!
let indexString2: Int = quote.distance(from: quote.startIndex, to: rangeString2.lowerBound )

attributedString.addAttributes(
            [.font: <UIfont>,
             .foregroundColor: <UI Color>,
             .underlineStyle: 0, .underlineColor:UIColor.clear
        ], range: NSRange(location: indexString2, length: string2.count));

let label = UILabel()
label.frame = CGRect(x: 20, y: 200, width: 375, height: 100)
label.isUserInteractionEnabled = true
label.addGestureRecognizer(tapAction)
label.attributedText = attributedString

टैप को पहचानने के लिए कोड

 @objc
  func tapLabel(gesture: UITapGestureRecognizer) {
     if gesture.didTapAttributedTextInLabel(label: <UILabel>, inRange: termsLabelRange {
            print("Terms of service")
     } else if gesture.didTapAttributedTextInLabel(label:<UILabel> inRange: privacyPolicyLabelRange) {
            print("Privacy policy")
     } else {
            print("Tapped none")
     }
    }

0

यह केदार के उत्तर पर आधारित एक Xamarin.iOS c # कार्यान्वयन है ।

ShouldInteractWithUrlओवरराइड के साथ MyClickableTextViewWithCustomUrlScheme कार्यान्वयन :

// Inspired from https://stackoverflow.com/a/44112932/15186
internal class MyClickableTextViewWithCustomUrlScheme : UITextView, IUITextViewDelegate
{
    public MyClickableTextViewWithCustomUrlScheme()
    {
        Initialize();
    }

    public MyClickableTextViewWithCustomUrlScheme(Foundation.NSCoder coder) : base(coder)
    {
        Initialize();
    }

    public MyClickableTextViewWithCustomUrlScheme(Foundation.NSObjectFlag t) : base(t)
    {
        Initialize();
    }

    public MyClickableTextViewWithCustomUrlScheme(IntPtr handle) : base(handle)
    {
        Initialize();
    }

    public MyClickableTextViewWithCustomUrlScheme(CoreGraphics.CGRect frame) : base(frame)
    {
        Initialize();
    }

    public MyClickableTextViewWithCustomUrlScheme(CoreGraphics.CGRect frame, NSTextContainer textContainer) : base(frame, textContainer)
    {
        Initialize();
    }

    void Initialize()
    {
        Delegate = this;
    }

    [Export("textView:shouldInteractWithURL:inRange:")]
    public new bool ShouldInteractWithUrl(UITextView textView, NSUrl URL, NSRange characterRange)
    {
        if (URL.Scheme.CompareTo(@"username") == 0)
        {
            // Launch the Activity
            return false;
        }
        // The system will handle the URL
        return base.ShouldInteractWithUrl(textView, URL, characterRange);
    }
}

परिवर्तित उद्देश्य-सी कोड c # बन जाता है:

MyClickableTextViewWithCustomUrlScheme uiHabitTile = new MyClickableTextViewWithCustomUrlScheme();
uiHabitTile.Selectable = true;
uiHabitTile.ScrollEnabled = false;
uiHabitTile.Editable = false;

// https://stackoverflow.com/a/34014655/15186
string wholeTitle = @"This is an example by marcelofabri";

NSMutableAttributedString attributedString = new NSMutableAttributedString(wholeTitle);
attributedString.AddAttribute(UIStringAttributeKey.Link,
   new NSString("username://marcelofabri"),
   attributedString.Value.RangeOfString(@"marcelofabri")
);
NSMutableDictionary<NSString, NSObject> linkAttributes = new NSMutableDictionary<NSString, NSObject>();
linkAttributes[UIStringAttributeKey.ForegroundColor] = UIColor.Green;
linkAttributes[UIStringAttributeKey.UnderlineColor] = UIColor.LightGray;
linkAttributes[UIStringAttributeKey.UnderlineStyle] = new NSNumber((short)NSUnderlineStyle.PatternSolid);

uiHabitTile.AttributedText = attributedString;

लिंक पर क्लिक करने में सक्षम होने के लिए संपादन योग्य = गलत और चयन योग्य = सेट करना सुनिश्चित करें।

इसके अलावा स्क्रॉलएक्टिव = सच टेक्स्टव्यू को इसकी ऊंचाई को सही ढंग से आकार देने की अनुमति देता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.