Создание UIKit-проекта без использования Storyboard в первый раз кажется чем-то сложным, на самом же деле основные проблемы при попытке запустить проект без сторибордов могут появиться при неправильном установке, удалении ненужных файлов и зависимостей.
Зачем вообще делать проект без Storyboard? Я вижу две причины:
- Более удобная работа с git. Storyboard - такой же текстовый файл, как и другой код, только любое малейшее изменение внутри сториборда приведёт к изменению текста файла, и такие изменения совсем нечитаемые
- Полный контроль над UI. Мы не разделяем настройки UI-элемента на те, что в сториборде, и те, что в коде. Всё находится в одном месте, и это проще контролировать.
Стоит учесть, что несмотря на эти минусы, на всех серьёзных проектах, где я работал, использовали Storyboard, расставляя все элементы, а основная настройка UI была в коде. В каком-то смысле это упрощает жизнь и уменьшает количество кода. Тем не менее, вот как настроить проект на работу с программным UI:
Создадим проект (как ни странно) с Interface: Storyboard
Сразу же удаляем файл Main.storyboard
В настройках Target удалим Main из поля Main Interface, оно остаётся пустым.
В Info.plist удалим всю строчку Application Scene Manifest
SceneDelegate.swift можно удалить (он может понадобиться для определённых целей, но в данном случае он не нужен). Спойлер: до iOS 13 "входом" в приложение был AppDelegate, там находилась логика запуска приложения и инициализация. С iOS13 ответственность разделилась:
- AppDelegate.swift отвечает за жизненный цикл приложения и первоначальные установки
- SceneDelegate.swift отвечает за то, что будет показано на экране
Это вызвано новым функционалом iPadOS и поддержкой мультиэкранов. Подробнее про AppDelegate и SceneDelegate можно прочитать здесь.
Далее изменим два файла:
AppDelegate.swift
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
return true
}
}
ViewController.swift
import UIKit
final class ViewController: UIViewController {
private let someLabel : UILabel = {
let label = UILabel()
label.text = "Label title"
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
setupSubviews()
}
private func setupSubviews() {
view.addSubview(someLabel)
/// Constraints
let margins = view.layoutMarginsGuide
NSLayoutConstraint.activate([
someLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
someLabel.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
someLabel.trailingAnchor.constraint(equalTo: margins.trailingAnchor),
someLabel.widthAnchor.constraint(equalTo: margins.widthAnchor),
])
}
}
Готово, запускаем проект и радуемся.
Для Objective-C принцип такой же, для примера можно использовать этот шаблон.