Создание и отображение ViewController в Swift: полное руководство
Основные способы создания и отображения ViewController в Swift
Проблема новичков в том, что они не учитывают разницу между инициализацией из кода и из Storyboard. Рассмотрим оба варианта.
1. Программное создание (без Storyboard)
Чтобы создать ViewController из кода, нужно убедиться, что у него есть правильный init. Если ваш класс наследуется от UIViewController, используйте:
// Создаем экземпляр
let vc = MyViewController()
// Настраиваем модальное представление (опционально)
vc.modalPresentationStyle = .fullScreen
// Показываем
self.present(vc, animated: true, completion: nil)
Важно: Если ваш ViewController имеет кастомный инициализатор или загружается из XIB, нужно вызывать соответствующий init:
// Из XIB с тем же именем
let vc = MyViewController(nibName: "MyViewController", bundle: nil)
// Или если XIB называется иначе
let vc = MyViewController(nibName: "CustomNibName", bundle: nil)
2. Создание через Storyboard
Для работы со Storyboard нужно:
- Установить Storyboard ID в Identity Inspector (например, "MyVC")
- Использовать
instantiateViewController(withIdentifier:)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MyVC")
self.present(vc, animated: true, completion: nil)
Ошибка "identifier not found" означает, что вы не задали Storyboard ID. Проверьте: откройте Storyboard, выберите ViewController, в Identity Inspector (правая панель) в поле "Storyboard ID" введите "MyVC".
3. Использование Navigation Controller
Если вам нужно навигироваться с кнопкой "Назад", лучше использовать UINavigationController:
// В AppDelegate или сцене
let rootVC = ViewController()
let navController = UINavigationController(rootViewController: rootVC)
window?.rootViewController = navController
// Внутри rootVC для перехода:
let detailVC = DetailViewController()
self.navigationController?.pushViewController(detailVC, animated: true)
4. Передача данных при создании
Чтобы передать данные в новый ViewController, добавьте свойства:
// В классе MyViewController
var userName: String = ""
// При создании:
let vc = MyViewController()
vc.userName = "Иван"
self.present(vc, animated: true)
5. Распространенные ошибки
- Не вызван super.viewDidLoad() — всегда вызывайте его в переопределенных методах.
- Попытка показать VC до завершения viewDidLoad — используйте
viewDidAppearдля первого запуска. - Модальный стиль .pageSheet (по умолчанию) — на больших экранах это выглядит как карточка. Используйте
.fullScreenдля классического вида.
Вот полный рабочий пример для программного подхода:
// MyViewController.swift
import UIKit
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// Добавьте свои элементы UI здесь
}
}
// В текущем ViewController:
@IBAction func showButtonTapped(_ sender: UIButton) {
let vc = MyViewController()
vc.modalPresentationStyle = .fullScreen
present(vc, animated: true)
}
Теперь ваш код должен работать. Главное — помните: для Storyboard нужен ID, для кода — правильный инициализатор.
<<Альтернативный подход: использование замыканий и делегатов
Кроме базового создания, часто требуется передать данные обратно из показанного ViewController. Вот два популярных способа:
1. Через замыкание (Closure)
class DetailViewController: UIViewController {
var onDataBack: ((String) -> Void)?
@IBAction func saveButtonTapped() {
onDataBack?("Данные из Detail")
dismiss(animated: true)
}
}
// В родительском VC:
let detailVC = DetailViewController()
detailVC.onDataBack = { data in
print("Получено: \(data)")
}
present(detailVC, animated: true)
2. Через делегат (Protocol)
protocol DetailDelegate: AnyObject {
func didSaveData(_ data: String)
}
class DetailViewController: UIViewController {
weak var delegate: DetailDelegate?
@IBAction func save() {
delegate?.didSaveData("Данные")
dismiss(animated: true)
}
}
// В родительском VC:
class MainVC: UIViewController, DetailDelegate {
func showDetail() {
let detailVC = DetailViewController()
detailVC.delegate = self
present(detailVC, animated: true)
}
func didSaveData(_ data: String) {
print("Делегат получил: \(data)")
}
}
Оба подхода работают отлично. Замыкания проще для одноразовых действий, делегаты — для сложной логики. Выбирайте по ситуации!