ZSWTappableLabel
ZSWTappableLabel是一个可点击、长按、3D触控和VoiceOver的UILabel子类。它具有可选的高亮行为,本身不绘制文本。其目标是尽可能接近UILabel,仅在用户与可点击区域交互时执行额外的代码。
基本的可点击链接
让我们创建一个完全可点击并且带有下划线的字符串
let string = NSLocalizedString("Privacy Policy", comment: "")
let attributes: [NSAttributedString.Key: Any] = [
.tappableRegion: true,
.tappableHighlightedBackgroundColor: UIColor.lightGray,
.tappableHighlightedForegroundColor: UIColor.white,
.foregroundColor: UIColor.blue,
.underlineStyle: NSUnderlineStyle.single.rawValue,
.link: URL(string: "http://imgur.com/gallery/VgXCk")!
]
label.attributedText = NSAttributedString(string: string, attributes: attributes)
NSString *s = NSLocalizedString(@"Privacy Policy", nil);
NSDictionary *a = @{
ZSWTappableLabelTappableRegionAttributeName: @YES,
ZSWTappableLabelHighlightedBackgroundAttributeName: [UIColor lightGrayColor],
ZSWTappableLabelHighlightedForegroundAttributeName: [UIColor whiteColor],
NSForegroundColorAttributeName: [UIColor blueColor],
NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle),
NSLinkAttributeName: [NSURL URLWithString:@"http://imgur.com/gallery/VgXCk"],
};
label.attributedText = [[NSAttributedString alloc] initWithString:s attributes:a];
这将产生一个渲染如下所示的标签
将控制器设为标签的tapDelegate
,当点击时会调用以下方法
func tappableLabel(
_ tappableLabel: ZSWTappableLabel,
tappedAt idx: Int,
withAttributes attributes: [NSAttributedString.Key : Any]
) {
if let url = attributes[.link] as? URL {
UIApplication.shared.openURL(url)
}
}
- (void)tappableLabel:(ZSWTappableLabel *)tappableLabel
tappedAtIndex:(NSInteger)idx
withAttributes:(NSDictionary<NSAttributedStringKey, id> *)attributes {
[[UIApplication sharedApplication] openURL:attributes[@"URL"]];
}
长按
您可以选择支持长按,通过在标签上设置longPressDelegate
。这行为与tapDelegate
非常相似
func tappableLabel(
_ tappableLabel: ZSWTappableLabel,
longPressedAt idx: Int,
withAttributes attributes: [NSAttributedString.Key : Any]
) {
guard let URL = attributes[.link] as? URL else {
return
}
let activityController = UIActivityViewController(activityItems: [URL], applicationActivities: nil)
present(activityController, animated: true, completion: nil)
}
- (void)tappableLabel:(ZSWTappableLabel *)tappableLabel
longPressedAtIndex:(NSInteger)idx
withAttributes:(NSDictionary<NSAttributedStringKey, id> *)attributes {
NSURL *URL = attributes[NSLinkAttributeName];
if ([URL isKindOfClass:[NSURL class]]) {
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:@[ URL ] applicationActivities:nil];
[self presentViewController:activityController animated:YES completion:nil];
}
}
您可以配置longPressDuration
来设置识别长按的时间。默认为0.5秒。
3D Touch
如果您已注册用于预览的标签或包含标签的视图,您可以使用 ZSWTappableLabel
上的两个 tappableRegionInfo
方法之一来获取关于可点击区域的信息。有关更多信息,请参阅 头文件。
当您被查询预览信息时,您可以使用这些方法中的信息做出响应。例如,要预览 SFSafariViewController
func previewingContext(
_ previewingContext: UIViewControllerPreviewing,
viewControllerForLocation location: CGPoint
) -> UIViewController? {
guard let regionInfo = label.tappableRegionInfo(
forPreviewingContext: previewingContext,
location: location
) else {
return nil
}
guard let URL = regionInfo.attributes[.link] as? URL else {
return nil
}
// convenience method that sets the rect of the previewing context
regionInfo.configure(previewingContext: previewingContext)
return SFSafariViewController(url: URL)
}
- (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext
viewControllerForLocation:(CGPoint)location {
id<ZSWTappableLabelTappableRegionInfo> regionInfo =
[self.label tappableRegionInfoForPreviewingContext:previewingContext location:location];
if (!regionInfo) {
return nil;
}
[regionInfo configurePreviewingContext:previewingContext];
return [[SFSafariViewController alloc] initWithURL:regionInfo.attributes[NSLinkAttributeName]];
}
数据检测器
让我们使用 NSDataDetector
来找到给定字符串中我们可以将其转换为链接的子字符串
let string = "check google.com or call 415-555-5555? how about friday at 5pm?"
let detector = try! NSDataDetector(types: NSTextCheckingAllSystemTypes)
let attributedString = NSMutableAttributedString(string: string, attributes: nil)
let range = NSRange(location: 0, length: (string as NSString).length)
detector.enumerateMatches(in: attributedString.string, options: [], range: range) { (result, flags, _) in
guard let result = result else { return }
var attributes = [NSAttributedString.Key: Any]()
attributes[.tappableRegion] = true
attributes[.tappableHighlightedBackgroundColor] = UIColor.lightGray
attributes[.tappableHighlightedForegroundColor] = UIColor.white
attributes[.underlineStyle] = NSUnderlineStyle.single.rawValue
attributes[.init(rawValue: "NSTextCheckingResult")] = result
attributedString.addAttributes(attributes, range: result.range)
}
label.attributedText = attributedString
NSString *string = @"check google.com or call 415-555-5555? how about friday at 5pm?";
NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingAllSystemTypes error:NULL];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string attributes:nil];
// the next line throws an exception if string is nil - make sure you check
[detector enumerateMatchesInString:string options:0 range:NSMakeRange(0, string.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
attributes[ZSWTappableLabelTappableRegionAttributeName] = @YES;
attributes[ZSWTappableLabelHighlightedBackgroundAttributeName] = [UIColor lightGrayColor];
attributes[ZSWTappableLabelHighlightedForegroundAttributeName] = [UIColor whiteColor];
attributes[NSUnderlineStyleAttributeName] = @(NSUnderlineStyleSingle);
attributes[@"NSTextCheckingResult"] = result;
[attributedString addAttributes:attributes range:result.range];
}];
label.attributedText = attributedString;
这将产生一个渲染如下所示的标签
检查 google.com 或拨打电话 415-555-5555? 或者 周五下午5点?
我们可以将 tapDelegate
连接到以接收检查结果,并在用户点击链接时处理每个结果类型
func tappableLabel(
tappableLabel: ZSWTappableLabel,
tappedAtIndex idx: Int,
withAttributes attributes: [NSAttributedString.Key : Any]) {
if let result = attributes[.init(rawValue: "NSTextCheckingResult")] as? NSTextCheckingResult {
switch result.resultType {
case [.address]:
print("Address components: \(result.addressComponents)")
case [.phoneNumber]:
print("Phone number: \(result.phoneNumber)")
case [.date]:
print("Date: \(result.date)")
case [.link]:
print("Link: \(result.url)")
default:
break
}
}
}
- (void)tappableLabel:(ZSWTappableLabel *)tappableLabel
tappedAtIndex:(NSInteger)idx
withAttributes:(NSDictionary<NSAttributedStringKey, id> *)attributes {
NSTextCheckingResult *result = attributes[@"NSTextCheckingResult"];
if (result) {
switch (result.resultType) {
case NSTextCheckingTypeAddress:
NSLog(@"Address components: %@", result.addressComponents);
break;
case NSTextCheckingTypePhoneNumber:
NSLog(@"Phone number: %@", result.phoneNumber);
break;
case NSTextCheckingTypeDate:
NSLog(@"Date: %@", result.date);
break;
case NSTextCheckingTypeLink:
NSLog(@"Link: %@", result.URL);
break;
default:
break;
}
}
}
子字符串链接
对于子字符串链接,我建议您使用 ZSWTaggedString,它创建这些属性字符串简单且可本地化。让我们使用这个库创建一个更高级的 '隐私策略' 链接
您可以使用简单的 ZSWTaggedString 创建这样的字符串
let options = ZSWTaggedStringOptions()
options["link"] = .dynamic({ tagName, tagAttributes, stringAttributes in
guard let type = tagAttributes["type"] as? String else {
return [NSAttributedString.Key: Any]()
}
var foundURL: URL?
switch type {
case "privacy":
foundURL = URL(string: "http://google.com/search?q=privacy")!
case "tos":
foundURL = URL(string: "http://google.com/search?q=tos")!
default:
break
}
guard let URL = foundURL else {
return [NSAttributedString.Key: Any]()
}
return [
.tappableRegion: true,
.tappableHighlightedBackgroundColor: UIColor.lightGray,
.tappableHighlightedForegroundColor: UIColor.white,
.foregroundColor: UIColor.blue,
.underlineStyle: NSUnderlineStyle.single.rawValue,
.link: foundURL
]
})
let string = NSLocalizedString("View our <link type='privacy'>Privacy Policy</link> or <link type='tos'>Terms of Service</link>", comment: "")
label.attributedText = try? ZSWTaggedString(string: string).attributedString(with: options)
ZSWTaggedStringOptions *options = [ZSWTaggedStringOptions options];
[options setDynamicAttributes:^NSDictionary *(NSString *tagName,
NSDictionary *tagAttributes,
NSDictionary *existingStringAttributes) {
NSURL *URL;
if ([tagAttributes[@"type"] isEqualToString:@"privacy"]) {
URL = [NSURL URLWithString:@"http://google.com/search?q=privacy"];
} else if ([tagAttributes[@"type"] isEqualToString:@"tos"]) {
URL = [NSURL URLWithString:@"http://google.com/search?q=tos"];
}
if (!URL) {
return nil;
}
return @{
ZSWTappableLabelTappableRegionAttributeName: @YES,
ZSWTappableLabelHighlightedBackgroundAttributeName: [UIColor lightGrayColor],
ZSWTappableLabelHighlightedForegroundAttributeName: [UIColor whiteColor],
NSForegroundColorAttributeName: [UIColor blueColor],
NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle),
@"URL": URL
};
} forTagName:@"link"];
NSString *string = NSLocalizedString(@"View our <link type='privacy'>Privacy Policy</link> or <link type='tos'>Terms of Service</link>", nil);
label.attributedText = [[ZSWTaggedString stringWithString:string] attributedStringWithOptions:options];
无障碍性
ZSWTappableLabel 是一个无障碍性容器,它将您的属性字符串中的子字符串公开为不同的元素。例如,上述字符串分为以下几部分:
查看我们的
(静态文本)隐私政策
(链接)或
(静态文本)服务条款
(链接)
这与 Safari 的类似行为,将元素分解为离散的部分。
当您设置 longPressDelegate
时,将添加一个附加操作以执行长按手势。您应配置 longPressAccessibilityActionName
以调整用户阅读的内容。
当您设置 accessibilityDelegate
时,您可以为特定链接添加自定义操作,例如
func tappableLabel(
_ tappableLabel: ZSWTappableLabel,
accessibilityCustomActionsForCharacterRange characterRange: NSRange,
withAttributesAtStart attributes: [NSAttributedString.Key : Any] = [:]
) -> [UIAccessibilityCustomAction] {
return [
UIAccessibilityCustomAction(
name: NSLocalizedString("View Link Address", comment: ""),
target: self,
selector: #selector(viewLink(_:))
)
]
}
- (NSArray<UIAccessibilityCustomAction *> *)tappableLabel:(ZSWTappableLabel *)tappableLabel
accessibilityCustomActionsForCharacterRange:(NSRange)characterRange
withAttributesAtStart:(NSDictionary<NSAttributedStringKey,id> *)attributes {
return @[
[[UIAccessibilityCustomAction alloc] initWithName:NSLocalizedString(@"View Link Address", nil)
target:self
selector:@selector(viewLink:)]
];
}
您还可以更改创建的无障碍元素的 accessibilityLabel
,例如
func tappableLabel(
_ tappableLabel: ZSWTappableLabel,
accessibilityLabelForCharacterRange characterRange: NSRange,
withAttributesAtStart attributes: [NSAttributedString.Key : Any] = [:]
) -> String? {
if attributes[.link] != nil {
return "Some Custom Label"
} else {
return nil
}
}
- (nullable NSString *)tappableLabel:(nonnull ZSWTappableLabel *)tappableLabel
accessibilityLabelForCharacterRange:(NSRange)characterRange
withAttributesAtStart:(nonnull NSDictionary<NSAttributedStringKey,id> *)attributes {
if (attributes[NSLinkAttributeName] != nil) {
return @"Some Custom Label";
} else {
return nil;
}
}
安装
ZSWTappableLabel可通过CocoaPods获取。要安装它,只需在Podfile中添加以下行
pod "ZSWTappableLabel", "~> 3.2"