The title will be different

    If you are developing a product for the mass market, then most likely people with low vision use it. If you strive to make user-friendly interfaces, then you need to do it conveniently for all clients, including people with low vision. I think we often forget about it. And it's time to fix it.

    I entered the query “pizza delivery” in the search on the App Store, downloaded the first 24 applications and checked which of them provides an interface for people with low vision. 

    2 out of 24 . And one of the two, it seems, did it by accident: as the font size increases, the entire interface “floats” and it becomes only harder to use. It’s sad.

    550,000 people use the Dodo Pizza iOS app every month. Even if 1% of our users have enlarged font, it is 5500 people who are uncomfortable using our application. We will correct it.

    Add Dynamic Type Support

    1. We use dynamic system text styles instead of static ones.
    2. Optionally, enable the Automatically Adjusts Font checkmark on labels in storyboards. Or, if the label in the button or is created through the code, we knock on its parameter adjustsFontForContentSizeCategory.
    3. We teach the interface to stretch to different font sizes:
      - We use automatic calculation of cell sizes where we can.
      - Where we can’t - we get the current font size settings and respond to changes in the method traitCollectionDidChange.
    4. We get an interface that is impossible to use.

    We change the interface so that it can be used

    We roll back and start thinking how to do everything well.

    Properly use the place in the menu

    Now there is a lot of empty space under the pizza picture. Let's try to put a picture above the name: this way it will become larger, and the empty space will disappear. To do this, we wrap the UIStackViewpicture and the view container with everything else, and then we will switch the direction of the stack if necessary. We do not have separators between menu items, which is why when the size is large, the cells begin to “stick together” and the pizza price tag is too close to the picture of the next pizza. Let's try to add a separator. Not that. Firstly, it looks so-so. Secondly, it will be hard to see for people with low vision. Even if repainted from gray to black. We remove it back and just try to increase the Internet between cells. Now then.

    Subtotal: use more space, eyes jump less from line to line, reading has become easier. 

    We improve stretching and removing

    Now you can increase the width of the add to cart button, otherwise it floated to the left and is not under your finger, although there is a lot of empty space on the right. You can, of course, simply move it to the right side, but then it will be inconvenient for left-handed people and, in general, let's better gesture than uncompressed. And we will make the stroke fatter, and now it is not consistent with the font. I look at all this and understand that the pizza photo, of course, is very huge. Let's try to hide it, maybe without pictures you can live. In general, a menu without photos has not lost much of information, but now one menu item almost always interferes with the screen of an iPhone 6S. But it has become less attractive, Drool does not flow when scrolling. Such. For now, let’s leave it like this, think carefully and maybe later return the picture.

    Do not forget to check "live"

    Now categories. In general, even with the first approach it turned out tolerably. Turn on a new one. I tried to browse through the menu and switch between categories. Nevertheless, it turned out badly: everything falls apart in action. When scrolling through the menu, categories automatically switch, and on such large pins it attracts too much attention. Let's replace with the button that will call . Voooot. Now you can take on the top panel, where the city, stocks, address and promotional code. 


    Do not forget about very long lines

    First, let's take a pick of the city. There is nothing complicated with the font in the button, but it’s interesting to teach the “triangles” to grow with the font. In our case, the triangle was made an icon in the button, which is moved to the right side through CGAffineTransform. Another option is to collect a NSAttributedStringtriangle from the text and icons, and then feed it all to the button. To normalize the icon, you can use a vector image, which must necessarily be in assets with the Preserve Vector Data checkmark.

    The icon of the triangle is black, and is painted white through the code. And for some reason, with the standard text size, artifacts in the form of black borderboards come out on it. It's funny Not really. He was cured by putting an icon that was originally white in assets.

    Now we stretch the dodo-rubles, everything is simple: And now the question is: what will happen if the name of the city turns out to be long and we have a lot of dodo-rubles? In theory, you need to shorten the name of the city. Remember that I was talking about the second option for adding such an icon to a button, through ? I tried and now there is a problem that when we reduce the title, the triangle icon disappears, because now it is part of the title. Stosh. We'll have to return the logic of moving the icon through transforms. 

    If you know a convenient way to move the icon in the button to the right side and scale it together with the font in the header - discard it in comments, please.

    Cram in

    Finally stocks. Here you need to sit down and think. The title can be long and even now it sometimes does not fit on one line. On a large size, he will not fit well at all. If you make the upper orange panel rubber and allow the title of the action in a large size to occupy several lines, then the upper block will eat up half the screen even on large iPhones, and you won’t have to remember about 4S. This is not the case. You can play with a layout inside the action box: make the picture square, and take the vacant place as a heading. But the pictures for stocks are customized to a specific format and will not display correctly in another. You can not do it this way.


    So, but you can again completely remove the pictures and take up the whole place with a headline.

    Yeah, it is. Hands itch to colorize the background under the title of the action, but this will adversely affect readability. And we, like, are trying to improve it. So we do not paint anything and move on to the remaining two buttons about the address and promotional codes.

    We work with strict restrictions

    The headings in these buttons are irreducible. But if they are not reduced, then the buttons will creep onto each other. And yes, you cannot hide these buttons.

    When I redid the stock, I did not want to increase the height of the upper orange panel. It seems to have to. It’s good that they didn’t increase it at that time, otherwise now there would be aduha in general. In general, I’m going to select one line for each button. Ufff, that's it. As for the turned off photos in the menu, I'm still not sure. Alternatively, you can show only half the pizza photo instead of a whole circle, but we have straight half pizza on the menu, so it won’t work, we can confuse users. Let's compare the first approach with the final result: Now let's compare the “before” and “after” with the simulation of poor vision:

    Do not be afraid to change the interface and controls. There is nothing wrong with someone seeing another button or, for example, a slider. And it is not fatal if someone does not see something or if the title is different.
    But UITabBarControllerwe didn’t touch it, because with a large text size, it “out of the box” can show the icon and the title of the tab in a large tapu in the same way as iOS shows the change in volume.

    We show how it all works inside

    Each logical UI component in the Dodo Pizza iOS application is highlighted in a separate one UIViewController. Each such controller has a separate file UIView. You can read more about this in our articles: 

    Controller, take it easy! We take out the code in the UIView Onion
    controller. We split screens into parts.

    Removing logical UI components into a separate UIViewControllerone has greatly simplified the task of modifying interfaces to different states. We recommend that you try this approach, even if you do not plan to add Dynamic Type support - it’s easier to control the status of screens: respond to changes in authorization, rights, roles, and so on.

    So here. We add an extra layer between such a UI component and its parent container. We have it called StateViewController.

    The controller with the menu integrates the state controller, and it already integrates into it collection- or the buttoncontroller.

    This one StateViewControllershows a particular UI component depending on the situation.

    To do this, I StateViewControllerneed to know about my states and switch them as necessary.

    In this example, it StateViewControllerwill switch the selection of categories in the menu from the collection to the button and vice versa. And in the case of a “normal” display, and in the case of a display for visually impaired people, the selector should be able to do the same things:

    • Show list of categories.
    • Highlight the selected category.
    • Update category list.
    • Report that the category "got out."

    Feel this wonderful smell of fresh little logs? And, no, this is a team of mobile api delivered pizza. 5 minutes break.

    2 slices later
    “... Well, we wrap our components like that to select categories in the protocols, AND THEY'RE IMMEDIATELY!”
    Hint: Launch the Accessibility Inspector to easily check how the interface responds to changes in the dynamic tile settings. To do this, in the open Xcode, click Xcode → Open Developer Tool → Accessibility Inspector, select the simulator in the device and go to the last tab

    Another hint: take out the dynamo taip control on the iPhone (not on the simulator) to the Control Center to easily and quickly change the text size. To do this, on an iPhone, go to Settings → Control Center → Customize Controls and add Text Size.

    We called the usual sample of the category CategoriesCollectionViewController, and for the visually impaired - CategoriesButtonViewController. The protocol common to them is named CategoriesPickerProtocol. Common state controller CategoriesStateViewController.

    We describe in our CategoriesStateViewControllerpossible states:

    private enum State {
        case collection, button

    We teach him to show the desired controller for each state:

    private var state: State = .collection {
        didSet {
            if state != oldValue {
                updateViewController(for: state)
    private func updateViewController(for state: State) {
        let viewController = self.viewController(for: state)
        self.updateController(with: viewController)
    private func viewController(for state: State) {
        switch state {
        case .collection:
            return CategoriesCollectionViewController.instantiateFromStoryboard()
        case .button:
            return CategoriesButtonViewController.instantiateFromStoryboard()

    instantiateFromStoryboard()- the method from the self-written extension to the view controller creates a controller instance from storyboards if they have the same name. The code is in the source code at the end of the article.

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    private func updateStateToCurrentContentSize() {
        let contentSize = self.traitCollection.preferredContentSizeCategory
        self.updateState(to: contentSize)
    private func updateState(to contentSize: UIContentSizeCategory) {
        self.state = contentSize.isAccessibilityCategory ? .button : .collection

    We describe the protocol CategoriesPickerProtocol, simultaneously adding two more protocols: for the delegate and for the datasuret.

    protocol CategoriesPickerProtocol where Self: UIViewController {
        var datasource: CategoriesDatasource? { get set }
        var delegate: CategoriesDelegate? { get set }
        func select(_ category: ProductCategoryModule.ProductCategoryViewModel)
        func updateCategories()
        var selectedCategory: ProductCategoryModule.ProductCategoryViewModel? { get }
    protocol CategoriesDatasource: class {
        var categories: [ProductCategoryModule.ProductCategoryViewModel] { get }
        func index(of category: Product.ProductCategory) -> Int
    protocol CategoriesDelegate: class {
        func productCategoriesView(_ categoriesPicker: CategoriesPickerProtocol, didSelect category: ProductCategoryModule.ProductCategoryViewModel)

    There is no sense in showing the implementation, it’s just that every piker displays the categories and reports upward change.

    A detailed example of using state controllers for dynamic taype can be found in my repo on GitHub .

    By the way, we are expanding

    Also popular now: