Làm cách nào tôi có thể tạo liên kết có thể nhấp trong NSAttributionString?


200

Thật đơn giản để làm cho các siêu liên kết có thể nhấp vào trong a UITextView. Bạn chỉ cần đặt hộp kiểm "phát hiện liên kết" trên chế độ xem trong IB và nó phát hiện các liên kết HTTP và biến chúng thành siêu liên kết.

Tuy nhiên, điều đó vẫn có nghĩa là những gì người dùng nhìn thấy là liên kết "thô". Các tệp RTF và HTML đều cho phép bạn thiết lập một chuỗi người dùng có thể đọc được với một liên kết "đằng sau" nó.

Thật dễ dàng để cài đặt văn bản được gán vào chế độ xem văn bản (hoặc một UILabelhoặc UITextField, đối với vấn đề đó.) Tuy nhiên, khi văn bản được gán đó bao gồm một liên kết, nó không thể nhấp được.

Có cách nào để làm cho văn bản người dùng có thể đọc được trong một UITextView, UILabelhoặc UITextFieldkhông?

Đánh dấu là khác nhau trên SO, nhưng đây là ý tưởng chung. Những gì tôi muốn là văn bản như thế này:

Hình thái này được tạo bằng Face Dancer , Bấm để xem trong cửa hàng ứng dụng.

Điều duy nhất tôi có thể nhận được là đây:

Hình thái này được tạo bằng Face Dancer, Nhấp vào http://example.com/facesancer để xem trong cửa hàng ứng dụng.


Hãy thử mẫu này .. IFTweetLabel Hy vọng nó sẽ giúp ..
Vidhyanand


Làm tốt công việc thổi qua 100K trong nháy mắt. Chào mừng đến với câu lạc bộ 100K. Cũng xứng đáng!
vacawama

@vacawama, đợi đã, chuyện đó xảy ra khi nào? Tôi đã ở mức ≈98k lần trước khi tôi nhìn! (Tôi nghe tin đồn rằng bạn nhận được một số swag SO khi là thành viên của câu lạc bộ 100k?)
Duncan C

Họ đã thay đổi upvote cho các câu hỏi từ +5 thành +10, vì vậy nếu bạn có 800 lượt upvote bạn sẽ net +4000 trong nháy mắt. Tôi vẫn đang chờ đợi trên 100 nghìn swag (vượt qua vào tháng Tư). Đôi điều về việc thay đổi nhà cung cấp swag ...
vacawama

Câu trả lời:


156

Sử dụng NSMutableAttributionString .

NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"Google"];
[str addAttribute: NSLinkAttributeName value: @"http://www.google.com" range: NSMakeRange(0, str.length)];
yourTextView.attributedText = str;

Chỉnh sửa :

Đây không phải là trực tiếp về câu hỏi mà chỉ để làm rõ UITextFieldUILabelkhông hỗ trợ mở URL. Nếu bạn muốn sử dụng UILabelvới các liên kết, bạn có thể kiểm tra TTTAttributionLabel .

Ngoài ra, bạn nên đặt dataDetectorTypesgiá trị của bạn UITextViewthành UIDataDetectorTypeLinkhoặc UIDataDetectorTypeAllđể mở URL khi nhấp vào. Hoặc bạn có thể sử dụng phương pháp đại biểu theo đề xuất trong các bình luận.


7
Yup, nó đang làm việc, chỉ cần đặt nó bên trong một phương pháp UITextView và ghi đè đại biểu: - (BOOL) TextView: (UITextView *) TextView shouldInteractWithURL: (NSURL *) url inRange: (NSRange) characterRange
Yunus Nedim Mehel

Điều này không hoạt động trong một UILabel - không có gì xảy ra khi bạn nhấn vào trường.
Jack BeNimble

7
@saboehnke bạn có nghĩa là gọi một phương thức khi nhấp vào liên kết? nếu vậy thực hiện phương thức ủy nhiệm, cung cấp một url giả làm thuộc tính và gọi phương thức của bạn trong- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
ujell

2
Tôi không biết làm thế nào nó hoạt động. Giá trị của thuộc tính nên là loại NSURL. ----[str addAttribute: NSLinkAttributeName value: [NSURL URLWithString:@"http://www.google.com"] range: NSMakeRange(0, str.length)];
Nirav Dangi

1
@NiravDangi từNSAttributedString.h UIKIT_EXTERN NSString * const NSLinkAttributeName NS_AVAILABLE(10_0, 7_0); // NSURL (preferred) or NSString
Ahmed Nawar

142

Tôi thấy điều này thực sự hữu ích nhưng tôi cần thực hiện nó ở một vài nơi vì vậy tôi đã kết thúc cách tiếp cận của mình trong một phần mở rộng đơn giản để NSMutableAttributedString:

Swift 3

extension NSMutableAttributedString {

    public func setAsLink(textToFind:String, linkURL:String) -> Bool {

        let foundRange = self.mutableString.range(of: textToFind)
        if foundRange.location != NSNotFound {
            self.addAttribute(.link, value: linkURL, range: foundRange)
            return true
        }
        return false
    }
}

Swift 2

import Foundation

extension NSMutableAttributedString {

   public func setAsLink(textToFind:String, linkURL:String) -> Bool {

       let foundRange = self.mutableString.rangeOfString(textToFind)
       if foundRange.location != NSNotFound {
           self.addAttribute(NSLinkAttributeName, value: linkURL, range: foundRange)
           return true
       }
       return false
   }
}

Ví dụ sử dụng:

let attributedString = NSMutableAttributedString(string:"I love stackoverflow!")
let linkWasSet = attributedString.setAsLink("stackoverflow", linkURL: "http://stackoverflow.com")

if linkWasSet {
    // adjust more attributedString properties
}

Mục tiêu-C

Tôi vừa đạt được một yêu cầu để làm điều tương tự trong một dự án Objective-C thuần túy, vì vậy đây là danh mục Objective-C.

@interface NSMutableAttributedString (SetAsLinkSupport)

- (BOOL)setAsLink:(NSString*)textToFind linkURL:(NSString*)linkURL;

@end


@implementation NSMutableAttributedString (SetAsLinkSupport)

- (BOOL)setAsLink:(NSString*)textToFind linkURL:(NSString*)linkURL {

     NSRange foundRange = [self.mutableString rangeOfString:textToFind];
     if (foundRange.location != NSNotFound) {
         [self addAttribute:NSLinkAttributeName value:linkURL range:foundRange];
         return YES;
     }
     return NO;
}

@end

Ví dụ sử dụng:

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:"I love stackoverflow!"];

BOOL linkWasSet = [attributedString setAsLink:@"stackoverflow" linkURL:@"http://stackoverflow.com"];

if (linkWasSet) {
    // adjust more attributedString properties
}

Đảm bảo rằng thuộc tính Hành vi của NSTextField được đặt là Có thể chọn. Thuộc tính hành vi Xcode NSTextField


Một ví dụ sử dụng / thực hiện nhanh chóng về điều này sẽ được đánh giá rất cao.
ioopl

3
@ioop. Tôi đã thêm một ví dụ rất nhỏ vào bài viết gốc ở trên, hy vọng điều đó có ích.
Karl Nosworthy

7
Điều này làm việc chính xác. Chỉ muốn nói rằng bạn cần làm cho UITextView của mình có thể chọn để cho phép liên kết có thể nhấp được
lujop

1
@felecia genet, trong cả hai triển khai Objective C và Swift, phương thức trả về kết quả boolean để cho biết nếu một trận đấu và tập kết quả đã diễn ra. Lỗi bạn thấy là do bạn không nắm bắt được kết quả đó - điều đó tốt. Bạn có thể nắm bắt kết quả đó bằng cách gán nó cho một biến cục bộ hoặc điều chỉnh phương thức để ngăn nó trả về giá trị boolean nếu điều đó phù hợp hơn với nhu cầu của bạn. Tôi hy vọng điều đó sẽ giúp?
Karl Nosworthy

1
Không có vấn đề gì @feleciagenet, tôi đã thêm việc lưu trữ và kiểm tra kết quả phương thức cho cả hai ví dụ Swift và ObjectiveC.
Karl Nosworthy

