Закрытие манипуляции в Swift

Я новичок в Swift и не могу понять, как обращаться с замыканиями и замыканиями.

Я недавно задал вопрос, и я узнаю, что моя переменная равна нулю, потому что geocodeAddressString работает асинхронно, поэтому печать приложения latLong задолго до того, как это свойство было в конечном итоге установлено.

Но вот новый вопрос, который я не могу понять:

import UIKit
import CoreLocation
import Firebase

var latLong: String!

override func viewDidLoad() {
    super.viewDidLoad()

}

func findCordiante(adress:String){

    let geocoder = CLGeocoder()
    geocoder.geocodeAddressString(adress) {
        placemarks, error in

        if (placemarks != nil){
            let placemark = placemarks?.first
            let lat = placemark?.location?.coordinate.latitude
            let lon = placemark?.location?.coordinate.longitude

            self.latLong = String(describing: lat!) + "," + String(describing: lon!)

        }else{
            //handle no adress
             self.latLong = ""
        }
    }

}

@IBAction func createSchool(_ sender: Any) {

      //when user press button i want execute function and assign value to variable latLong  
      findCordiante(adress: "Cupertino, California, U.S.")
      //so then I need to manipulate with that value here, let's say example

      //example
      print("Hi user, your coordinates is \(latLong)")

}

Когда я добавлю print(latLong) внутри закрытия это печать, но я не хочу делать все функции внутри закрытия.

Просто ХОЧУ добавить результат func findCordiante() к переменной latLongтак что после этого я могу манипулировать этой переменной везде внутри класса

3 ответа

Главное, что нужно понять, это то, что преобразование адреса в координаты (и многие другие операции геолокации) требует времени и, следовательно, возвращает результат со значительной задержкой. Во время задержки приложение продолжает работать и должно реагировать на действия пользователя.

Вот почему используются замыкания, а именно, чтобы разделить операцию на две части:

  • Начните операцию (ваш findCoordinate функция)
  • Завершить действие после завершения операции (замыкание, используется как обратный вызов)

Между этими двумя частями ваше приложение работает нормально. Это не ждет и не блокирует. Если вам нужно поведение ожидания, вы должны реализовать его самостоятельно (например, отключить кнопки, игнорировать жесты пользователя и т. Д. (.

Вы можете легко переместить часть кода внутри замыкания в отдельную функцию:

func findCordiante(adress:String){

    let geocoder = CLGeocoder()
    geocoder.geocodeAddressString(adress) {
        placemarks, error in

        if let placemarks = placemarks {
            self.findCoordinateCompleted(placemarks)
        } else {
            self.findCoordinateFailed()
        }
    }
}

func findCoordinateCompleted(placemarks: [CLPlacemark]) {
    let placemark = placemarks.first!
    let lat = placemark.location!.coordinate.latitude
    let lon = placemark.location!.coordinate.longitude
    latLong = String(describing: lat) + "," + String(describing: lon)

    completeCreatingSchool()
}

func findCoordinateFailed() {
    latLong = ""
    print("Hi user, invalid address")
    // do more stuff here
}

@IBAction func createSchool(_ sender: Any) {
    findCoordinate(adress: "Cupertino, California, U.S.")
}

func completeCreatingSchool() {
    //example
    print("Hi user, your coordinates is \(latLong)")
}

В соответствии с вашим кодом самое близкое решение - использовать обработчик завершения и вернуть логическое значение success в закрытии. Тогда вы можете использовать latLong переменная (не будет CLLocationCoordinate2D лучше типа?) сразу после установки.

func findCoordinate(adress:String, completion: @escaping (Bool)->()){

    let geocoder = CLGeocoder()
    geocoder.geocodeAddressString(adress) {
        placemarks, error in

        if (placemarks != nil){
            let placemark = placemarks?.first
            let lat = placemark?.location?.coordinate.latitude
            let lon = placemark?.location?.coordinate.longitude

            self.latLong = String(describing: lat!) + "," + String(describing: lon!)
            completion(true)
        }else{
            //handle no adress
             self.latLong = ""
             completion(false)
        }
    }

}

@IBAction func createSchool(_ sender: Any) {

   //when user press button i want execute function and assign value to variable latLong  
   findCoordinate(adress: "Cupertino, California, U.S.") { success in
       //so then I need to manipulate with that value here, let's say example

       //example
       if success {
          print("Hi user, your coordinates is \(latLong)")
       }
   }
}

После того, как вы установили latLong в закрытии, это будет доступно для остальной части вашего класса. Единственная проблема у вас есть, если createSchool вызывается до завершения закрытия.

Решение этой проблемы состоит в том, чтобы иметь кнопку и / или пункт меню, который указывает на createSchool начало отключено. Затем вы включаете его после завершения закрытия.

Другие вопросы по тегам