2017-10-09 10 views
-1

J'écris une interface graphique d'échecs dans Swift 3 et j'utilise nvzqz/Sage comme modèle/bibliothèque d'échecs. Maintenant, je suis confronté à un problème avec une fermeture Sage utilisé pour la promotion de la pièce.Comment faire une fermeture attendre que le joueur appuie sur un bouton dans un autre contrôleur de vue?

Sage utilise (dans son jeu classe) la execute (mouvement: promotion :) méthode pour l'exécution de mouvement de promotion qui a une fermeture qui retourne une sorte de pièce de promotion. Cela permet de demander à l'utilisateur un morceau de promotion ou effectuer d'autres opérations avant de choisir un morceau de promotion genre, comme suit:

try game.execute(move: move) { 
    ... 
    return .queen 
} 

Je mis en place un contrôleur de vue de la promotion (« PVC ») qui est appelé dans cette fermeture si que le joueur peut sélectionner la nouvelle pièce:

// This is in the main View Controller class 

/// The piece selected in promotionViewController into which a pawn shall promote 
var newPiece: Piece.Kind = ._queen // default value = Queen 

try game.execute(move: move) { 
    boardView.isUserInteractionEnabled = false 

    // promotionview controller appears to select new promoted piece 
    let pvc = PromotionViewController(nibName: nil, bundle: nil) 
    pvc.delegate = self 
    pvc.modalPresentationStyle = .overCurrentContext 
    self.present(pvc, animated:true) 

    return newPiece 
} 

Lorsque le bouton de la nouvelle pièce dans le PVC est pressée, le PVC lui-même est rejeté et les données de la pièce sélectionnée (la constante de selectedType) est de nouveau transféré au contrôleur principal par délégation:

// This is in the sending PVC class 
protocol PromotionViewControllerDelegate { 
    func processPromotion(selectedType: Piece.Kind) 
} 

func buttonPressed(sender: UIButton) { 
    let selectedType = bla bla bla ... 
    delegate?.processPromotion(selectedType: selectedType) 
    presentingViewController!.dismiss(animated:true) 
} 


// This is in the receiving main View Controller class 
extension GameViewController: PromotionViewControllerDelegate { 

func processPromotion(selectedType: Piece.Kind) { 
    defer { 
     boardView.isUserInteractionEnabled = true 
    } 
    newPiece = selectedType 
} 

Le problème que j'ai est que la fermeture (dans le game.execute méthode) ne pas attendre que le joueur a fait sa sélection dans le PVC (et retourne immédiatement la variable de newpiece qui est toujours la valeur par défaut valeur) de sorte que je ne reçois jamais une autre pièce de promotion traitée autre que la valeur par défaut. Comment puis-je faire la fermeture attendre jusqu'à ce que le joueur a appuyé sur un bouton dans le pvc?

Bien sûr, j'ai essayé de trouver une solution et de lire à propos des rappels, des gestionnaires de complétion ou des observateurs de propriété. Gestionnaire de réalisation: le pvc se ferme automatiquement à la suite d'un événement de pression de bouton, de sorte que le gestionnaire de complétion ne se trouve pas dans le contrôleur de vue (principal) de réception. Comment puis-je faire face à cela?

observateur Propriété: Je pourrais appeler la méthode game.execute try (déplacement) seulement après la pièce de promotion a été fixé (avec didset) mais qui amélioreraient le code difficile à lire et ne pas utiliser la fermeture belle la game.execute méthode fournit.

Les rappels peuvent être liés aux gestionnaires d'achèvement, mais je ne suis pas sûr.

Répondre

1

Ainsi, votre bloc dans game.execute(move: move) sera entièrement exécuté, ce qui est donc conçu par l'API Sage. Vous ne pouvez pas faire une pause aussi facile mais c'est faisable, essayons toujours de le résoudre dans l'autre sens; Pourquoi avez-vous besoin d'appeler la présentation du contrôleur de vue dans ce bloc? Par tous les moyens, essayez de déplacer cela. L'appel try game.execute(move: move) { ne doit être appelé que dans la méthode de délégation processPromotion. Vous n'avez pas publié de code, mais partout où ce code try game.execute(move: move) { doit être remplacé, il suffit de présenter un contrôleur de vue.

Ensuite, sur le délégué, vous n'avez même pas besoin de conserver la valeur newPiece = selectedType, mais appelez simplement try game.execute(move: move) { return selectedType }.

donc sur la suspension d'un bloc:

Il est impossible directement « pause » un bloc parce qu'il est une partie d'exécution qui signifie l'opération a besoin de faire une pause qui à la fin signifie que vous devez pause votre fil entier. Cela signifie que vous devez déplacer l'appel vers un thread séparé et mettre celui-ci en pause. Cela ne fonctionnera que si l'API supporte le multithreading, si le callback est appelé sur le même pas que son appel execute ... Il y a donc beaucoup d'outils et de façons de verrouiller un thread alors laissez-moi utiliser le plus primitif qui rend le sommeil de fil:

var executionLocked: Bool = false 

    func foo() { 
     DispatchQueue(label: "confiramtion queue").async { 
      self.executionLocked = true 
      game.execute(move: move) { 
       // Assuming this is still on the "confiramtion queue" queue 
       DispatchQueue.main.async { 
        // UI code needs to be executed on main thread 
        let pvc = PromotionViewController(nibName: nil, bundle: nil) 
        pvc.delegate = self 
        pvc.modalPresentationStyle = .overCurrentContext 
        self.present(pvc, animated:true) 
       } 
       while self.executionLocked { 
        Thread.sleep(forTimeInterval: 1.0/5.0) // Check 5 times per second if unlocked 
       } 
       return self.newPiece // Or whatever the code is 
      } 
     } 
    } 

maintenant, dans votre délégué dont vous avez besoin:

func processPromotion(selectedType: Piece.Kind) { 
    defer { 
     boardView.isUserInteractionEnabled = true 
    } 
    newPiece = selectedType 
    self.executionLocked = false 
} 

Alors qu'est-ce qui se passe ici est que nous commençons un nouveau fil. Puis verrouillez l'exécution et commencez l'exécution sur l'instance game. Dans le bloc, nous exécutons maintenant notre code sur le thread principal puis créons une boucle "sans fin" dans laquelle un thread dort un peu à chaque fois (le sleep n'est pas vraiment nécessaire mais il empêche la boucle de prendre trop de puissance CPU). Maintenant, tout ce qui se passe sur le thread principal est qu'un nouveau contrôleur est présenté et que l'utilisateur peut faire des choses avec lui ... Ensuite, un délégué débloquera le verrou d'exécution qui fera sortir la boucle "sans fin" (sur un autre thread) et renvoyez une valeur à votre instance game.

Je ne m'attends pas à ce que vous l'appliquiez, mais si vous le faites, assurez-vous de prendre toutes les précautions pour relâcher correctement la boucle si nécessaire. Comme si le contrôleur de vue est fermé, il devrait le déverrouiller, si un délégué a une version "annuler", il devrait quitter ...