34

Tôi vừa tạo một lớp con của UILabel để đặc biệt giải quyết các trường hợp sử dụng như vậy. Bạn có thể thêm nhiều liên kết dễ dàng và xác định các trình xử lý khác nhau cho chúng. Nó cũng hỗ trợ làm nổi bật liên kết được nhấn khi bạn chạm xuống để phản hồi cảm ứng. Vui lòng tham khảo https://github.com/null09264/FRHyperLabel .

Trong trường hợp của bạn, mã có thể như thế này:

FRHyperLabel *label = [FRHyperLabel new];

NSString *string = @"This morph was generated with Face Dancer, Click to view in the app store.";
NSDictionary *attributes = @{NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]};

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

[label setLinkForSubstring:@"Face Dancer" withLinkHandler:^(FRHyperLabel *label, NSString *substring){
    [[UIApplication sharedApplication] openURL:aURL];
}];

Ảnh chụp màn hình mẫu (trình xử lý được đặt để bật cảnh báo thay vì mở url trong trường hợp này)

đối mặt


nếu giả sử văn bản của tôi giống như thế này Hình thái này được tạo bằng Face Dancer, chế độ xem Click to Face Dancer trong cửa hàng ứng dụng Face Dancer. Ở đây tôi đang có 3 Face Dancer, nó không hoạt động cho nó
MANCHIKANTI KRISHNAKISHORE

1
Trong trường hợp này, vui lòng sử dụng API - (void)setLinkForRange:(NSRange)range withLinkHandler:(void(^)(FRHyperLabel *label, NSRange selectedRange))handler; thay thế. Vui lòng tham khảo readme trong trang github.
Jinghan Wang

1
FRHyperLabel dường như không hoạt động nữa. Bên trong "characterIndexForPoint:", nó luôn trả về -1 (không tìm thấy).
John Pang

Không làm việc cho tôi cho nhãn multiline. Phát hiện các ký tự là sai. Chuỗi liên kết 15 ký tự chỉ có thể nhấp vào một số ký tự đầu tiên, các ký tự khác không làm gì cả
Accid Bright

27

Cải thiện nhỏ cho giải pháp của ujell: Nếu bạn sử dụng NSURL thay vì NSString, bạn có thể sử dụng bất kỳ URL nào (ví dụ: url tùy chỉnh)

NSURL *URL = [NSURL URLWithString: @"whatsapp://app"];
NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"start Whatsapp"];
[str addAttribute: NSLinkAttributeName value:URL range: NSMakeRange(0, str.length)];
yourTextField.attributedText = str;

Chúc vui vẻ!


21

Swift 4:

var string = "Google"
var attributedString = NSMutableAttributedString(string: string, attributes:[NSAttributedStringKey.link: URL(string: "http://www.google.com")!])

yourTextView.attributedText = attributedString

Swift 3,1:

var string = "Google"
var attributedString = NSMutableAttributedString(string: string, attributes:[NSLinkAttributeName: URL(string: "http://www.google.com")!])

yourTextView.attributedText = attributedString

Câu trả lời này hoạt động hoàn hảo như vốn có. Dường như không cần bất kỳ lớp con màu hoặc tùy chỉnh nào mà các câu trả lời khác sử dụng.
zeroimpl

19

Tôi cũng có một yêu cầu tương tự, ban đầu tôi đã sử dụng UILabel và sau đó tôi nhận ra rằng UITextView tốt hơn. Tôi đã tạo UITextView hoạt động giống như UILabel bằng cách vô hiệu hóa tương tác và cuộn và tạo một phương thức thể loại NSMutableAttributedStringđể đặt liên kết thành văn bản giống như những gì Karl đã làm (+1 cho điều đó) đây là phiên bản obj c của tôi

-(void)setTextAsLink:(NSString*) textToFind withLinkURL:(NSString*) url
{
    NSRange range = [self.mutableString rangeOfString:textToFind options:NSCaseInsensitiveSearch];

    if (range.location != NSNotFound) {

        [self addAttribute:NSLinkAttributeName value:url range:range];
        [self addAttribute:NSForegroundColorAttributeName value:[UIColor URLColor] range:range];
    }
}

