Avatar ·

Создание и отображение ViewController в Swift: полное руководство

Всем привет! Я новичок в Swift и iOS-разработке, перехожу с Objective-C. У меня возникла проблема с созданием и отображением нового ViewController программно (без Storyboard). В Objective-C я просто делал: ```objc MyViewController *vc = [[MyViewController alloc] init]; [self presentViewController:vc animated:YES completion:nil]; ``` Но в Swift это не работает так же просто. Я пробовал: ```swift let vc = MyViewController() self.present(vc, animated: true, completion: nil) ``` Но ничего не происходит — экран просто не отображается. Я читал, что нужно использовать Storyboard или XIB, но мне нужно сделать всё программно. Также пробовал: ```swift let storyboard = UIStoryboard(name: "Main", bundle: nil) let vc = storyboard.instantiateViewController(withIdentifier: "MyVC") self.present(vc, animated: true) ``` Но получаю ошибку, что identifier не найден. Подскажите, как правильно создать и показать ViewController в Swift? Может быть, я что-то забываю? Я использую Xcode 14 и iOS 16. Буду благодарен за пример кода с пояснениями. <<>>

Основные способы создания и отображения 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)")
    }
}

Оба подхода работают отлично. Замыкания проще для одноразовых действий, делегаты — для сложной логики. Выбирайте по ситуации!