Navigation history stack (iOS 14)

September 23, 2020

Одно из незначительных, но полезных нововведений - "история" в навигации. Чтобы вернуться на начальный экран Navigation Controller раньше приходился нажимать "назад" столько раз, сколько потребуется, но в iOS 14 есть возможность выбрать определённый экран в цепочке, по которой мы пришли к текущему, а также вернуться на первоначальный экран одним движением.

На первый взгляд полезная штука, про которую первое время придётся вспоминать, однако есть одна проблема. Современные дизайны часто используют кнопку "назад" без текста, из-за чего меню с историей выглядит пустым.

Как это работает?

В меню появляются navigationItem.title того экрана, за который отвечает этот пункт меню. Однако это название можно изменить, если устанавливать title для кнопки "назад". В таком случае в меню будет показываться именно он, и можно обойтись даже с пустым тайтлом вью.

class MyViewController: UIViewController {

    override func viewDidLoad() {

        title = ""

        navigationItem.backButtonTitle = "Custom back title"

    }

}

Однако, если для кнопки назад установлен пустой тайтл, результат будет как на первом скриншоте. У нас есть два пути:

  • Отключить показ этого меню, пока не выйдет обновление, в котором это меню можно будет кастомизировать, например.
  • Сделать текст кнопки "назад" бесцветным

В первом случае нужно сделать сабкласс UIBarButtonItem, в котором меню просто не будет устанавливаться:

extension UIViewController {

    func setupEmptyBackButton() {

        let backButtonItem = BackBarButtonItem(title: "",

                                               style: .plain,

                                               target: nil,

                                               action: nil)

        navigationItem.backBarButtonItem = backButtonItem

    }

}

class BackBarButtonItem: UIBarButtonItem {

    @available(iOS 14.0, *)

    override var menu: UIMenu? {

        set {

//            super.menu = menu

        }

        get {

            return super.menu

        }

    }

}

Во втором случае нужно проверять iOS и устанавливать цвет для iOS > 14. Для предыдущих версий происходит обычная установка кнопки с пустым тайтлом:

extension UIViewController {

    func setupEmptyBackButton() {

        if #available(iOS 14.0, *) {

            let appearance = UINavigationBarAppearance()

            appearance.configureWithDefaultBackground()

            appearance.backButtonAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.clear]

            UINavigationBar.appearance().standardAppearance = appearance

            UINavigationBar.appearance().compactAppearance = appearance

        } else {

            navigationItem.backBarButtonItem = UIBarButtonItem(title: "",

                                                               style: .plain,

                                                               target: nil,

                                                               action: nil)

        }

    }

}

В обоих случаях я делаю это в отдельном методе UIViewController, хотя есть и другие пути.

В итоге получаем меню и кнопку, соответствующую дизайну: