我有一个快速的应用程序,它在一个视图控制器上抓取一些gps数据,然后将该数据传递给另一个视图控制器。
我当前的方法涉及一个按钮,用户将输入一些数据,点击此按钮,然后该按钮应执行两个动作,计算gps路线,并对下一个视图控制器执行segue。
然而,在进入下一个屏幕之前,我似乎无法让segue等待我的计算完成。如果我落在第二个视图上然后按回来,然后再次按下按钮,数据将显示在第二个屏幕上,但我似乎无法立即开始工作。
我关注了这个问题:Separate Button action and segue action但仍然遇到问题
我是一个快速的完全菜鸟,所以如果这个问题很简单,我道歉
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
}
})
}
答案 0 :(得分:3)
您的代码看起来很好,所以问题出在您省略的部分之一。我看到两种可能性:
1)你的&#34; segue_to_table&#34; segue直接链接到故事板中的calculateButton
。
如果属于这种情况,则会立即执行和同时调用getRoutes()
。解决方案是删除该segue并制作新的手册。
要执行此操作,请在第一个视图控制器中按住Ctrl键并单击其中带有白色方块的小黄色圆圈,然后拖动到第二个视图控制器。给它一个标识符,你就可以了。
2)省略的&#34;长任务&#34;涉及异步的事情。
如果是这种情况,getRoutes()
将启动异步任务,然后在完成之前立即触发segue。
如何修复它取决于具体的异步代码,但很可能你会想要找到一个&#34;完成&#34;回拨并将您的电话转到performSegue()
。
更新新代码
您肯定遇到了异步代码的问题,这种问题因递归而变得复杂。除了什么时候出现问题,看起来你还有一个竞争条件:getRoutes()
你开始calculateRoutes()
两次,所以两者都会运作以不可预测的顺序在同一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()
}
}
})
}
现在当你给你calculateRoute()
打电话时,你传递了一个函数,当它完成时调用。它会一个接一个地对calculate()
进行一次异步调用,直到不再有,然后它会调用你的completionHandler
并终止。我应该提一下,让它在外部locationArray
上运行并不是超级安全的(如果某个其他进程在calculateRoute()
运行时更改了该数组会发生什么?)但这是一个独立的问题。
现在,您要使用calculateRoute()
计算两条路线,并将它们按顺序附加到routesArray
和distanceArray
。因为您不知道哪个会先终止,所以您不能同时打电话给他们。你拨打第一个电话,然后从第一个completionHandler
拨打第二个电话。由于你想在两个完成后执行segue,你可以从第二个完成处理程序中调用它。所以它看起来像这样:
func getRoutes(button: UIButton) {
// ...
// start first asynchronous calculation
calculateRoute(index: 0, distance: 0, routes: [], color: UIColor.green) {
// ...
// finished first calculation, start second one
self.calculateRoute(index: 0, distance: 0, routes: [], color: UIColor.red, completionHandler: {
// finished second calculation, now segue
self.performSegue(withIdentifier: "segue_to_table", sender: self)
})
}
}
请注意,我们在这里使用trailing closure syntax,这很整洁但如果您不熟悉它可能会让您感到困惑。
答案 1 :(得分:0)
这很简单,只需要阻止segue执行segue,
如果你的功能尚未准备好,则返回false。 功能准备就绪后,再次调用perform segue并返回true
func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if (identifier == "mySegueIdentifier") {
return isComplete
}
return true
}