[ Music ]
Hello. My name is Sommer Panage and I'm an engineering manager on the Accessibility team here at Apple.
I'll be talking about the Large Content Viewer.
A feature that helps people who prefer larger text sizes.
In this talk, you'll learn how to ensure that all parts of your app are readable for everyone.
We'll first cover what the Large Content Viewer is and why it's important.
Next, we'll look at some API that will allow you to support it in your app.
Finally, we'll cover examples of some situations you might encounter in your app and how to handle them.
So let's start with some background.
Before we talk about the Large Content Viewer, let's review a related topic, Dynamic Type.
Dynamic Type is a feature that allows you to customize the size of text for the system.
You can make it smaller if you want to fit more content on screen, or you can make it larger and more readable.
By default, you can choose between seven different sizes.
But if you go into your Accessibility settings, you can enable larger sizes and then you get five additional sizes.
This is important because many people aren't just using this feature because they prefer a different text size.
They're doing it because they need a larger text size in order to be able to read the text.
So let's take a look at how Dynamic Type changes the behavior of iOS.
Here's how the phone app looks at the default text size.
Now, if I change my text size to one of the Accessibility sizes, the text gets much bigger.
However, you might have noticed that part of the text on screen is not getting bigger.
For example, look at the button in the bottom left.
If the customer has set their size to be large like we see in the content area, then they likely wouldn't see the text that we see in the tab bar because it's still small.
You might not be able to make up the icon above it either in fact.
However, we don't want to grow the tab bar because that would leave very little room for the main content that you see, in this case your contact list.
So instead, if you're using larger text sizes, we allow you to long press on that button to see a larger version.
Here's how that looks.
You can now drag your finger along the bottom bar to see what's on each of those buttons.
When you finally drag your finger onto the button that you want to tap, you can just let go and the Apple act as though you tapped the button.
This feature is what we call the Large Content Viewer.
It allows people with low vision to use tab bars or any other UI that has to stay small.
It's important for your app to show the Large Content Viewer when appropriate.
If you're using standard UIKit bars, you already showed the Large Content Viewer.
You might just not have noticed it before because it's only enabled for the Accessibility text sizes.
But if you've got a custom bar or some other custom UI that has to stay small, you'll need to implement some API so that your UI works just as well as the standard UIKit controls.
Remember, this is only for the case when your custom UI cannot grow in size.
Scaling with dynamic type is always preferred to showing the Large Content Viewer.
But sometimes, our bars simply can't scale.
So let's look at how you can support the Large Content Viewer in your app.
Earlier, I said that you already showed the Large Content Viewer if you're using standard UIKit bars.
But you might still need to do something to make sure that it looks good.
If your bar items use PDF images, make sure that you check the Preserve Vector checkbox in the asset catalog as seen here.
That will ensure that your images scale smoothly to larger sizes.
If your image button doesn't have vector data, for example if you're using a PNG, then by default that image will look blurry when it's shown in the viewer.
To fix that, you'll need to provide a larger version of the image.
Use the large content size image property on UI bar item to do that.
If you need to adjust the positioning, for example, if your image doesn't look centered in the viewer the way you wanted to, then you can adjust the large content size image in sets.
So what do you need to do if you have custom UI?
Well, before iOS 13, you basically had to make your own Large Content Viewer from scratch.
But now, we've added API that allows you to show the same UI that's shown for standard UIKit bars.
So let's say you have a custom tab bar, first you'll need to specify the buttons in your tab bar as items to be shown in the viewer.
Then the Large Content Viewer needs a title and/or an image for each of those buttons.
If you happened to be using standard UIKit classes for those buttons, you'll get those for free.
Finally, you'll need to set up a gesture interaction on the custom tab bar itself.
The new UILargeContentViewerItem protocol specifies the info that the Large Content Viewer needs in order to show your content.
This showsLargeContentViewer property is what you'll use to mark your tab bar buttons.
Set it to true on any views that should present the viewer.
Then you can specify a largeContentTitle and a largeContentImage.
You can specify either of those or both of them.
If you want to reuse a PDF image from a small button, you can set the scalesLargeContentImage property to true.
But just as we saw earlier with UI bar item, make sure that you preserve the vector data for your image.
And lastly, if you want to center your image, you can use the largeContentImageInsets.
UIView already implements this protocol and it also provides setters.
So in many cases, you can just set the properties rather than having to subclass and override.
Also, as we mentioned earlier, certain UIKit classes like UI button and UI label, return default values for their title and image.
So that's how you annotate the buttons in your tab bar.
Next, you'll need to add a gesture interaction to the tab bar itself.
To add the interaction, you'll use the same addInteraction method that's used for adding, drag and drop support.
Here's how the interaction for the Large Content Viewer works.
It's got a few properties on it.
In the simple case, you can create an instance of this interaction with no arguments and you don't need to touch any of the other properties here.
But if your app has other gesture recognizers, you may need to customize the behavior.
You can provide a delegate to get fine-grained control over how it works.
You can also use the gestureRecognizer property if you need to set up relationships with your app's other gesture recognizers.
Finally, to find out whether the Large Content Viewer is enabled, you can check the isEnabled property.
And of course, you can listen for the notification to see when that changes.
There are a few ways to customize the behavior using the delegate.
First, you can specify what happens when the user lifts their finger from an item in your custom view.
It should act as though the user tapped on that item.
If you don't implement this method and you're using a standard UI control like UI button, by default it will get sent a touchup inside event.
But if you got something more custom there, for example a view with its own tap gesture recognizer, you can implement this method to provide the behavior you need.
Next, there's an option to provide the item at a particular point.
By default, the item will be found by calling point inside with event recursively on your view hierarchy.
But that might not work if you aren't using views for example.
So, this method lets you return which item should be shown in the viewer for a particular point.
Finally, you can decide which viewController should house the Large Content Viewer.
By default, UIKit will try to pick a viewController that contains the view that you added the interaction to.
But if that default choice doesn't work well for your app, you can specify one here.
So, we've covered the API.
Now let's look at some examples showing how to use it.
First, let's look at a simple case.
You've got a custom bar but inside you've got standard UIKit views.
Here we've got a bar with a UI button and a UI label.
Because we're using standard views, all we need to do is up to the button and the label in using the showsLargeContentViewer property.
There's no need to specify a title or an image since those can be inferred from the views of the properties.
Finally, we can add the gesture interaction to the custom bar with no need to modify any properties.
If your custom bar uses something else for its buttons, then you'll need some of the other API.
Let's say each of your buttons is an instance of a MyButton custom class.
You can override the UI Large Content Viewer item properties on your buttons to return the right info.
Here we're returning true for showsLargeContentViewer and we're returning our text as the title.
If you have an image with vector data for each button, then you can use the exact same image for the Large Content Viewer.
Just make sure to return true for scalesLargeContentImage so that it grows to the correct size.
Finally, let's look at an example when you need to deal with another gesture recognizer.
A classic example from one of Apple's apps is in Safari.
When you tap on the Back button, it goes to the previous page.
But when you long press on it, it brings you to page with your past browsing history.
The Large Content Viewer also uses a long press gesture.
So how do you get those to work together?
You should absolutely allow both things to work.
In other words, let the Large Content Viewer show so that people can see what's on the button.
But once they've had a chance to read it, do the thing you would normally do on long press.
To do that, first, let's increase our existing longPressRecognizer's duration.
That way we'll give people some extra time to show the viewer first before we do the other action.
You should also make sure you update the duration if people's setting change.
If they start or stop using one of the Accessibility text sizes, the duration should also update.
Next, let's set a UIGestureRecognizer delegate on the existing longPressRecognizer.
Now, in your delegate callbacks, you can make sure that both of your existing longPressRecognizer and the gestureRecognizer from the Large Content Viewer can recognize at the same time.
That way, neither one of them will prevent the other from working.
Here, we can see the behavior with our Large Content Viewer implementation.
If I lift my finger while the Large Content Viewer is visible, I go back as expected.
However, if I long press for the longer duration, I can still get to that browsing history.
So that's how your app can accommodate people who prefer larger text sizes even for UI that needs to stay small.
Note that you should only use the Large Content Viewer for UI that can't scale.
Scaling for Dynamic Type is always the preferred option.
So if you can do that, don't use this as API.
Finally, if your custom view has other interactions, make sure that those are all still possible for people who use the Large Content Viewer.
That way, everyone can use all of your app's great features.
That's it for now.
Thanks for watching.