Как программно вернуться на предыдущий ViewController в Swift? (UINavigationController, unwind segue)
Основные способы программного возврата на предыдущий ViewController
Проблема, с которой вы столкнулись, типична для начинающих разработчиков. Главное — понять, как именно был открыт ваш второй экран. В iOS есть два основных механизма навигации: модальное представление (present/dismiss) и навигационный стек (push/pop). Рассмотрим оба случая.
1. Если вы используете UINavigationController (Show/Push segue)
В этом случае правильный способ — использовать метод popViewController(animated:). Он удаляет текущий ViewController из навигационного стека и возвращает к предыдущему.
@IBAction func backButtonTapped(_ sender: UIButton) {
// Проверяем, что navigationController существует
guard let navController = self.navigationController else {
print("Ошибка: ViewController не находится в UINavigationController")
return
}
navController.popViewController(animated: true)
}
Важно: если вы используете Show segue из Storyboard, то ваш второй экран автоматически помещается в стек UINavigationController. Метод dismiss здесь не сработает, потому что экран не был представлен модально.
2. Если вы используете модальный переход (Present modally)
В этом случае экран был представлен с помощью present(_:animated:completion:). Тогда для возврата нужно использовать dismiss(animated:completion:):
@IBAction func backButtonTapped(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
При этом dismiss должен вызываться на том ViewController, который был представлен (или на его parent-контроллере).
3. Универсальное решение
Если вы не уверены, какой способ используется, можно написать универсальный код, который проверяет оба варианта:
@IBAction func backButtonTapped(_ sender: UIButton) {
if let navController = self.navigationController {
// Если есть navigationController — используем pop
navController.popViewController(animated: true)
} else {
// Иначе — dismiss (модальное представление)
self.dismiss(animated: true, completion: nil)
}
}
4. Unwind Segue (возврат через Storyboard)
Unwind segue — это мощный механизм, который позволяет вернуться к любому предку в цепочке навигации. Для его использования:
- В первом ViewController (куда хотите вернуться) объявите метод с атрибутом
@IBActionи параметром типаUIStoryboardSegue:
@IBAction func unwindToFirstViewController(_ segue: UIStoryboardSegue) {
// Здесь можно обработать данные, переданные обратно
if let sourceVC = segue.source as? SecondViewController {
print("Получены данные: \(sourceVC.someData)")
}
}
- В Storyboard создайте unwind segue: перетащите от кнопки "Назад" к значку "Exit" (зелёный квадрат с стрелкой) в вашем втором ViewController и выберите созданный метод.
- В коде кнопки просто вызовите
performSegue(withIdentifier:sender:):
@IBAction func backButtonTapped(_ sender: UIButton) {
self.performSegue(withIdentifier: "unwindToFirst", sender: self)
}
Unwind segue автоматически находит правильный ViewController в стеке и выполняет переход. Это особенно удобно, когда у вас сложная иерархия экранов.
5. Распространённые ошибки и их решение
- Приложение падает с ошибкой: проверьте, что ваш ViewController действительно находится в UINavigationController. Если вы создали его напрямую через Storyboard без встраивания в Navigation Controller, то navigationController будет nil.
- Ничего не происходит: убедитесь, что вы не вызываете dismiss на уже отпущенном контроллере. Используйте guard для проверки.
- Использование dismiss вместо pop: если вы использовали Show segue, dismiss не сработает, так как экран не был представлен модально.
Рекомендую для простых случаев использовать popViewController (если есть NavigationController) или dismiss (для модальных окон). Для сложной навигации — unwind segues.
Альтернативный подход: использование делегата или замыкания для передачи данных назад
Часто при возврате на предыдущий экран нужно передать какие-то данные (например, выбранный элемент). Вместо того чтобы полагаться только на механизм навигации, можно использовать паттерн делегирования или замыкания (closures).
Пример с замыканием:
class SecondViewController: UIViewController {
var onBack: ((String) -> Void)? // Замыкание для передачи данных
@IBAction func backButtonTapped(_ sender: UIButton) {
// Передаём данные и возвращаемся назад
onBack?("Данные с второго экрана")
self.navigationController?.popViewController(animated: true)
}
}
// В первом ViewController:
class FirstViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondVC = segue.destination as? SecondViewController {
secondVC.onBack = { [weak self] data in
self?.receivedData = data
self?.tableView.reloadData()
}
}
}
}
Этот подход особенно полезен, когда нужно обновить данные на первом экране после возврата. Он работает как с push, так и с модальными переходами. Главное — не забыть использовать [weak self] в замыкании, чтобы избежать утечек памяти.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги