如何进行封闭等待,直到玩家在不同的视图控制器中按下按钮?

问题描述:

我正在Swift 3编写一个国际象棋GUI,并使用nvzqz/Sage作为国际象棋模型/库。现在我面临一个用于片段推广的Sage封闭的问题。如何进行封闭等待,直到玩家在不同的视图控制器中按下按钮?

贤者使用(在其游戏类)的执行(招:促销:)推广移动执行具有封闭返回一个推广片种方法。这允许以提示用户进行了推广片或选择一个促销片样之前执行任何其他操作,如下所示:

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

我实现被称为在该封闭件的提升视图控制器(“PVC”),以便玩家可以选择新的片:

// 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 
} 

当按下用于在PVC新片的按钮时,PVC驳回本身和所选择的片(恒定selectedType)的数据传输回通过授权到主视图控制器:

// 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 
} 

我的问题是,封闭(在game.execute方法)不会等到玩家在PVC做了他的选择(并立即返回newPiece变量,它仍然是默认价值),所以我从来没有得到另一个促销片以外的默认值处理。

我该如何让关闭等到玩家在pvc中按下按钮?

当然,我试图找到一个解决方案,并阅读回调,完成处理程序或属性观察员。我不知道哪个是最好的前进方向,有些想法:

完成处理程序:pvc在按下按钮事件时自动关闭,因此完成处理程序不在接收(主)视图控制器中。我如何处理这个问题?

房产观察员:我可以调用尝试game.execute(移动)推广片只设置后方法(与didset),但会使代码难以阅读,而不是用好的封闭的game.execute方法提供了

回调可能与完成处理程序有关,但我不确定。

因此,您在game.execute(move: move)中的块将完全执行,这是由Sage API设计的。你不能简单地暂停它,但它是可行的,我们仍然试着用另一种方式来解决它;

为什么你需要调用这个块内的视图控制器的演示文稿?通过一切手段试图将其移开。 try game.execute(move: move) {只能在processPromotion委托方法内调用。您没有发布任何代码,但无论此代码是何处,都需要通过单独展示视图控制器来替换。

然后在委托上,您甚至不需要保留值newPiece = selectedType,而只需拨打try game.execute(move: move) { return selectedType }即可。

所以有关暂停块:

这是不可能直接“暂停”的模块,因为这是执行的一部分,这意味着整个操作需要暂停这到底意味着你需要暂停你的整个线程。这意味着您需要将呼叫转移到单独的线程并暂停该线程。不过,如果API支持多线程,如果回调的调用方式与execute的调用方式相同,则此方法才有效...因此,如何锁定线程有很多工具和方法,因此我只需使用最原始的一个这使得线程睡眠:

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 
      } 
     } 
    } 

现在在你代表你需要:

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

所以会发生什么。这是我们开始一个新的线程。然后锁定执行并开始执行game实例。在块中,我们现在在主线程上执行我们的代码,然后创建一个“无休止”的循环,每次都有一个线程休眠一下(睡眠不是真的需要,但它阻止了循环占用太多的CPU能力)。现在所有的东西都发生在主线程上,这是一个新的控制器提供,用户可以做它的东西...然后一旦完成委托将解锁执行锁,这将使“无尽的”循环退出(在另一个线程)并将值返回给您的game实例。

我不指望你实现这一点,但如果你会确保你做好所有的预防措施,如果需要正确释放循环。如果视图控制器被解雇,它应该解锁它,如果委托人有“取消”版本,它应该退出...