ZSWTappableLabel is a UILabel subclass for links which are tappable, long-pressable, 3D Touchable, and VoiceOverable. It has optional highlighting behavior, and does not draw text itself. Its goal is to be as minimally different from UILabel as possible, and only executes additional code when the user is interacting with a tappable region.
A basic, tappable link
Let’s create a string that’s entirely tappable and shown with an underline:
You can configure the longPressDuration for how long until a long-press is recognized. This defaults to 0.5 seconds.
3D Touch
If you’ve registered either the label or a view containing the label for previewing using peek/pop, you can get information about the tappable regions at a point using one of the two tappableRegionInfo methods on ZSWTappableLabel. See the header file for more information.
When you’re queried for previewing information, you can respond using the information from these methods. For example, to preview an 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)
}
We can wire up the tapDelegate to receive the checking result and handle each result type when the user taps on the link:
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;
}
}
}
Substring linking
For substring linking, I suggest you use ZSWTaggedString which creates these attributed strings painlessly and localizably. Let’s create a more advanced ‘privacy policy’ link using this library:
ZSWTappableLabel is an accessibility container, which exposes the substrings in your attributed string as distinct elements. For example, the above string breaks down into:
View our (static text)
Privacy Policy (link)
or (static text)
Terms of Service (link)
This is similar behavior to Safari, which breaks up elements into discrete chunks.
When you set a longPressDelegate, an additional action on links is added to perform the long-press gesture. You should configure the longPressAccessibilityActionName to adjust what is read to users.
When you set an accessibilityDelegate, you can add custom actions to a particular link, for example:
ZSWTappableLabel
ZSWTappableLabel is a UILabel subclass for links which are tappable, long-pressable, 3D Touchable, and VoiceOverable. It has optional highlighting behavior, and does not draw text itself. Its goal is to be as minimally different from UILabel as possible, and only executes additional code when the user is interacting with a tappable region.
A basic, tappable link
Let’s create a string that’s entirely tappable and shown with an underline:
This results in a label which renders like:
Setting your controller as the
tapDelegate
of the label results in the following method call when tapped:Long-presses
You may optionally support long-presses by setting a
longPressDelegate
on the label. This behaves very similarly to thetapDelegate
:You can configure the
longPressDuration
for how long until a long-press is recognized. This defaults to 0.5 seconds.3D Touch
If you’ve registered either the label or a view containing the label for previewing using peek/pop, you can get information about the tappable regions at a point using one of the two
tappableRegionInfo
methods onZSWTappableLabel
. See the header file for more information.When you’re queried for previewing information, you can respond using the information from these methods. For example, to preview an SFSafariViewController:
Data detectors
Let’s use
NSDataDetector
to find the substrings in a given string that we might want to turn into links:This results in a label which renders like:
We can wire up the
tapDelegate
to receive the checking result and handle each result type when the user taps on the link:Substring linking
For substring linking, I suggest you use ZSWTaggedString which creates these attributed strings painlessly and localizably. Let’s create a more advanced ‘privacy policy’ link using this library:
You can create such a string using a simple ZSWTaggedString:
Accessibility
ZSWTappableLabel is an accessibility container, which exposes the substrings in your attributed string as distinct elements. For example, the above string breaks down into:
View our
(static text)Privacy Policy
(link)or
(static text)Terms of Service
(link)This is similar behavior to Safari, which breaks up elements into discrete chunks.
When you set a
longPressDelegate
, an additional action on links is added to perform the long-press gesture. You should configure thelongPressAccessibilityActionName
to adjust what is read to users.When you set an
accessibilityDelegate
, you can add custom actions to a particular link, for example:You can also change the
accessibilityLabel
of the created accessibility elements, for example:Installation
ZSWTappableLabel is available through CocoaPods. To install it, simply add the following line to your Podfile:
ZSWTappableLabel is available through Swift Package Manager in a
Package.swift
like:To add it to an Xcode project, add the URL via File > Swift Packages -> Add Package Dependency.
License
ZSWTappableLabel is available under the MIT license. This library was created while working on Free who allowed this to be open-sourced.