如何在闭包中调用函数

How To Call a func within a Closure

在模型的classLocation中,我得到了当前城市的名称:

var currentLatitude: Double!
var currentLongitude: Double!
var currentLocation: String!
var currentCity: String!

func getLocationName() {

    let geoCoder = CLGeocoder()
    let location = CLLocation(latitude: currentLatitude, longitude: currentLongitude)

    geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
        guard let addressDict = placemarks?[0].addressDictionary else {
            return
        }
        if let city = addressDict["City"] as? String {
            self.currentCity = city
            print(city)
        }
        if let zip = addressDict["ZIP"] as? String {
            print(zip)
        }
        if let country = addressDict["Country"] as? String {
            print(country)
        }

        self.nowUpdateUI()
    })
}

在视图控制器中,我想更新 UI 并更新我的标签以显示当前城市。 但是,self.currentCity = city 发生在闭包内部。因此,如果我只是 运行 视图控制器中的一个函数:

func updateUI() {
        cityLbl.text = Location.sharedInstance.currentCity
}

要处理这种情况,您有多种选择。

  1. 使用您的位置创建 delegate/protocol class

    • 创建一个协议并使用您的 ViewController 实现该协议方法,并在您的 Location class 中声明其实例。然后在 reverseGeocodeLocationcompletionHandler 中调用此委托方法。查看 Protocol 上的 Apple 文档了解更多详情。
  2. 您可以使用 Location class 的 getLocationName 方法创建 completionHandler

    • getLocationName 添加 completionHandler 并像这样在 reverseGeocodeLocationcompletionHandler 中调用 completionHandler

      func getLocationName(completionHandler: @escaping (_ success: Bool) -> Void) {
      
          let geoCoder = CLGeocoder()
          let location = CLLocation(latitude: currentLatitude, longitude: currentLongitude)
      
          geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
              guard let addressDict = placemarks?[0].addressDictionary else {
                  completionHandler(false)
                  return
              }
              if let city = addressDict["City"] as? String {
                  self.currentCity = city
                  print(city)
              }
              if let zip = addressDict["ZIP"] as? String {
                  print(zip)
              }
              if let country = addressDict["Country"] as? String {
                  print(country)
              }
              completionHandler(true)
              //self.nowUpdateUI()
          })
      }
      

      现在在 ViewController 调用此函数的地方调用完成块内的 updateUI 方法。

      Location.sharedInstance.getLocationName { (success) in
          if success {//If successfully got response
              self.updateUI()
          }
      }
      
  3. 您可以为(NS)NotificationCenter添加观察者。

    • (NS)NotificationCenter 注册观察者,然后 post reverseGeocodeLocationcompletionHandler 内的通知。您可以通过 Whosebug Post.
    • 获得更多详细信息

你的整段代码:

completionHandler: { placemarks, error in
        guard let addressDict = placemarks?[0].addressDictionary else {
            return
        }
        if let city = addressDict["City"] as? String {
            self.currentCity = city
            print(city)
        }
        if let zip = addressDict["ZIP"] as? String {
            print(zip)
        }
        if let country = addressDict["Country"] as? String {
            print(country)
        }

        self.nowUpdateUI()
    }

)

已经 发生在 completionHandler 中(在一切完成后发生)还有 运行 你的 updateUI() 在 completionHandler 中。所以你的结束代码是:

completionHandler: { placemarks, error in
        guard let addressDict = placemarks?[0].addressDictionary else {
            return
        }
        if let city = addressDict["City"] as? String {
            self.currentCity = city
            DispatchQueue.main.async {
                updateUI() 
        }
        }
        if let zip = addressDict["ZIP"] as? String {
            print(zip)
        }
        if let country = addressDict["Country"] as? String {
            print(country)
        }

        self.nowUpdateUI()
    }

)

你必须使用 DispatchQueue.main 的原因是因为你的 completionHandler 在后台队列上,但你必须始终从你的 mainQueue 中做你 UI 相关的事情——这样用户在他们的 UI 没有任何故障。想象一下,如果您在后台线程上执行操作并且速度很慢

// I thing issue back ground thread you need to update your UI in main thread
var currentLatitude: Double!
var currentLongitude: Double!
var currentLocation: String!
var currentCity: String!

func getLocationName() {

    let geoCoder = CLGeocoder()
    let location = CLLocation(latitude: currentLatitude, longitude: currentLongitude)

    geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
        guard let addressDict = placemarks?[0].addressDictionary else {
            return
        }
        if let city = addressDict["City"] as? String {
            self.currentCity = city
            print(city)
        }
        if let zip = addressDict["ZIP"] as? String {
            print(zip)
        }
        if let country = addressDict["Country"] as? String {
            print(country)
        }
        DispatchQueue.main.async {
            self.nowUpdateUI()
            // Update your UI in main thread
        }

    })
}