Ios 如何使segue在按下按钮时等待功能完成?

Ios 如何使segue在按下按钮时等待功能完成?,ios,swift,uibutton,segue,uistoryboardsegue,Ios,Swift,Uibutton,Segue,Uistoryboardsegue,我有一个swift应用程序,它在一个视图控制器上获取一些gps数据,然后将这些数据传递给另一个视图控制器 我目前的方法包括一个按钮,用户将在其中输入一些数据,点击该按钮,然后该按钮将执行两个操作,计算gps路线,并执行到下一个视图控制器的切换 然而,在进入下一个屏幕之前,我似乎无法让屏幕等待我的计算完成。如果我在第二个视图上着陆,然后按后退,然后再按按钮,数据将显示在第二个屏幕上,但我似乎无法立即让它工作 我问了这个问题:我仍然有困难 我完全是斯威夫特的不速之客,所以如果这个问题很琐碎,我很抱歉

我有一个swift应用程序,它在一个视图控制器上获取一些gps数据,然后将这些数据传递给另一个视图控制器

我目前的方法包括一个按钮,用户将在其中输入一些数据,点击该按钮,然后该按钮将执行两个操作,计算gps路线,并执行到下一个视图控制器的切换

然而,在进入下一个屏幕之前,我似乎无法让屏幕等待我的计算完成。如果我在第二个视图上着陆,然后按后退,然后再按按钮,数据将显示在第二个屏幕上,但我似乎无法立即让它工作

我问了这个问题:我仍然有困难

我完全是斯威夫特的不速之客,所以如果这个问题很琐碎,我很抱歉

FirstViewController.swift

....
@IBOutlet weak var calculateButton: UIButton!
var routeArray = Array<Array<MKRoute>>()
var distanceArray: [CLLocationDistance] = []

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "segue_to_table"{
        if let destination = segue.destination as? SecondTableViewController{
            destination.routeArray = self.routeArray
            destination.distanceArray = self.distanceArray
        }
    }
}


