如何在闭包内调用func

时间:2017-02-15 05:12:14

标签: swift block cllocation completionhandler

在模型的课程#rust中,我得到当前城市的名称:

Location

在视图控制器中,我想更新UI并更新我的标签以显示当前城市。 但是,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() }) } 发生在闭包内部。所以如果我只是在视图控制器中运行一个func:

self.currentCity = city
  • 我没有到达任何地方,因为关闭还没有完成。 我被建议向func updateUI() { cityLbl.text = Location.sharedInstance.currentCity } 添加一个完成处理程序,在其中,执行对将更新UI的func的调用。 但是,从闭包,完成处理程序的所有教程中,我不清楚如何实现这一点。 如何构造一个完成处理程序,将其作为arg传递给getLocationName()以及如何从视图控制器调用getLocationName()

3 个答案:

答案 0 :(得分:2)

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

  1. 使用您的位置等级

    创建delegate/protocol
    • 创建一个协议并使用ViewController实现该协议方法,并在Location类中声明其实例。然后在completionHandler reverseGeocodeLocation中调用此委托方法。有关详细信息,请查看Protocol上的Apple文档。
  2. 您可以使用[{1}}类completionHandler方法创建getLocationName

    • 使用Location添加completionHandler,并以getLocationName completionHandler的方式将completionHandler称为reverseGeocodeLocation

      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注册观察者,然后将通知发布在completionHandler的{​​{1}}内。您可以使用此StackOverflow Post
    • 获取有关详细信息

答案 1 :(得分:0)

这整段代码:

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中发生(在完成所有操作后都会发生)还要在completionHandler中运行updateUI()。所以你的最终代码是:

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是在backgroundqueue上的,但是你必须总是从你的mainQueue做与UI相关的东西 - 所以用户在他们的UI中获得最快的变化而没有任何故障。想象一下,如果你在后台线程上做的事情发生得很慢

答案 2 :(得分:0)

// 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
        }

    })
}