Код SVProgressHUD выполняется не по порядку
Я создаю приложение прогнозирования шины с использованием API NextBus, которое поможет пользователям получить время прогнозирования и информацию об автобусе. Я реализовал функцию, которая берет текущее местоположение пользователя и выбранный адрес и возвращает список из 10 автобусных маршрутов, которые минимизируют расстояние и время в пути.
Вот @IBAction
это вызывает вышеупомянутую функцию:
@IBAction func findAWayPressed(_ sender: UIButton) {
// Hide confirm button.
confirmButton.isHidden = true
// Setup loading HUD.
let blue = UIColor(red: 153/255, green: 186/255, blue: 221/255, alpha: 1.0)
SVProgressHUD.setBackgroundColor(blue)
SVProgressHUD.setStatus("Finding a way for you...")
SVProgressHUD.setBorderColor(UIColor.black)
SVProgressHUD.show()
// Finds a list of ten bus routes that minimizes the distance from the user and their destination.
WayFinder.shared.findAWay(startCoordinate: origin!, endCoordinate: destination!)
SVProgressHUD.dismiss()
}
Проблема в том, что confirmButton.isHidden = true
и линии SVProgressHUD, кажется, делают что-нибудь только после WayFinder.shared.findAWay()
выполняет. HUD отображается на короткое время, а затем сразу же SVProgressHUD.dismiss()
,
Вот findAWay
функция:
func findAWay(startCoordinate: CLLocationCoordinate2D, endCoordinate: CLLocationCoordinate2D) {
// Get list of bus routes from NextBus API.
getRoutes()
guard !self.routes.isEmpty else {return}
// Initialize the the lists of destination and origin stops.
closestDestinations = DistanceData(shortestDistance: 1000000, stops: [])
closestOrigins = DistanceData(shortestDistance: 1000000, stops: [])
// Fetch route info for every route in NextBus API.
var routeConfigsDownloaded: Int = 0
for route in routes {
// Counter is always one whether the request fails
// or succeeds to prevent app crash.
getRouteInfo(route: route) { (counter) in
routeConfigsDownloaded += counter
}
}
while routeConfigsDownloaded != routes.count {}
// Iterate through every stop and retrieve a list
// of 10 possible destination stops sorted by distance.
getClosestDestinations(endCoordinate: endCoordinate)
// Use destination stop routes to find stops near
// user's current location that end at destination stops.
getOriginStops(startCoordinate: startCoordinate)
// Sort routes by adding their orign distance and destination
// distance and sorting by total distance.
getFoundWays()
}
private func getRouteInfo(route: Route, completion: @escaping (Int) -> Void) {
APIWrapper.routeFetcher.fetchRouteInfo(routeTag: route.tag) { (config) in
if let config = config {
self.routeConfigs[route.tag] = config
} else {
print("Error retrieving route config for Route \(route.tag).")
}
completion(1)
}
}
Зачем код в @IBAction
не выполнить в порядке? Как мог HUD не показывать на экране раньше findAWay
назывался? Есть идеи?
1 ответ
Итак, вы захотите немного почитать о "главном потоке" и о том, как он работает. Возможно ПОНИМАНИЕ ОСНОВНОЙ РЕЗЬБЫ IOS
По сути, вы просите систему показать HUD, затем выполняете, я полагаю, длительную и блокирующую операцию, а затем удаляете HUD из основного потока.
Для системы невозможно отобразить HUD, пока метод не существует, так как он будет частью следующего цикла (рисование / макет / другие важные вещи). В таких случаях я бы склонялся к какому-то "обещающему" API, такому как PromiseKit или Hydra, так как это будет очень просто надеяться на поток.
Основная цель заключается в том, чтобы: находясь в основном потоке, представить HUD, используя фоновый поток, выполнить запрос, когда он будет завершен, закрыть HUD, но сделать это в основном потоке.
Который может выглядеть примерно так...
SVProgressHUD.show()
DispatchQueue.global(qos: .userInitiated).async {
WayFinder.shared.findAWay(startCoordinate: origin!, endCoordinate: destination!)
DispatchQueue.main.async {
SVProgressHUD.dismiss()
}
}
Помните, что никогда не изменяйте пользовательский интерфейс вне контекста основного потока, если ОС обнаружит это, это приведет к сбою вашего приложения!
Я мог бы также рассмотреть возможность использования DispatchSemaphore
вместо "дикого бега" while-loop
так что вместо..
// Fetch route info for every route in NextBus API.
var routeConfigsDownloaded: Int = 0
for route in routes {
// Counter is always one whether the request fails
// or succeeds to prevent app crash.
getRouteInfo(route: route) { (counter) in
routeConfigsDownloaded += counter
}
}
while routeConfigsDownloaded != routes.count {}
Вы можете использовать что-то вроде...
let semaphore = DispatchSemaphore(value: routes.count)
// Fetch route info for every route in NextBus API.
var routeConfigsDownloaded: Int = 0
for route in routes {
// Counter is always one whether the request fails
// or succeeds to prevent app crash.
getRouteInfo(route: route) { (counter) in
semaphore.signal()
}
}
semaphore.wait()
который будет делать то же самое, но более эффективно