Je fais un jeu en utilisant SpriteKit. J'ai 3 viewControllers: sélectionner le niveau vc, le jeu vc et gagner vc. Une fois le jeu terminé, je veux montrer le win vc, puis si j'appuie sur le bouton OK du win vc, je veux rejeter le win vc ET le jeu vc (sortir deux contrôleurs de vue de la pile). Mais je ne sais pas comment faire parce que si j'appelle
self.dismissViewControllerAnimated(true, completion: {})
le win vc (haut de la pile) est rejeté, donc je ne sais pas où l'appeler à nouveau pour rejeter le jeu vc. Existe-t-il un moyen de résoudre ce problème sans utiliser le contrôleur de navigation?
Ceci est le 1er VC: (Veuillez faire attention à mes commentaires ci-dessous commençant par "//")
class SelectLevelViewController: UIViewController { // I implemented a UIButton on its storyboard, and its segue shows GameViewController
override func viewDidLoad() {
super.viewDidLoad()
}
}
C'est le 2ème VC:
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() {...}
}
Et ceci est le 3e VC:
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()
}
}
Le commentaire de @Ken Toh a été ce qui a fonctionné pour moi dans cette situation - appelez le renvoi du contrôleur de vue que vous souhaitez afficher après que tout le reste soit rejeté.
Si vous avez une "pile" de 3 contrôleurs de vue présentés A
, B
et C
, où C
est en haut, puis appelez A.dismiss(animated: true, completion: nil)
rejettera B et C simultanément.
Si vous n'avez pas de référence à la racine de la pile, vous pouvez chaîner quelques accès à presentingViewController
pour y accéder. Quelque chose comme ça:
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
Vous pouvez ignorer le contrôleur de présentation de WinVC (GameViewController) dans le bloc d'achèvement:
let presentingViewController = self.presentingViewController
self.dismissViewControllerAnimated(false, completion: {
presentingViewController?.dismissViewControllerAnimated(true, completion: {})
})
Alternativement, vous pouvez contacter le contrôleur de vue racine et appeler rejeterViewControllerAnimated, qui rejettera les deux contrôleurs de vue modaux dans une seule animation:
self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: {})
Vous devriez pouvoir appeler:
self.presentingViewController.dismissViewControllerAnimated(true, completion: {});
(Vous devrez peut-être ajouter ?
ou !
quelque part - je ne suis pas un développeur Swift)
Il y a un enchaînement de déroulement spécial destiné à restaurer la pile de vues sur certains contrôleurs de vue. S'il vous plaît voir ma réponse ici: comment rejeter 2 contrôleur de vue dans Swift ios?
J'ai rencontré des problèmes d'animation lors de la tentative de réponse acceptée dans ma candidature. Les vues précédemment présentées clignotaient ou tentaient de s'animer à l'écran. C'était ma solution:
if let first = presentingViewController,
let second = first.presentingViewController,
let third = second.presentingViewController {
second.view.isHidden = true
first.view.isHidden = true
third.dismiss(animated: true)
}
Swift 5 (et éventuellement 4, 3 etc)
presentingViewController?.presentingViewController?
n'est pas très élégant et ne fonctionne pas dans certains cas. Utilisez plutôt segues
.
Disons que nous avons ViewControllerA
, ViewControllerB
et ViewControllerC
. Nous sommes à ViewControllerC
(nous avons atterri ici par ViewControllerA
-> ViewControllerB
, donc si nous faisons dismiss
nous reviendrons à ViewControllerB
). Nous voulons que ViewControllerC
retourne directement à ViewControllerA
.
Dans ViewControllerA
ajoutez l'action suivante dans votre classe ViewController:
@IBAction func unwindToViewControllerA(segue: UIStoryboardSegue) {}
Oui, cette ligne va dans le ViewController du ViewController auquel vous voulez revenir!
Maintenant, vous devez créer une séquence de sortie à partir du storyboard de ViewControllerC
(StoryboardC
). Allez-y et ouvrez StoryboardC
et sélectionnez le storyboard. Maintenez CTRL enfoncé et faites glisser pour quitter comme suit:
Vous recevrez une liste de séquences à choisir, y compris celle que nous venons de créer:
Vous devriez maintenant avoir une séquence, cliquez dessus:
Allez dans l'inspecteur et définissez un identifiant unique:
Dans le ViewControllerC
au point où vous souhaitez supprimer et revenir à ViewControllerA
, procédez comme suit (avec l'ID que nous avons défini dans l'inspecteur précédemment):
self.performSegue(withIdentifier: "yourIdHere", sender: self)
Ajout à la réponse de Phlippie Bosman, lors de l'appel
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
si vous ne voulez pas voir (quel serait le presentingViewController
) vous pouvez faire quelque chose comme
self.presentingViewController?.view.addSubview(self.view)
Cela semble un peu hacky, mais jusqu'à présent, c'est la seule façon dont j'ai pu donner l'impression que deux contrôleurs de vue sont en train de rejeter à l'unisson.
Bien que la réponse de Rafeels soit acceptable. Tout le monde n'utilise pas Segue.
Pour moi, la solution suivante fonctionne le mieux
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)
}
}
Swift 4.
let presentingViewController = self.presentingViewController
presentingViewController?.presentingViewController?.presentingViewController?.dismiss(animated: false, completion: nil)