Создание TabBarController и NavigationController программно в UIKit

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Зачем UIKit в 2022

В начале 2022 года может показаться, что использование UIKit на фоне SwiftUI не актуально или даже старомодно. Но так как еще очень много приложений которые созданы на основе фреймворка UIKit и у нас в России до сих пор на нем создается, то его актуальность еще на довольно солидном уровне.

А еще более важным будет знание, как это работает. Сейчас идет постепенный переход от Storyboard к чистому коду в SwiftUI. Переход медленный и это дает нам возможность, еще пока, учится с помощью картинок и кода создавать приложения и одновременно учится создавать приложения с помощью кода.

Задача

Давайте сразу перейдем к задаче:

  1. Создайте новый проект с названием Navigation, используя шаблон Single View App.

  2. Удалите из проекта Main.storyboard, который создался по умолчанию. Не забудьте изменить конфигурацию в Info.plist.

  3. В SceneDelegate.swift добавьте UITabBarController. Добавьте в него два UINavigationController. Первый будет показывать ленту пользователя, а второй — профиль.

  4. Измените Tab Bar Item у добавленных контроллеров, добавьте заголовок и картинку. Картинки можно добавить в Assets.xcassets или использовать SF Symbols.

  5. Создайте FeedViewController и ProfileViewController и добавьте их как root view controller у навигационных контроллеров.

  6. Добавьте PostViewController для показа выбранного поста. Поменяйте заголовок у контроллера и цвет главной view. Добавьте кнопку на FeedViewController и сделайте переход на экран поста. Контроллер должен показаться в стеке UINavigationController.

  7. Создайте структуру Post со свойством title: String. Создайте объект типа Post в FeedViewController и передайте его в PostViewController. В классе PostViewController выставьте title полученного поста в качестве заголовка контроллера.

  8. На PostViewController добавьте Bar Button Item в навигейшн бар. При нажатии на него должен открываться новый контроллер InfoViewController. Контроллер должен показаться модально.

  9. На InfoViewController создайте кнопку. При нажатии на неё должен показаться UIAlertController с заданным titlemessage и двумя UIAlertAction. При нажатии на UIAlertAction в консоль должно выводиться сообщение.

В рамках данной статьи мы не будем рассматривать пункты 1 и 2. Не в смысле их примитивности, а потому-что подобными ответами пестрит интернет. Замечу только, что после удаления не забудьте вернуть в SceneDelegate (если вы еще используете Xcode до 13 версии, то AppDelegate), строку, которая удаляется автоматически с Main.storyboard.

var window: UIWindow?

Реализация

В данной статье мы рассмотрим пункты 3, 4, 5 и 6. Остальны в последующих статьях. Приступим!

Для реализации пункта 2 существуют 2 способа:

  1. Релизация UITabBarController внутри SceneDelegate.swift

  2. Создание собственного UITabBarController.swift и в него инкапсулировать два UINavigationController

Релизация UITabBarController внутри SceneDelegate.swift

Перейдем в SceneDelegate.swift и первым делом заменим ViewController на TabBarController

window = UIWindow(frame: windowScene.coordinateSpace.bounds)

window?.windowScene = windowScene

window?.rootViewController = UITabBarController()

window?.makeKeyAndVisible()

Теперь можно удалить сам контроллер просмотра ViewController.swift. А сейчас мы пойдем от обратного. Наш TabBarController будет удерживать навигационные контроллеры и для того, чтобы TabBarController что-то отобразил, нам придется сначала создать навигационные контроллеры. Приступим.

Создаем 2 ViewController (cmd⌘ + N -> Cocoa Touch Class), где Class: у нас будут FeedViewController и ProfileViewController, а Subclass of: UIViewController соответственно. С самого начала контроллеры прозрачные и для того, чтобы мы могли понять, что они у нас есть, мы зададим им фоновый цвет:

override func viewDidLoad() {

        super.viewDidLoad()

        // Задаем фоновый цвет

        view.backgroundColor = .lightGray

}

