Уволить более одного контроллера одновременно
Я делаю игру, используя SpriteKit. У меня есть 3 viewControllers: выбор уровня vc, игры vc и win vc. После того, как игра окончена, я хочу показать win vc, затем, если я нажму кнопку OK на win vc, я хочу отклонить win vc и игру vc (вытолкнуть из стека два контроллера вида). Но я не знаю, как это сделать, потому что если я позвоню
self.dismissViewControllerAnimated(true, completion: {})
win vc (вершина стека) отбрасывается, поэтому я не знаю, где его снова вызвать, чтобы закрыть vc игры. Можно ли как-то исправить это без использования контроллера навигации?
Это 1-й ВК: (Пожалуйста, обратите внимание на мои комментарии ниже, начиная с "//")
class SelectLevelViewController: UIViewController { // I implemented a UIButton on its storyboard, and its segue shows GameViewController
override func viewDidLoad() {
super.viewDidLoad()
}
}
Это второй ВК:
class GameViewController: UIViewController, UIPopoverPresentationControllerDelegate {
var scene: GameScene!
var stage: Stage!
var startTime = NSTimeInterval()
var timer = NSTimer()
var seconds: Double = 0
var timeStopped = false
var score = 0
@IBOutlet weak var targetLabel: UILabel!
@IBOutlet var displayTimeLabel: UILabel!
@IBOutlet weak var scoreLabel: UILabel!
@IBOutlet weak var gameOverPanel: UIImageView!
@IBOutlet weak var shuffleButton: UIButton!
@IBOutlet weak var msNum: UILabel!
var mapNum = Int()
var stageNum = Int()
var tapGestureRecognizer: UITapGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as! SKView
skView.multipleTouchEnabled = false
scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .AspectFill
msNum.text = "\(mapNum) - \(stageNum)"
stage = Stage(filename: "Map_0_Stage_1")
scene.stage = stage
scene.addTiles()
scene.swipeHandler = handleSwipe
gameOverPanel.hidden = true
shuffleButton.hidden = true
skView.presentScene(scene)
Sound.backgroundMusic.play()
beginGame()
}
func beginGame() {
displayTimeLabel.text = String(format: "%ld", stage.maximumTime)
score = 0
updateLabels()
stage.resetComboMultiplier()
scene.animateBeginGame() {
self.shuffleButton.hidden = false
}
shuffle()
startTiming()
}
func showWin() {
gameOverPanel.hidden = false
scene.userInteractionEnabled = false
shuffleButton.hidden = true
scene.animateGameOver() {
self.tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "hideWin")
self.view.addGestureRecognizer(self.tapGestureRecognizer)
}
}
func hideWin() {
view.removeGestureRecognizer(tapGestureRecognizer)
tapGestureRecognizer = nil
gameOverPanel.hidden = true
scene.userInteractionEnabled = true
self.performSegueWithIdentifier("win", sender: self) // this segue shows WinVC but idk where to dismiss this GameVC after WinVC gets dismissed...
}
func shuffle() {...}
func startTiming() {...}
}
И это 3-й ВК:
class WinVC: UIViewController {
@IBOutlet weak var awardResult: UILabel!
@IBAction func dismissVC(sender: UIButton) {
self.dismissViewControllerAnimated(true, completion: {}) // dismissing WinVC here when this button is clicked
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
10 ответов
Комментарий @Ken Toh был тем, что сработало для меня в этой ситуации - вызовите dismiss от контроллера представления, который вы хотите показать после того, как все остальное будет отклонено.
Если у вас есть "стек" из 3 представленных контроллеров представления A
, B
а также C
, где C
на вершине, то звонит A.dismiss(animated: true, completion: nil)
уволит B и C одновременно.
Если у вас нет ссылки на корень стека, вы можете связать пару обращений к presentingViewController
чтобы добраться до него. Что-то вроде этого:
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
Вы можете отклонить текущий контроллер WinVC (GameViewController) в блоке завершения:
let presentingViewController = self.presentingViewController
self.dismissViewControllerAnimated(false, completion: {
presentingViewController?.dismissViewControllerAnimated(true, completion: {})
})
В качестве альтернативы, вы можете обратиться к корневому контроллеру представления и вызвать dismissViewControllerAnimated, который отклонит оба модальных контроллера представления в одной анимации:
self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: {})
Swift 5 (и, возможно, 4, 3 и т. Д.)
presentingViewController?.presentingViewController?
не очень элегантно и не работает в некоторых случаях. Вместо этого используйте segues
,
Допустим, у нас есть ViewControllerA
, ViewControllerB
, а также ViewControllerC
, Мы в ViewControllerC
(мы приземлились здесь через ViewControllerA
-> ViewControllerB
так что если мы сделаем dismiss
мы вернемся к ViewControllerB
). Мы хотим от ViewControllerC
прыгнуть прямо к ViewControllerA
,
В ViewControllerA
добавьте следующее действие в ваш класс ViewController:
@IBAction func unwindToViewControllerA(segue: UIStoryboardSegue) {}
Да, эта строка идет в ViewController ViewController, к которому вы хотите вернуться!
Теперь вам нужно создать выход из ViewControllerC
раскадровка (StoryboardC
). Иди и открой StoryboardC
и выберите раскадровку. Удерживая клавишу CTRL, перетащите для выхода следующим образом:
Вам будет предоставлен список сегментов на выбор, включая тот, который мы только что создали:
Теперь у вас должен быть переход, нажмите на него:
Зайдите в инспектор и установите уникальный идентификатор:
в ViewControllerC
в точке, где вы хотите уволить и вернуться к ViewControllerA
выполните следующие действия (с идентификатором, который мы установили в инспекторе ранее):
self.performSegue(withIdentifier: "yourIdHere", sender: self)
Вы должны быть в состоянии позвонить:
self.presentingViewController.dismissViewControllerAnimated(true, completion: {});
(Вам может понадобиться добавить ?
или же !
где-то - я не стремительный разработчик)
У меня возникли проблемы с анимацией при попытке принять ответ в моем приложении. Ранее представленные виды будут мигать или пытаться анимировать на экране. Это было мое решение:
if let first = presentingViewController,
let second = first.presentingViewController,
let third = second.presentingViewController {
second.view.isHidden = true
first.view.isHidden = true
third.dismiss(animated: true)
}
Есть специальный сценарий, предназначенный для отката стека представлений до определенного контроллера представлений. Пожалуйста, посмотрите мой ответ здесь: как отменить 2 вида контроллера в Swift IOS?
Добавление к ответу Phlippie Bosman при звонке
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
если вы не хотите видеть (что было бы presentingViewController
) вы можете сделать что-то вроде
self.presentingViewController?.view.addSubview(self.view)
Это кажется немного странным, но до сих пор это был единственный способ, которым я смог создать впечатление, что два контроллера представления увольняются в унисон.
Хотя ответ Рафилса приемлемый. Не все используют Segue's.
Для меня лучше всего работает следующее решение
if let viewControllers = self.navigationController?.viewControllers {
let viewControllerArray = viewControllers.filter {
$0 is CustomAViewController || $0 is CustomBViewController }
DispatchQueue.main.async {
self.navigationController?.setViewControllers(viewControllerArray,
animated: true)
}
}
Лучший способ добиться результата OP БЕЗ сбоя анимации (где контроллер промежуточного представления ненадолго отображается во время увольнения) - это встроить контроллер представления A (первый vc) в контроллер навигации, а затем просто поместить строку
Как говорится в Apple Doc:
[Это] обновляет или заменяет текущий стек контроллера представления без явного нажатия или извлечения каждого контроллера. Кроме того, этот метод позволяет обновлять набор контроллеров без анимации изменений.
Другими словами, мы просто избавляемся от контроллера промежуточного представления (невидимого в фоновом режиме), так что простой
Swift 4.0
let presentingViewController = self.presentingViewController
presentingViewController?.presentingViewController?.presentingViewController?.dismiss(animated: false, completion: nil)