bạn có thể sử dụng đại biểu dưới đây để xử lý hành động

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)url inRange:(NSRange)characterRange
{
    // do the task
    return YES;
}

1
Theo như tôi có thể nói cài đặt NSForegroundColorAttributeNametrong một phạm vi NSLinkAttributeNameáp dụng không hoạt động. Không có vấn đề gì, những linkTextAttributescủa UITextViewđược áp dụng để thay thế. Có NSForegroundColorAttributeNamelàm việc cho bạn?
Dima

Bạn có chắc là bạn cũng không thiết lập linkTextAttributesđiều tương tự? hoặc có lẽ tintColor? Bạn có thể làm cho 2 liên kết hiển thị trong các màu khác nhau trong cùng một văn bản không?
Dima

1
Dưới đây là một mã làm việc NSRange phạm vi = [self.text phạm viOfString: textToFind tùy chọn: NSCaseInsensitiveSearch]; if (Range.location! = NSNotFound) {NSMutableAttributionString * string = [[NSMutableAttributionString alloc] initWithString: self.text]; [chuỗi addAttribution: NSLinkAttributionName giá trị: phạm vi url: phạm vi]; [chuỗi addAttribution: NSForegroundColorAttributionName giá trị: [UIColor blueColor] phạm vi: phạm vi]; tự.text = @ ""; self.attributionText = chuỗi; }
Nosov Pavel

16

Sử dụng UITextView nó hỗ trợ Liên kết có thể nhấp. Tạo chuỗi được gán bằng mã sau

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks];

Sau đó đặt văn bản UITextView như sau

NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor redColor],

                                 NSUnderlineColorAttributeName: [UIColor blueColor],

                                 NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};

customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;

Đảm bảo rằng bạn bật hành vi "Có thể chọn" của UITextView trong XIB.


15
Tôi nghĩ rằng đây là giải pháp tốt nhất! Lưu ý về việc kích hoạt Selectablelà rất quan trọng!
LunaCodeGirl

Điều này không gạch chân liên kết cho tôi (iOS 7, 8). Tôi cần sử dụng NSUnderlineStyleAttributionName: [NSNumber numberWithInt: NSUnderlineStyleSingle]
prewett

1
làm cho nó có thể lựa chọn là thông tin quan trọng nhất và không trực quan!
Nicolas Massart

13

Trọng tâm của câu hỏi của tôi là tôi muốn có thể tạo các liên kết có thể nhấp trong chế độ xem văn bản / trường / nhãn mà không phải viết mã tùy chỉnh để thao tác văn bản và thêm liên kết. Tôi muốn nó được điều khiển dữ liệu.

Cuối cùng tôi đã tìm ra cách để làm điều đó. Vấn đề là IB không tôn trọng các liên kết nhúng.

Hơn nữa, phiên bản iOS NSAttributedStringkhông cho phép bạn khởi tạo một chuỗi được quy cho từ tệp RTF. Phiên bản OS X NSAttributedString không có trình khởi tạo lấy tệp RTF làm đầu vào.

NSAttributedString tuân thủ giao thức NSCoding, vì vậy bạn có thể chuyển đổi nó sang / từ NSData

Tôi đã tạo một công cụ dòng lệnh OS X lấy tệp RTF làm đầu vào và xuất ra một tệp có phần mở rộng .data có chứa NSData từ NSCoding. Sau đó tôi đặt tệp .data vào dự án của mình và thêm một vài dòng mã tải văn bản vào dạng xem. Mã trông như thế này (dự án này là trong Swift):

/*
If we can load a file called "Dates.data" from the bundle and convert it to an attributed string,
install it in the dates field. The contents contain clickable links with custom URLS to select
each date.
*/
if
  let datesPath = NSBundle.mainBundle().pathForResource("Dates", ofType: "data"),
  let datesString = NSKeyedUnarchiver.unarchiveObjectWithFile(datesPath) as? NSAttributedString
{
  datesField.attributedText = datesString
}

Đối với các ứng dụng sử dụng nhiều văn bản có định dạng, tôi tạo quy tắc xây dựng cho Xcode biết rằng tất cả các tệp .rtf trong một thư mục đã cho là nguồn và các tệp .data là đầu ra. Khi tôi làm điều đó, tôi chỉ cần thêm các tệp .rtf vào thư mục được chỉ định, (hoặc chỉnh sửa các tệp hiện có) và quy trình xây dựng chỉ ra rằng chúng mới / được cập nhật, chạy công cụ dòng lệnh và sao chép các tệp vào gói ứng dụng. Nó hoạt động rất đẹp.

Tôi đã viết một bài đăng blog liên kết đến một dự án mẫu (Swift) thể hiện kỹ thuật này. Bạn có thể thấy nó ở đây:

Tạo các URL có thể nhấp trong UITextField mở trong ứng dụng của bạn


11

Ví dụ Swift 3 để phát hiện các hành động trên vòi văn bản được quy cho

https://stackoverflow.com/a/44226491/5516830

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
}

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
}

Giống như khôn ngoan, bạn có thể thêm bất kỳ hành động nào bạn muốn với shouldInteractWith URLphương thức UITextFieldDelegate.

Chúc mừng !!


7

Câu trả lời nhanh là sử dụng UITextView thay vì UILabel. Bạn cần kích hoạt Selectablevà vô hiệu hóa Editable.

Sau đó vô hiệu hóa các chỉ số cuộn và bị trả lại.

Ảnh chụp màn hình

Ảnh chụp màn hình

Giải pháp của tôi sử dụng NSMutableAttributedStringtừ chuỗi htmlNSHTMLTextDocumentType

NSString *s = @"<p><a href='https://itunes.apple.com/us/app/xxxx/xxxx?mt=8'>https://itunes.apple.com/us/app/xxxx/xxxx?mt=8</a></p>";

NSMutableAttributedString *text = [[NSMutableAttributedString alloc]
                                           initWithData: [s dataUsingEncoding:NSUnicodeStringEncoding]
                                           options: @{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType }
                                           documentAttributes: nil
                                           error: nil
                                           ];

cell.content.attributedText = text;

Điều này. Tôi đã có thể đọc tệp RTF từ gói tài nguyên của mình, chuyển đổi nó thành NSAttributedString, đặt nó làm liên kết attributedTextcủa tôi UITextViewvà các siêu liên kết chỉ hoạt động! Sẽ có rất nhiều công việc để tìm phạm vi của mỗi siêu liên kết và thiết lập nó bằng các thuộc tính.
Nicolas Miari

6

Tôi đã viết một phương thức, thêm một liên kết (linkString) vào một chuỗi (fullString) với một url nhất định (urlString):

- (NSAttributedString *)linkedStringFromFullString:(NSString *)fullString withLinkString:(NSString *)linkString andUrlString:(NSString *)urlString
{
    NSRange range = [fullString rangeOfString:linkString options:NSLiteralSearch];
    NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:fullString];

    NSMutableParagraphStyle *paragraphStyle = NSMutableParagraphStyle.new;
    paragraphStyle.alignment = NSTextAlignmentCenter;
    NSDictionary *attributes = @{NSForegroundColorAttributeName:RGB(0x999999),
                                 NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:10],
                                 NSParagraphStyleAttributeName:paragraphStyle};
    [str addAttributes:attributes range:NSMakeRange(0, [str length])];
    [str addAttribute: NSLinkAttributeName value:urlString range:range];

    return str;
}

Bạn nên gọi nó như thế này:

NSString *fullString = @"A man who bought the Google.com domain name for $12 and owned it for about a minute has been rewarded by Google for uncovering the flaw.";
NSString *linkString = @"Google.com";
NSString *urlString = @"http://www.google.com";

_youTextView.attributedText = [self linkedStringFromFullString:fullString withLinkString:linkString andUrlString:urlString];

Nó có thể nhấp nhưng nó không mở liên kết hoặc bất cứ điều gì. nó chỉ nhấp như nút không làm gì cả.
Reza.Ab

5

Tôi cần tiếp tục sử dụng một UILabel thuần túy, vì vậy được gọi là từ trình nhận dạng vòi của tôi (điều này dựa trên phản hồi của malex ở đây: Chỉ số ký tự tại điểm chạm cho UILabel )

UILabel* label = (UILabel*)gesture.view;
CGPoint tapLocation = [gesture locationInView:label];

// create attributed string with paragraph style from label

NSMutableAttributedString* attr = [label.attributedText mutableCopy];
NSMutableParagraphStyle* paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.alignment = label.textAlignment;

[attr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, label.attributedText.length)];

// init text storage

NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attr];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];

// init text container

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(label.frame.size.width, label.frame.size.height+100) ];
textContainer.lineFragmentPadding  = 0;
textContainer.maximumNumberOfLines = label.numberOfLines;
textContainer.lineBreakMode        = label.lineBreakMode;

[layoutManager addTextContainer:textContainer];

// find tapped character

NSUInteger characterIndex = [layoutManager characterIndexForPoint:tapLocation
                                                  inTextContainer:textContainer
                         fractionOfDistanceBetweenInsertionPoints:NULL];

// process link at tapped character

[attr enumerateAttributesInRange:NSMakeRange(characterIndex, 1)
                                         options:0
                                      usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
                                          if (attrs[NSLinkAttributeName]) {
                                              NSString* urlString = attrs[NSLinkAttributeName];
                                              NSURL* url = [NSURL URLWithString:urlString];
                                              [[UIApplication sharedApplication] openURL:url];
                                          }
                                      }];

Điều này khá hữu ích, tôi không thể lấy chỉ mục từ các ký tự ở dòng cuối cùng. Mã của bạn có +100 trên textContainer khi khởi tạo CGSize, điều này không có ý nghĩa gì với tôi, nhưng nó đã làm được điều đó.
blueether

4

Cập nhật:

Có 2 phần chính cho câu hỏi của tôi:

  1. Cách tạo liên kết trong đó văn bản hiển thị cho liên kết có thể nhấp khác với liên kết thực tế được gọi:
  2. Cách thiết lập các liên kết mà không phải sử dụng mã tùy chỉnh để đặt các thuộc tính trên văn bản.

Hóa ra iOS 7 đã thêm khả năng tải văn bản được quy cho NSData.

Tôi đã tạo một lớp con tùy chỉnh UITextViewđể tận dụng lợi thế của@IBInspectable thuộc tính và cho phép bạn tải nội dung từ tệp RTF trực tiếp trong IB. Bạn chỉ cần nhập tên tệp vào IB và lớp tùy chỉnh thực hiện phần còn lại.

Đây là những thông tin chi tiết:

Trong iOS 7, NSAttributedStringđã đạt được phương pháp initWithData:options:documentAttributes:error:. Phương pháp đó cho phép bạn tải NSAttributionString từ một đối tượng NSData. Trước tiên, bạn có thể tải tệp RTF vào NSData, sau đó sử dụng initWithData:options:documentAttributes:error:để tải NSData đó vào chế độ xem văn bản của bạn. (Lưu ý rằng cũng có một phương thức initWithFileURL:options:documentAttributes:error:sẽ tải một chuỗi được gán trực tiếp từ một tệp, nhưng phương thức đó không được dùng trong iOS 9. An toàn hơn khi sử dụng phương thức initWithData:options:documentAttributes:error:không bị phản đối.

Tôi muốn một phương thức cho phép tôi cài đặt các liên kết có thể nhấp vào chế độ xem văn bản của mình mà không phải tạo bất kỳ mã cụ thể nào cho các liên kết tôi đang sử dụng.

Giải pháp tôi đưa ra là tạo một lớp con tùy chỉnh của UITextView mà tôi gọi RTF_UITextViewvà cung cấp cho nó một thuộc @IBInspectabletính được gọi RTF_Filename. Thêm@IBInspectable thuộc tính vào thuộc tính sẽ khiến Trình tạo giao diện hiển thị thuộc tính đó trong "Trình theo dõi thuộc tính". Sau đó, bạn có thể đặt giá trị đó từ mã tùy chỉnh IB.

Tôi cũng đã thêm một @IBDesignablethuộc tính cho lớp tùy chỉnh của mình. Các @IBDesignablethuộc tính nói với Xcode rằng nó nên cài đặt một copy running của class view tùy chỉnh của bạn vào giao diện người xây dựng, do đó bạn có thể nhìn thấy nó trong màn hình hiển thị đồ họa của hệ thống phân cấp tầm nhìn của bạn. () Thật không may, đối với lớp này, @IBDesignabletài sản dường như không ổn định. Nó hoạt động khi tôi thêm lần đầu tiên, nhưng sau đó tôi đã xóa nội dung văn bản đơn giản của chế độ xem văn bản của mình và các liên kết có thể nhấp trong chế độ xem của tôi đã biến mất và tôi không thể lấy lại được.)

Mã cho tôi RTF_UITextViewrất đơn giản. Ngoài việc thêm @IBDesignablethuộc tính và thuộc tính RTF_Filenamevới @IBInspectablethuộc tính, tôi đã thêm một didSet()phương thức vào thuộc RTF_Filenametính. Các didSet()phương pháp được gọi bất cứ lúc nào giá trị của RTF_Filenametài sản thay đổi. Mã cho didSet()phương thức này khá đơn giản:

@IBDesignable
class RTF_UITextView: UITextView
{
  @IBInspectable
  var RTF_Filename: String?
    {
    didSet(newValue)
    {
      //If the RTF_Filename is nil or the empty string, don't do anything
      if ((RTF_Filename ?? "").isEmpty)
      {
        return
      }
      //Use optional binding to try to get an URL to the
      //specified filename in the app bundle. If that succeeds, try to load
      //NSData from the file.
      if let fileURL = NSBundle.mainBundle().URLForResource(RTF_Filename, withExtension: "rtf"),
        
        //If the fileURL loads, also try to load NSData from the URL.
        let theData = NSData(contentsOfURL: fileURL)
      {
        var aString:NSAttributedString
        do
        {
          //Try to load an NSAttributedString from the data
          try
            aString = NSAttributedString(data: theData,
              options: [:],
              documentAttributes:  nil
          )
          //If it succeeds, install the attributed string into the field.
          self.attributedText = aString;
        }
        catch
        {
          print("Nerp.");
        }
      }
      
    }
  }
}

Lưu ý rằng nếu thuộc tính @IBDesignable sẽ không cho phép bạn xem trước văn bản theo kiểu của mình trong Trình tạo giao diện thì tốt hơn nên đặt mã ở trên dưới dạng phần mở rộng của UITextView thay vì lớp con tùy chỉnh. Bằng cách đó bạn có thể sử dụng nó trong bất kỳ chế độ xem văn bản nào mà không phải thay đổi chế độ xem văn bản thành lớp tùy chỉnh.

Xem câu trả lời khác của tôi nếu bạn cần hỗ trợ các phiên bản iOS trước iOS 7.

Bạn có thể tải xuống một dự án mẫu bao gồm lớp mới này từ gitHub:

Dự án demo DatesInSwift trên Github


3

Chỉ cần tìm một giải pháp không có mã cho UITextView: nhập mô tả hình ảnh ở đây

Bật tùy chọn Phát hiện-> Liên kết, URL và email cũng sẽ được phát hiện và có thể nhấp!


3
Điều đó làm cho các liên kết có thể nhấp. Tôi muốn có văn bản người dùng có thể đọc được có một liên kết đằng sau nó. Xem ví dụ trong câu hỏi ban đầu của tôi.
Duncan C

Có, câu trả lời của tôi chỉ áp dụng cho trường hợp liên kết giống như văn bản. Nếu liên kết là một thứ khác, tôi sẽ làm theo câu trả lời của @ ujell.
Bill Chan

3
Câu hỏi của tôi rất cụ thể về văn bản có thể nhấp để hiển thị nội dung nào đó ngoài URL. Bạn đã không làm nhiều hơn là liếc nhìn câu hỏi, phải không?
Duncan C