Конечно для фона лучше использовать системные цвета (например: .systemBlue). Apple адаптировала системные цвета к темному и светлому режижам работы в отличии от простых цветов.

В качестве эксперимента давай зададим нашим ViewController разные цвета.

Теперь давай создадим два навигационных контроллераr и добавимFeedViewController и ProfileViewController и как root view controller. В SceneDelegate.swift перед window = UIWindow(frame: windowScene.coordinateSpace.bounds создаем навигационные контроллеры

let feedViewController = UINavigationController(rootViewController: FeedViewController())

let profileViewController = UINavigationController(rootViewController: profileViewController())

Теперь помещаем навигационные контроллеры в панель вкладок пользовательского интерфейса. Создаем панель вкладок:

let tabBarController = UITabBarController()

Помещаем в нее навигационнные контроллеры:

tabBarController.viewControllers = [feedViewController, profileViewController]

Не забудь поменять UITabBarController() на пользовательский tabBarController в

window?.rootViewController = UITabBarController()

Теперь когда мы запустим наш проект, мы увидим что экран отображает заданный цвет и если пощелкать внизу справа и слева, то даже будут переключаться экраны. Хотя наш TabBar и не отображает ничего. Это говорит, что у нас все прекрасно работает.

Давай теперь настроим наш TabBar, чтобы не играть в игру: Куда я нажимаю?

Настройка TabBar

Давай сделаем наш код красивым и не будем все собирать в кучу, а разделим каждый блок в методы.

В первый метод соберем наш FeedViewController, во второй ProfileViewController, ну а в третий TabBarController. Реализация методов в SceneDelegate.swift станет выполнением пункда 6 нашего задания, мы добавим и иконки и заголовки.

Создаем фукцию контроллера поисковой навигации, который возвращает контроллер пользовательского интерфейса:

func createFeedViewController() -> UINavigationController {

Инициализируем поисковый контроллер, который заменим созданный выше

let feedViewController = FeedViewController()

Добавляем заголовок, который отобразится вверху нашего экрана

feedViewController.title = "Лента"

А теперь настроим и саму кнопку, добавив на нее иконку и название. Создаем элемент панели вкладок пользовательского интерфейса UITabBarIt, где title: это заголовок, а image: в нашем случае системная иконка (системные иконки можно найти в программе "Символы SF" или в самом Xcode Edit -> Emoji & Symbols) и последнее tag:это индекс положения, где 0 - положение слева, а 1 справа.

feedViewController.tabBarItem = UITabBarItem(title: "Лента", image: UIImage(systemName: "doc.richtext"), tag: 0)

Возвращаем навигатор пользовательского интерфейса

return UINavigationController(rootViewController: feedViewController)

}

Делаем тоже самое для навигационного контроллера ProfileViewController

 func createProfileViewController() -> UINavigationController {

let profileViewController = ProfileViewController()

profileViewController.title = "Профиль"

profileViewController.tabBarItem = UITabBarItem(title: "Профиль", image: UIImage(systemName: "person.circle"), tag: 1)

return UINavigationController(rootViewController: profileViewController)

}

Теперь удалим, за ненадобностью вверху:

let feedViewController = UINavigationController(rootViewController: FeedViewController())

let profileViewController = UINavigationController(rootViewController: profileViewController())

Теперь давай создадим метод для панели вкладок и передадим эту функцию корневому представлению

func createTabBarController() -> UITabBarController {

Удали сверху и вставь в метод строки кода

let tabBarController = UITabBarController()

Настроим внешний вид панели вкладок и установим основной цвет синий

UITabBar.appearance().backgroundColor = .systemBlue

Поменяем наши контроллеры на методы созданные чуть выше

tabBarController.viewControllers = [createFeedViewController(), createProfileViewController()]

Возвращаем панель вкладок

return tabBarController

}

Не забудь поменять tabBarController() созданный метод createTabBarController() в

window?.rootViewController = tabBarController

Теперь можещь запустить созданный проект и посмотреть на ту красоту, которую мы создали. Благодаря методам мы сделали красивый и хорошо пахнущий код, который не засоряет собою SceneDelegate.swift

Давай перейдем ко второму методу.

Создание собственного UITabBarController.swift и в него инкапсулировать два UINavigationController

Кому-то может не понравится способ создания кода в SceneDelegate.swift и это тоже хорошо и по-этому мы рассмотрим второй способ.

Во-первых, мы создаем свой TabBarController

final class TabBarController: UITabBarController {

В него мы инкапсулируем FeedViewController, во второй ProfileViewControlle. Мы можен инкапсулировать больше двух контроллеров, то есть сколько айтемов, столько и кейсов. Создаем перечисление TabBarItem с описанием наших контроллеров

private enum TabBarItem: Int {

        case feed

        case profile

        Добавим заголовки

        var title: String {

            switch self {

            case .feed:

                return "Лента"

            case .profile:

                return "Профиль"

            }

        }

        Добавим иконки

        var iconName: String {

            switch self {

            case .feed:

                return "house"

            case .profile:

                return "person.crop.circle"

            }

        }

    }

Теперь загрузим это все в TabBarController

override func viewDidLoad() {

        super.viewDidLoad()

        self.setupTabBar()

    }

В методе мы создадим создадим контроллеры FeedViewController и ProfileViewControlle для TabBarController. Не забудьте как и в первом способе создать сами swift файлы!

private func setupTabBar() {

        let dataSource: [TabBarItem] = [.feed, .profile]

        self.viewControllers = dataSource.map {

            switch $0 {

            case .feed:

                let feedViewController = FeedViewController()

                return self.wrappedInNavigationController(with: feedViewController, title: $0.title)

            case .profile:

                let profileViewController = ProfileViewController()

                return self.wrappedInNavigationController(with: profileViewController, title: $0.title)

            }

        }

        self.viewControllers?.enumerated().forEach {

            $1.tabBarItem.title = dataSource[$0].title

            $1.tabBarItem.image = UIImage(systemName: dataSource[$0].iconName)

            $1.tabBarItem.imageInsets = UIEdgeInsets(top: 5, left: .zero, bottom: -5, right: .zero)

        }

    }

Ну и в самом конце методом wrappedInNavigationController(with: profileViewController, title: $0.title) мы обернем переданный контроллер FeedViewController или ProfileViewControlleв NavigationController

private func wrappedInNavigationController(with: UIViewController, title: Any?) -> UINavigationController {

        self.navigationController?.pushViewController(with, animated: true)

        return UINavigationController(rootViewController: with)

    }

Второй способ позволит нам оставить чистым SceneDelegate.swift

  guard let windowScene = (scene as? UIWindowScene) else { return }

        self.window = UIWindow(windowScene: windowScene)

        self.window?.makeKeyAndVisible()

        self.window?.rootViewController = TabBarController()

        

В следующей статье мы рассмотрим оставшиеся пункты, а так же программный способ создания интерфейса.

Источник: https://habr.com/ru/post/652543/


Интересные статьи

Интересные статьи

Stack Overflow побеседовал с Эрин Ишимотича, инженером в группе Software Delivery Engineering из Чокто Нейшн в Оклахоме. Ишимотича, работающая инженером на постоянной основе уже 15 лет, начала сво...
Вы разрабатываете функцию для веб-сайта. Пусть это простая форма входа в систему. Поскольку вы превосходный разработчик, то решили провести базовое планирование, прежде ч...
Группы доступности AlwaysOn — мощное решение, дающее возможность для администраторов баз данных, реализовать очень высокий уровень доступности (HA, high availability) с п...
В обновлении «Сидней» Битрикс выпустил новый продукт в составе Битрикс24: магазины. Теперь в любом портале можно создать не только лендинг или многостраничный сайт, но даже интернет-магазин. С корзино...
Изучение глифов на терминалах DEC VT100 и VT220 Недавно я немного увлёкся эмуляцией аналоговых медиа: хотелось воссоздать растровую графику ЭЛТ, как на «стеклянных терминалах» прошлого, таких...