Swift 如何在闭包中调用func

Swift 如何在闭包中调用func,swift,block,cllocation,completionhandler,Swift,Block,Cllocation,Completionhandler,在模型的classLocation中,我得到了当前城市的名称: var currentLatitude: Double! var currentLongitude: Double! var currentLocation: String! var currentCity: String! func getLocationName() { let geoCoder = CLGeocoder() let location = CLLocation(latitude: curren

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

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()
    })
}
在view controller中,我想更新UI并更新标签以显示当前城市。 但是,
self.currentCity=city
发生在闭包内部。因此,如果我只是在视图控制器中运行func:

func updateUI() {
        cityLbl.text = Location.sharedInstance.currentCity
}
  • 我哪儿也去不了,因为关闭还没有结束。 有人建议我在
    getLocationName()
    中添加一个完成处理程序,并在其中执行对func的调用,该函数将更新UI。 然而,从所有关于闭包、完成处理程序的教程中,我不清楚如何实现这一点。 如何构造完成处理程序,将其作为参数传递给
    getLocationName()
    ,以及如何从视图控制器调用
    getLocationName

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

  • 使用Location类创建
    委托/协议

    • 创建一个协议,并使用
      ViewController
      实现该协议方法,并在
      位置
      类中声明其实例。然后在
      reversegeocodocation
      completionHandler
      中调用此委托方法。有关更多详细信息,请查看上的Apple文档
  • 您可以使用
    Location
    类的
    getLocationName
    方法创建
    completionHandler

    • getLocationName
      添加
      completionHandler
      ,并在
      reverseGeocodeLocation
      completionHandler
      中这样调用该
      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()
          }
      }
      
  • 您可以为
    (NS)通知中心添加观察员

    • (NS)NotificationCenter
      注册观察者,然后在
      ReverseGeoDelocation
      completionHandler
      中发布通知。你可以通过这个得到更多的细节

  • 您的整个代码:

    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位于后台队列中,但您必须始终从主队列中执行与UI相关的操作,以便用户能够以最快的速度更改其UI,而不会出现任何故障。想象一下,如果您在后台线程上执行此操作,并且执行速度很慢,但updateUI()是一个func from view controller,其中声明了位置标签。我应该创建视图控制器的实例并调用func吗?@Mr_Vlasov我明白了,不,你不应该这样做。你应该按照Nirav D的建议去做。如果你有任何疑问,请随意提问。是的,我还不擅长编程,你能展示你提供的第二个选项的实现吗?(假设updateUI()是视图控制器中的一个方法,而不是在实现getLocationName的位置中。)@Mr_Vlasov,所以您希望将
    completionHandler
    与方法一起使用。你在使用Swift 3吗?我正在努力寻找一个关于完成处理程序的好教程或简单解释。Apple文档在介绍闭包的文章中包含了所有不必要的中间人员。你能为闭包理论提供一些好的来源吗?试图掌握它的用法逻辑,而不是语法。@Vlasov先生检查这个和这个
    // 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
            }
    
        })
    }