Nói chung, nếu chúng tôi muốn có một liên kết có thể nhấp trong văn bản được hiển thị bởi UILabel, chúng tôi sẽ cần giải quyết hai tác vụ độc lập:
- Thay đổi diện mạo của một phần văn bản để trông giống như một liên kết
- Phát hiện và xử lý chạm vào liên kết (mở URL là một trường hợp cụ thể)
Điều đầu tiên là dễ dàng. Bắt đầu từ iOS 6 UILabel hỗ trợ hiển thị các chuỗi được quy. Tất cả những gì bạn cần làm là tạo và cấu hình một thể hiện của NSMutableAttributionString:
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;
Đó là nó! Đoạn mã trên làm cho UILabel hiển thị Chuỗi có liên kết
Bây giờ chúng ta nên phát hiện chạm vào liên kết này. Ý tưởng là bắt tất cả các vòi trong UILabel và tìm hiểu xem vị trí của vòi có đủ gần với liên kết hay không. Để bắt được chạm, chúng ta có thể thêm nhận dạng cử chỉ nhấn vào nhãn. Đảm bảo bật userInteraction cho nhãn, nó được tắt theo mặc định:
label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];
Bây giờ là thứ phức tạp nhất: tìm hiểu xem liệu vòi có ở nơi liên kết được hiển thị và không trên bất kỳ phần nào khác của nhãn. Nếu chúng ta có UILabel một dòng, nhiệm vụ này có thể được giải quyết tương đối dễ dàng bằng cách mã hóa giới hạn khu vực nơi liên kết được hiển thị, nhưng hãy giải quyết vấn đề này một cách thanh lịch hơn và trong trường hợp chung - UILabel đa cấp mà không có kiến thức sơ bộ về bố cục liên kết.
Một trong những cách tiếp cận là sử dụng các khả năng của API Text Kit được giới thiệu trong 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;
Lưu các phiên bản được tạo và định cấu hình của NSLayoutManager, NSTextContainer và NSTextStorage trong các thuộc tính trong lớp của bạn (rất có thể là hậu duệ của UIViewContoder) - chúng ta sẽ cần chúng trong các phương thức khác.
Bây giờ, mỗi khi nhãn thay đổi khung của nó, hãy cập nhật kích thước của textContainer:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.textContainer.size = self.label.bounds.size;
}
Và cuối cùng, phát hiện xem liệu vòi có chính xác trên liên kết hay không:
- (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/"]];
}
}
Swift 4
giải pháp hoạt động hoàn toàn . Nó sử dụngUITextView
nhưng làm cho nó hoạt động như mộtUILabel
. Tôi đã thử các giải pháp ở đây và không thể phát hiện liên kết chính xác.