1
không phục vụ mục đích của người khác nhưng chắc chắn đây là thứ tôi đã đến để tìm kiếm ... một cách để tạo liên kết trong ứng dụng trò chuyện của tôi có thể nhấp được. Bingo tôi tìm thấy bài viết này ... cảm ơn! Wish xcode sẽ cho phép twitter và thẻ băm cho phép.
MizAkita 17/03/2016

Điều này hoạt động ngay cả với văn bản tùy chỉnh insted của liên kết thô. Nhớ chọn Hành vi -> Có thể chọn và Phát hiện -> Liên kết.
krlbsk

3

Phiên bản Swift:

    // Attributed String for Label
    let plainText = "Apkia"
    let styledText = NSMutableAttributedString(string: plainText)
    // Set Attribuets for Color, HyperLink and Font Size
    let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(14.0), NSLinkAttributeName:NSURL(string: "http://apkia.com/")!, NSForegroundColorAttributeName: UIColor.blueColor()]
    styledText.setAttributes(attributes, range: NSMakeRange(0, plainText.characters.count))
    registerLabel.attributedText = styledText

3

Sử dụng UITextView và đặt dataDetectorTypes cho Liên kết.

như thế này:

testTextView.editable = false 
testTextView.dataDetectorTypes = .link

Nếu bạn muốn phát hiện liên kết, số điện thoại, địa chỉ, v.v.

testTextView.dataDetectorTypes = .all

3
Không. Điều này chỉ cho phép bạn tạo liên kết có thể nhấp. Câu hỏi của tôi là cụ thể để tạo văn bản tùy ý như "nhấp vào đây" có thể nhấp, không phải là URL nhưhttp://somedomain/someurl?param=value
Duncan C

2

Một bổ sung nhanh chóng cho hành vi IB mô tả ban đầu của Duncan C. Ông viết: "Thật đơn giản để làm cho các siêu liên kết có thể nhấp vào trong UITextView. Bạn chỉ cần đặt hộp kiểm" phát hiện liên kết "trên chế độ xem trong IB và nó phát hiện các liên kết http và biến chúng thành siêu liên kết."

Kinh nghiệm của tôi (ít nhất là trong xcode 7) là bạn cũng phải bỏ chọn hành vi "Có thể chỉnh sửa" để các url được phát hiện & có thể nhấp.


2

Trong trường hợp bạn gặp sự cố với những gì @Karl Nosworthy và @esilver đã cung cấp ở trên, tôi đã cập nhật tiện ích mở rộng NSMutableAttributionString lên phiên bản Swift 4 của nó.

extension NSMutableAttributedString {

public func setAsLink(textToFind:String, linkURL:String) -> Bool {

    let foundRange = self.mutableString.range(of: textToFind)
    if foundRange.location != NSNotFound {
         _ = NSMutableAttributedString(string: textToFind)
        // Set Attribuets for Color, HyperLink and Font Size
        let attributes = [NSFontAttributeName: UIFont.bodyFont(.regular, shouldResize: true), NSLinkAttributeName:NSURL(string: linkURL)!, NSForegroundColorAttributeName: UIColor.blue]

        self.setAttributes(attributes, range: foundRange)
        return true
    }
    return false
  }
}


0

Nếu bạn muốn sử dụng NSLinkAttributionName trong UITextView, thì bạn có thể xem xét sử dụng thư viện AttributionTextView. Đây là một lớp con UITextView giúp xử lý chúng rất dễ dàng. Để biết thêm thông tin, hãy xem: https://github.com/evermeer/AttributionTextView

Bạn có thể làm cho bất kỳ phần nào của văn bản tương tác như thế này (trong đó textView1 là một IBoutlet UITextView):

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) 

Và để xử lý hashtags và đề cập, bạn có thể sử dụng mã như thế này:

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


0
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks];

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

customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;

NHỮNG ĐIỂM CHÍNH:

  • Đảm bảo rằng bạn bật hành vi "Có thể chọn" của UITextView trong XIB.
  • Đảm bảo rằng bạn tắt hành vi "Có thể chỉnh sửa" của UITextView trong XIB.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.