override func viewDidLoad() {
    super.viewDidLoad()
    ....
    calculateButton.addTarget(self, action: #selector(getRoutes(button:)), for: .touchUpInside)
}

func getRoutes(button: UIButton){
   locationArray = set of calculated MKMapItems
   calculateRoute(index: 0, distance: 0, routes: [], color: UIColor.green)

    locationArray = []
    locationArray = set of other MKMapItems (different route essentially)
    calculateRoute(index: 0, distance: 0, routes: [], color: UIColor.red)
}

func calculateRoute(index:Int, distance: CLLocationDistance, routes: [MKRoute], color: UIColor){
    let request: MKDirectionsRequest = MKDirectionsRequest()
    request.source = locationArray[index]

    request.destination = locationArray[index+1]

    request.requestsAlternateRoutes = true
    request.transportType = .walking

    let directions = MKDirections(request: request)
    directions.calculate(completionHandler: {(response:MKDirectionsResponse?, error: Error?) in
        if let routeResponse = response?.routes{
            var distVar = distance
            var routeVar = routes

            routeVar.append(routeResponse[0])
            distVar += routeResponse[0].distance

            if index + 2 < self.locationArray.count{
                self.calculateRoute(index: index+1, distance: distVar, routes: routeVar, color: color)

            }
            else{
                self.routeArray.append(routeVar)
                self.distanceArray.append(distVar)
            }

        }else if let _ = error{
            //alert
        }


        })


      }
。。。。
@IBOUTLE弱var计算按钮:UIButton!
var routarray=Array()
var distanceArray:[CLLocationDistance]=[]
覆盖功能准备(对于segue:UIStoryboardSegue,发送方:有吗?){
如果segue.identifier==“segue_到_表”{
如果let destination=segue.destination as?SecondTableViewController{
destination.routerray=self.routerray
destination.distanceArray=self.distanceArray
}
}
}
重写func viewDidLoad(){
super.viewDidLoad()
....
calculateButton.addTarget(self,action:#选择器(getRoutes(button:)),for:.touchUpInside)
}
func getRoutes(按钮:UIButton){
locationArray=计算出的MKMapItems集
计算器输出(索引:0,距离:0,路由:[],颜色:UIColor.green)
locationArray=[]
locationArray=其他mkMapItem的集合(本质上不同的路由)
计算器输出(索引:0,距离:0,路由:[],颜色:UIColor.red)
}
func计算器输出(索引:Int,距离:CLLocationDistance,路由:[MKRoute],颜色:UIColor){
let请求:MKDirectionsRequest=MKDirectionsRequest()
request.source=locationArray[索引]
request.destination=locationArray[index+1]
request.requestSalterRoutes=true
request.transportType=.walking
let directions=MKDirections(请求:请求)
计算(completionHandler:{(响应:mkDirectionResponse?,错误:error?)在
如果让路由响应=响应?路由{
var distVar=距离
var routeVar=路由
routeVar.append(路由响应[0])
distVar+=路由响应[0]。距离
如果索引+2
您的代码看起来很好,所以问题出在您省略的部分之一。我认为有两种可能性:

1)您的“segue\u to_table”segue直接链接到情节提要中的
calculate按钮。

如果是这种情况,它将立即执行segue,同时调用
getRoutes()
。解决方案是删除该segue并创建一个新的手动segue

要执行此操作,请在第一个视图控制器中单击内部带有白色正方形的黄色小圆圈,然后拖动到第二个视图控制器。给它一个标识符,你就一切就绪了

2)省略的“长任务”涉及异步的内容。

如果是这种情况,
getRoutes()
将启动异步任务,然后在完成之前立即触发segue

如何修复它取决于特定的异步代码,但很可能您需要查找“完成”回调,并将调用放到
performsgue()
那里


更新新代码

异步代码肯定有问题,递归使其变得复杂。除了什么时候继续的问题,看起来你也有一个竞争条件:在
getRoutes()
中,你启动
calculatoroutes()
两次,因此这两次都将在相同的
routeArray
distanceArray
上运行

要解决这个问题,您需要认识到,
calculateRoute()
是一个异步函数,并使其行为类似于一个异步函数。当
calculateRoute()
完成其异步调用时,您希望发生一些事情,因此请添加一个参数,为其提供自己的完成回调,并在所有异步工作完成时调用它:

func calculateRoute(index:Int, distance: CLLocationDistance, routes: [MKRoute], color: UIColor, completionHandler: @escaping () -> Void) {
    // ...
    directions.calculate(completionHandler: {(response:MKDirectionsResponse?, error: Error?) in
        if let routeResponse = response?.routes {
            // ...
            if index + 2 < self.locationArray.count{
                self.calculateRoute(index: index+1, distance: distVar, routes: routeVar, color: color, completionHandler: completionHandler)
            } else {
                self.routeArray.append(routeVar)
                self.distanceArray.append(distVar)
                // done, call completion handler
                completionHandler()
            }

        }
    })
}

请注意,我们使用的是here,它很简洁,但如果您不熟悉它,可能会让人困惑。

它非常简单,只需防止segue with应执行segue

如果函数尚未就绪,则返回false。 函数准备好后,再次调用perform segue并返回true

func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
    if (identifier == "mySegueIdentifier") {
        return isComplete
    }
    return true
}

不要直接从按钮触发segue;将segue链接到情节提要中的view controller对象,然后在准备就绪时通过
performsgue
以编程方式触发segue。它可能是第二个,因为我已经实现了第一部分。我会试着和你回去看看这是不是最好的选择problem@user3470987很多CoreLocation函数都是异步的,所以如果您使用的是GPS,这是一个很大的可能性。如果您遇到问题,请将该代码添加到您的问题中,以获得更具体的答案。@user3470987如果没有看到代码,很难说清楚。你能把你的问题编辑成包含
getRoutes()
calculatoroutes()
中的代码吗?@user3470987你的情况很复杂,但我想我已经解决了。看看我的最新答案。@user3470987我想我明白你的意思了。为了整洁,我把那条线留了下来,但有一个重要的变化。当你递归地
func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
    if (identifier == "mySegueIdentifier") {
        return isComplete
    }
    return true
}