Зачем UIKit в 2022
В начале 2022 года может показаться, что использование UIKit на фоне SwiftUI не актуально или даже старомодно. Но так как еще очень много приложений которые созданы на основе фреймворка UIKit и у нас в России до сих пор на нем создается, то его актуальность еще на довольно солидном уровне.
А еще более важным будет знание, как это работает. Сейчас идет постепенный переход от Storyboard к чистому коду в SwiftUI. Переход медленный и это дает нам возможность, еще пока, учится с помощью картинок и кода создавать приложения и одновременно учится создавать приложения с помощью кода.
Задача
Давайте сразу перейдем к задаче:
Создайте новый проект с названием
Navigation, используя шаблонSingle View App.Удалите из проекта
Main.storyboard, который создался по умолчанию. Не забудьте изменить конфигурацию вInfo.plist.В
SceneDelegate.swiftдобавьтеUITabBarController. Добавьте в него дваUINavigationController. Первый будет показывать ленту пользователя, а второй — профиль.Измените
Tab Bar Itemу добавленных контроллеров, добавьте заголовок и картинку. Картинки можно добавить вAssets.xcassetsили использовать SF Symbols.Создайте
FeedViewControllerиProfileViewControllerи добавьте их как root view controller у навигационных контроллеров.Добавьте
PostViewControllerдля показа выбранного поста. Поменяйте заголовок у контроллера и цвет главной view. Добавьте кнопку наFeedViewControllerи сделайте переход на экран поста. Контроллер должен показаться в стекеUINavigationController.Создайте структуру
Postсо свойствомtitle: String. Создайте объект типаPostвFeedViewControllerи передайте его вPostViewController. В классеPostViewControllerвыставьтеtitleполученного поста в качестве заголовка контроллера.На
PostViewControllerдобавьтеBar Button Itemв навигейшн бар. При нажатии на него должен открываться новый контроллерInfoViewController. Контроллер должен показаться модально.На
InfoViewControllerсоздайте кнопку. При нажатии на неё должен показатьсяUIAlertControllerс заданнымtitle,messageи двумяUIAlertAction. При нажатии наUIAlertActionв консоль должно выводиться сообщение.
В рамках данной статьи мы не будем рассматривать пункты 1 и 2. Не в смысле их примитивности, а потому-что подобными ответами пестрит интернет. Замечу только, что после удаления не забудьте вернуть в SceneDelegate (если вы еще используете Xcode до 13 версии, то AppDelegate), строку, которая удаляется автоматически с Main.storyboard.
<source>var window: UIWindow<source>
Реализация
В данной статье мы рассмотрим пункты 3, 4, 5 и 6. Остальны в последующих статьях. Приступим!
Для реализации пункта 2 существуют 2 способа:
Релизация
UITabBarControllerвнутриSceneDelegate.swiftСоздание собственного
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 {
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()
В следующей статье мы рассмотрим оставшиеся пункты, а так же программный способ создания интерфейса.
Комментарии (3)

2Grey
21.02.2022 10:45Зачем UIKit в 2022?
Потому что SwiftUI все ещё сырой и для него нужно придумывать свои костыли, чтобы решать тривиальные задачи: те же пикеры и алерты.

MaksMai Автор
23.02.2022 09:24Инвестируйте не в поиск будущей работы, а в себя.
Изучая сейчас UIKit вы значительно лучше будете понимать, как работает система в целом, изучая “ванильный” SwiftUI, эти знания будут даваться несколько сложнее.
Изучая UIKit вы повышаете свой профессиональный уровень, а именно профессионалы нужны рынку.
alnite
А зачем переходитьтна SwiftUI, если UIKit всё еще эффективно работает?