简体   繁体   English

如何让关闭等待玩家按下不同视图控制器中的按钮?

[英]How do I make a closure wait until the player pressed a button in a different view controller?

I am writing a chess GUI in Swift 3 and use nvzqz/Sage as the chess model/library.我正在 Swift 3 中编写国际象棋 GUI,并使用nvzqz/Sage作为国际象棋模型/库。 Now I face a problem with a Sage closure used for piece promotion.现在,我面临着用于件数促销的 Sage 闭包的问题。

Sage uses (in its game class) the execute(move: promotion:) method for promotion move execution which has a closure that returns a promotion piece kind. Sage 使用(在它的游戏类中) execute(move:promotion:)方法来执行提升移动执行,它有一个返回一个提升块类型的闭包。 This allows to prompt the user for a promotion piece or perform any other operations before choosing a promotion piece kind, as follows:这允许在选择促销片类型之前提示用户选择促销片或执行任何其他操作,如下所示:

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

I implemented a promotion view controller ("pvc") which is called in this closure so that the player may select the new piece:我实现了一个在这个闭包中调用的促销视图控制器(“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
}

When the button for the new piece in the pvc is pressed, the pvc dismisses itself and the data of the selected piece (the constant selectedType ) is transferred back to the main view controller via delegation:当按下 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
}

The problem I have is that the closure (in the game.execute method) does not wait until the player made his selection in the pvc (and immediately returns the newPiece variable which is still the default value) so that I never get another promotion piece processed other than the default value.遇到的问题是闭包(在game.execute方法中)不会等到玩家在 pvc 中做出选择(并立即返回仍然是默认值的newPiece变量),这样我就永远不会得到另一个促销件处理非默认值。

How do I make the closure wait until the player pressed a button in the pvc?如何让关闭等待玩家按下 pvc 中的按钮?

Of course, I tried to find a solution and read about callbacks, completion handlers or property observers.当然,我试图找到解决方案并阅读有关回调、完成处理程序或属性观察器的信息。 I do not know which is the best way forward, some thoughts:我不知道哪种方法是最好的前进方式,一些想法:

Completion handler: the pvc dismisses itself upon button-press event so the completion handler is not in the receiving (main) view controller.完成处理程序:pvc 在按钮按下事件时自行解除,因此完成处理程序不在接收(主)视图控制器中。 How do I deal with this?我该如何处理?

Property observer: I could call the try game.execute(move) method only after the promotion piece was set (with didset ) but that would make the code difficult to read and not use the nice closure the game.execute method provides.属性观察者:我只能在设置了促销片段(使用didset )之后才能调用try game.execute(move)方法,但这会使代码难以阅读,并且无法使用game.execute方法提供的良好闭包。

Callbacks may be related to completion handlers, but am not sure.回调可能与完成处理程序有关,但我不确定。

So your block in game.execute(move: move) will fully execute which is so designed by the Sage API.所以你在game.execute(move: move)块将完全执行,这是由 Sage API 设计的。 You can not pause it as easy but it is doable, still let's try to solve it the other way;你不能简单地暂停它,但它是可行的,我们仍然尝试以其他方式解决它;

Why do you need to call the presentation of the view controller within this block?为什么需要在这个块内调用视图控制器的呈现? By all means try to move that away.无论如何,尝试将其移开。 The call try game.execute(move: move) { should only be called within processPromotion delegate method.调用try game.execute(move: move) {应该只在processPromotion委托方法中调用。 You did not post any code but wherever this try game.execute(move: move) { code is it needs to be replaced by presenting a view controller alone.您没有发布任何代码,但是无论在哪里try game.execute(move: move) {代码是否需要通过单独呈现视图控制器来替换。

Then on delegate you do not even need to preserve the value newPiece = selectedType but rather just call try game.execute(move: move) { return selectedType } .然后在委托上,您甚至不需要保留值newPiece = selectedType而只是调用try game.execute(move: move) { return selectedType }

So about pausing a block:关于暂停块:

It is not possible to directly "pause" a block because it is a part of execution which means the whole operation needs to pause which in the end means you need to pause your whole thread.不可能直接“暂停”一个块,因为它是执行的一部分,这意味着整个操作需要暂停,最后意味着您需要暂停整个线程。 That means you need to move the call to a separate thread and pause that one.这意味着您需要将调用移动到一个单独的线程并暂停该线程。 Still this will only work if the API supports the multithreading, if the callback is called on the same tread as its execute call... So there are many tools and ways on how to lock a thread so let me just use the most primitive one which is making the thread sleep:尽管如此,这只有在 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
            }
        }
    }

Now in your delegate you need:现在在您的委托中,您需要:

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

So what happens here is we start a new thread.所以这里发生的是我们开始一个新线程。 Then lock the execution and start execution on game instance.然后锁定执行并在game实例上开始执行。 In the block we now execute our code on main thread and then create an "endless" loop in which a thread sleeps a bit every time (the sleep is not really needed but it prevents the loop to take too much CPU power).在块中,我们现在在主线程上执行我们的代码,然后创建一个“无限”循环,其中一个线程每次都休眠一点(并不真正需要休眠,但它可以防止循环占用过多的 CPU 功率)。 Now all the stuff is happening on main thread which is that a new controller is presented and user may do stuff with it... Then once done a delegate will unlock the execution lock which will make the "endless" loop exit (on another thread) and return a value to your game instance.现在所有的事情都发生在主线程上,即出现一个新的控制器,用户可以用它做一些事情……然后,一旦完成,委托将解锁执行锁,这将使“无限”循环退出(在另一个线程上) ) 并向您的game实例返回一个值。

I do not expect you to implement this but if you will then ensure you make all precautions to correctly release the loop if needed.我不希望您实现这一点,但如果您将确保采取所有预防措施以在需要时正确释放循环。 Like if view controller is dismissed it should unlock it, if a delegate has a "cancel" version it should exit...就像如果视图控制器被解除它应该解锁它,如果一个委托有一个“取消”版本它应该退出......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在按下按钮时使按钮弹出警报视图并返回主视图控制器? - How Do I Make A Button Get An Alert View To Pop Up When Pressed And Go Back To The Main View Controller? 如何使按钮保持按下状态? - How do I make a button stay pressed? 按下按钮后转到其他视图控制器 - On button pressed go to different view controller 如何在按下按钮时更改按钮的颜色,并在按下其他按钮时重置为原始颜色? - How do I change a button's color when pressed and reset to original color when a different button is pressed? 按下按钮时如何更改界面 - How do I make changes to my interface while a button is pressed 如何通过按钮重新打开视图控制器? - How do I reopen a view controller by a button? 如何在其他视图中创建按钮,向其他视图中的对象发送动作? - how do i make a button in a different view, send an action to something located in the other view? 如何使注释标注指向其他视图控制器? - How do I make my annotation callout lead to a different view controller? 在检测到按下后退按钮后如何将数据发送回第一个视图控制器? - How can I send data back to the first view controller after detecting that the back button was pressed? 在Swift 3中按下按钮时如何转到另一个视图控制器 - How to go to another view controller when button is pressed in swift 3
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM