获得"从主线程访问引擎后,从后台线程修改自动布局引擎"甚至在使用DispatchQueue时

时间:2017-10-06 21:59:05

标签: ios swift xcode

您好我试图创建一个简单的应用程序,从我的Web服务器获取注释,然后使用它们传播地图。唯一的问题是当我在30秒后调用函数bob从另一个位置获取一个新的注释它给我上面的错误时,我尝试使用DispatchQueue.main.async修复它无济于事。任何帮助表示赞赏。

这是有问题的功能

// this is the test to see if it can add a new annotation after 30 seconds
if bob == 30{

    let user_lat_temp = 26.7709
    let user_lng_temp = -80.1067

    DispatchQueue.main.async() {
        // Do stuff to UI
        self.GetAnnotations(lat: user_lat_temp, lng: user_lng_temp)
    }

    // reset it to see if it breaks
    bob = 0
    }
    bob = bob + 1
    print("bob: ", bob)
}

这是完整的代码

import UIKit
import MapKit
import CoreLocation

class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {



    @IBOutlet weak var mapView: MKMapView!
    let access_token = ""
    let manager = CLLocationManager()
    var firstTime = 1
    var user_lat = 0.0
    var user_lng = 0.0

    var bob = 0

    override func viewDidLoad() {

        super.viewDidLoad()

        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        manager.requestWhenInUseAuthorization()

        manager.startUpdatingLocation()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        let userLocation = locations[0]


        user_lat = userLocation.coordinate.latitude
        user_lng = userLocation.coordinate.longitude

        self.mapView.showsUserLocation = true

        if firstTime == 1{

            GetAnnotations(lat: user_lat, lng: user_lng)

            firstTime = 0
        }


       // this is the test to see if it can add a new annotation after 30 seconds
   if bob == 30{
        let user_lat_temp = 26.7709
        let user_lng_temp = -80.1067

        DispatchQueue.main.async() {
            // Do stuff to UI
            self.GetAnnotations(lat: user_lat_temp, lng: user_lng_temp)
        }

        // reset it to see if it breaks
        bob = 0
        }
        bob = bob + 1
        print("bob: ", bob)
    }

    func GetAnnotations(lat: Double, lng: Double){

        guard let url = URL(string: "http://192.168.1.10:7888/api/?controller=location&action=get_locations") else {return}

        var request = URLRequest(url: url)

        request.httpMethod = "POST"

        let postString = "access_token=\(access_token)&lat=\(lat)&lng=\(lng)";

        request.httpBody = postString.data(using: String.Encoding.utf8)

        URLSession.shared.dataTask(with: request) { (data, response, err) in

            if let error = err {
                print("the server is not responding \(error)")
            }

            if let response = response {

                // if the user has a bad access token or is logged out
                if let httpResponse = response as? HTTPURLResponse {
                    if httpResponse.statusCode == 401{
                        print("bad access token")
                        return
                    }
                }else{
                    print("the server is not responding")
            }
            print(response)




            guard let data = data else { return }

            // parse the json for the locations
            do {
                let mapJSON = try JSONDecoder().decode(parseJsonLocations.self, from: data)
                let user_id = mapJSON.user_info.user_id

                print(user_id)
                print(mapJSON.locations.count)

             // do map 
              let distanceSpan:CLLocationDegrees = 10000
               let userLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat, lng)
                self.mapView.setRegion(MKCoordinateRegionMakeWithDistance(userLocation, distanceSpan, distanceSpan), animated: true)
               self.mapView.delegate = self

                var i = 0

                while i < mapJSON.locations.count {

                    let location_id = mapJSON.locations[i].location_id
                    let location_name = mapJSON.locations[i].location_name
                    let location_lat = mapJSON.locations[i].lat
                    let location_lng = mapJSON.locations[i].lng

                    let locationsLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location_lat, location_lng)

                    let subtitle = "location_id: \(location_id)"

                    let userAnnotation = Annotation(title: location_name, subtitle: subtitle, coordinate: locationsLocation)

                    self.mapView.addAnnotation( userAnnotation )

                    i = i + 1

                }

            } catch {
                print("error trying to convert data to JSON")
                print(error)
            }





        }
        }.resume()
    }


}

2 个答案:

答案 0 :(得分:2)

在很多其他地方,你没有注意到你可能会遇到什么线索的问题。

你在说

if firstTime == 1 {
    GetAnnotations( // ...

不确定你是否在主线上。

然后,在GetAnnotations中,你说的是

self.mapView.setRegion
// ... and all that follows ...

不确定你是否在主线上。

我并没有说你在那些时刻不在主线上,但也没有理由认为你也是。你应该检查并整理出来。

答案 1 :(得分:1)

你有正确的想法;显然在主队列上调度UI更新,遗憾的是你已经调度了错误的函数。

GetAnnotations(根据惯例应为getAnnotations)通过URLSession DataTask进行异步网络调用,并在完成处理程序中返回结果。对于像这样的网络操作,很有可能不会在主队列上调用完成处理程序,这就是这种情况。

由于完成处理程序未在主队列上执行并且您更新了UI,因此会收到错误消息。

您需要在主队列

上调度这些UI操作

例如:

guard let data = data else { return }

DispatchQueue.main.async {
        // parse the json for the locations
        do {
            let mapJSON = try JSONDecoder().decode(parseJsonLocations.self, from: data)
            let user_id = mapJSON.user_info.user_id

            print(user_id)
            print(mapJSON.locations.count)

         // do map 
          let distanceSpan:CLLocationDegrees = 10000
           let userLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat, lng)
            self.mapView.setRegion(MKCoordinateRegionMakeWithDistance(userLocation, distanceSpan, distanceSpan), animated: true)
           self.mapView.delegate = self

            var i = 0

            while i < mapJSON.locations.count {

                let location_id = mapJSON.locations[i].location_id
                let location_name = mapJSON.locations[i].location_name
                let location_lat = mapJSON.locations[i].lat
                let location_lng = mapJSON.locations[i].lng

                let locationsLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location_lat, location_lng)

                let subtitle = "location_id: \(location_id)"

                let userAnnotation = Annotation(title: location_name, subtitle: subtitle, coordinate: locationsLocation)

                self.mapView.addAnnotation( userAnnotation )

                i = i + 1

            }

        } catch {
            print("error trying to convert data to JSON")
            print(error)
        }
}

对于位置管理器委托调用,文档说明:

  

从您启动相应位置服务的线程调用委托对象的方法。该线程本身必须有一个活动的运行循环,就像在应用程序的主线程中找到的那样。

因此,只要您从主队列调用startUpdatingLocation,您的委托回调就会在主队列上