Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/118.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/http/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 只有一个(自定义)注释从其他注释数组旋转_Ios_Swift_Rotation_Uiviewanimation_Mkannotationview - Fatal编程技术网

Ios 只有一个(自定义)注释从其他注释数组旋转

Ios 只有一个(自定义)注释从其他注释数组旋转,ios,swift,rotation,uiviewanimation,mkannotationview,Ios,Swift,Rotation,Uiviewanimation,Mkannotationview,我的应用程序快到最后阶段了,它显示了公交车的实时地图。因此,基本上,我有一个计时器,它定期从提供总线实时位置的xml表中获取总线的纬度和经度。我能够设置xml解析器,设置公交车运动的动画,并为公交车设置自定义(箭头)图像 然而,问题是,从多条总线阵列中,我只能得到一条总线来旋转。查看xml数据,它总是来自旋转的xml表的第一条总线。早些时候,我在旋转一辆公交车时遇到了麻烦,所以用户“GoodDoug”帮了我一把,我就可以让它工作了。你可以在这里看到帖子:。我尝试使用相同的解决方案,为每个总线创建

我的应用程序快到最后阶段了,它显示了公交车的实时地图。因此,基本上,我有一个计时器,它定期从提供总线实时位置的xml表中获取总线的纬度和经度。我能够设置xml解析器,设置公交车运动的动画,并为公交车设置自定义(箭头)图像

然而,问题是,从多条总线阵列中,我只能得到一条总线来旋转。查看xml数据,它总是来自旋转的xml表的第一条总线。早些时候,我在旋转一辆公交车时遇到了麻烦,所以用户“GoodDoug”帮了我一把,我就可以让它工作了。你可以在这里看到帖子:。我尝试使用相同的解决方案,为每个总线创建一个MKAnnotationView数组。我不确定这是否是正确的方法。如果有人能帮我解决这个问题,我会很高兴:)

首先,这是XML表的样子(在本例中,有两个车辆,所以我们只需要跟踪其中的两个):

这是我的ViewController.swift中的代码-

var busArray: [Bus!] = []           //Array to hold custom defined "Bus" types (from Bus.swift file)
var busViewArray : [MKAnnotationView?] = [nil, nil]                //Array to hold MKAnnotationView of each bus. We're assuming 2 buses are active in this case.
var vehicleCount = 0                // variable to hold the number of buses
var vehicleIndex = 0                // variable to check which bus the xml parser is currently on.
var trackingBusForTheVeryFirstTime = true

// My xml parser function:
func parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: NSDictionary!) {
   if (elementName == "vehicle" ) {             
                let latitude = attributeDict["lat"]?.doubleValue                // Get latitude of current bus
                let longitude = attributeDict["lon"]?.doubleValue                // Get longitude of current bus
                let dir = attributeDict["heading"]?.doubleValue                        // Get direction of current bus

                var currentCoord = CLLocationCoordinate2DMake(latitude!, longitude!)                // Current coordinates of the bus

                // Checking the buses for the VERY FIRST TIME. This is usually the start of the program
                if (trackingBusForTheVeryFirstTime || vehicleCount == 0) {                  
                        let bus = Bus(coord: currentCoord)
                        self.busArray.append(bus)                        // Put current bus to the busArray
                        self.vehicleCount++                                        
                }
                else {        // UPDATE BUS Location. (Note: this is not the first time)

                        // If index exceeded count, that means number of buses changed, so we need to start over
                        if (self.vehicleIndex >= self.vehicleCount) {                         
                                self.trackingBusForTheVeryFirstTime = true                       
                                // Reset count and index for buses
                                self.vehicleCount = 0
                                self.vehicleIndex = 0
                                return
                        }

                        let oldCoord = busArray[vehicleIndex].oldCoord                   

                        if (oldCoord.latitude == latitude && oldCoord.longitude == longitude) {
                                // if oldCoordinates and current coordinates are the same, the bus hasn't moved. So do nothing.
                                return
                        }
                        else {                       
                                // Move and Rotate the bus:                       
                                UIView.animateWithDuration(0.5) {
                                        self.busArray[self.vehicleIndex].coordinate = CLLocationCoordinate2DMake(latitude!, longitude!)

                                        // if bus annotations have not been added to the map yet, add them:
                                        if (self.busArray[self.vehicleIndex].addedToMap == false) {
                                                self.map.addAnnotation(self.busArray[self.vehicleIndex])
                                                self.busArray[self.vehicleIndex].addedToMap = true
                                                return
                                        }

                                        if let pv = self.busViewArray[self.vehicleIndex] {
                                                pv.transform = CGAffineTransformRotate(self.map.transform, CGFloat(self.degreesToRadians(dir!)))         // Rotate bus
                                        }                          
                                }
                                if (vehicleIndex < vehicleCount - 1) 
                                        self.vehicleIndex++
                                }
                                else {
                                        self.vehicleIndex = 0
                                }
                                return     
                        }
                }
   }
我对我的
注释视图
的实施表示怀疑。我也不确定是否可以使用
MKAnnotationView
s数组。也许,我对注释视图在iOS中如何工作的理解是错误的。如果有人能帮我解决这个问题,我会很高兴的,因为我已经坚持了一段时间了。即使整体实现需要更改,我也很乐意尝试一下。这是问题的截图


再一次,请注意,所有的巴士都出现在正确的位置,移动平稳,但只有一辆真正旋转。提前谢谢

我认为解析代码不适合直接操作注释视图。您不知道它们是否可见,是否已经实例化,等等。mapview负责管理注释视图,而不是您

如果需要维护总线和注释之间的交叉引用,可以这样做,但不要维护对注释视图的引用。应用程序与注释的交互应仅限于注释本身。因此,创建一个具有
angle
属性的注释子类

class MyAnnotation : MKPointAnnotation {
    @objc dynamic var angle: CGFloat = 0.0
}
然后,您可以让注释视图子类“观察”自定义注释子类,并随着注释的角度的变化而旋转。例如,在Swift 4中:

class MyAnnotationView : MKAnnotationView {

    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        addAngleObserver()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        addAngleObserver()
    }

    // Remember, since annotation views can be reused, if the annotation changes,
    // remove the old annotation's observer, if any, and add new one's.

    override var annotation: MKAnnotation? {
        willSet { token = nil        }
        didSet  { addAngleObserver() }
    }

    // add observer

    var token: NSKeyValueObservation!

    private func addAngleObserver() {
        if let annotation = annotation as? MyAnnotation {
            transform = CGAffineTransform(rotationAngle: annotation.angle)
            token = annotation.observe(\.angle) { [weak self] annotation, _ in
                UIView.animate(withDuration: 0.25) {
                    self?.transform = CGAffineTransform(rotationAngle: annotation.angle)
                }
            }
        }
    }
}
或在Swift 3中:

private var angleObserverContext = 0

class MyAnnotationView : MKAnnotationView {
    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        addAngleObserver()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        addAngleObserver()
    }

    // add observer

    private func addAngleObserver() {
        if let annotation = annotation as? MyAnnotation {
            transform = CGAffineTransform(rotationAngle: annotation.angle)
            annotation.addObserver(self, forKeyPath: #keyPath(MyAnnotation.angle), options: [.new, .old], context: &angleObserverContext)
        }
    }

    // remove observer

    private func removeAngleObserver() {
        if let annotation = annotation as? MyAnnotation {
            annotation.removeObserver(self, forKeyPath: #keyPath(MyAnnotation.angle))
        }
    }

    // remember to remove observer when annotation view is deallocated

    deinit {
        removeAngleObserver()
    }

    // Remember, since annotation views can be reused, if the annotation changes,
    // remove the old annotation's observer, if any, and add new one's.

    override var annotation: MKAnnotation? {
        willSet { removeAngleObserver() }
        didSet  { addAngleObserver()    }
    }

    // Handle observation events for the annotation's `angle`, rotating as appropriate

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard context == &angleObserverContext else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
            return
        }

        UIView.animate(withDuration: 0.5) {
            if let angleNew = change![.newKey] as? CGFloat {
                self.transform = CGAffineTransform(rotationAngle: angleNew)
            }
        }
    }
}
现在,您的应用程序可以维护对已添加到地图的注释的引用,并设置它们的
角度
,这将在地图视图中视情况显示


下面是一个快速而肮脏的例子:

class ViewController: UIViewController {

    @IBOutlet weak var mapView: MKMapView!

    var annotation = MyAnnotation()

    private let reuseIdentifer = Bundle.main.bundleIdentifier! + ".annotation"

    private lazy var manager: CLLocationManager = {
        let manager = CLLocationManager()
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        return manager
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        mapView.register(MyAnnotationView.self, forAnnotationViewWithReuseIdentifier: reuseIdentifer)

        manager.requestWhenInUseAuthorization()
        manager.startUpdatingHeading()
        manager.startUpdatingLocation()

        mapView.addAnnotation(annotation)
    }

}

extension ViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        if annotation is MKUserLocation { return nil }

        return mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifer, for: annotation)
    }
}

extension ViewController: CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last,
            location.horizontalAccuracy >= 0 else {
                return
        }
        annotation.coordinate = location.coordinate
    }

    func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
        guard newHeading.headingAccuracy >= 0 else { return }
        annotation.angle = CGFloat(newHeading.trueHeading * .pi / 180)
    }
}


请参阅Swift 2示例。

如果您在“pv.transform=CGAffineTransformRotate(self.map.transform,CGFloat(self.degreestoradis(dir!))//Rotate bus”设置断点,那么您的代码是否在每次迭代中都会找到pv值?只是选中了。代码没有在每次迭代中为pv找到一个值。对于为annotationView添加rotate或bearing func的MapKit开发人员来说,这是否太难了?我认为制作一张完整的地图肯定比他们做的更难……嗨,罗布,你说的很有道理。因此,我尝试如下设置每个总线注释的角度:
busaray[vehicleIndex].angle=CGFloat(degreestorarians!(dir!))
。但是,观察者没有被调用,总线也没有旋转。这就是方法,还是我必须做些别的事情?开始在自定义注释子类和自定义注释视图子类中设置断点,并找出哪里出了问题。例如,您确定
viewForAnnotation
正在实例化这个正确的子类吗?它是否正在点击
addAngleObserver
行?您需要进行更多的调试,并找出哪里出了问题。但是如果观察者没有被触发,可能是因为您没有添加观察者,可能是因为视图或其注释不是正确的类,或者您忽略了
动态
限定符或诸如此类的内容。但是我使用上面的代码来旋转多个注释视图,效果很好。谢谢您,好先生!让它发挥作用。刚刚更改了注释视图中的一些内容Rob你能举个例子说明正确的使用方法吗?
class MyAnnotationView : MKAnnotationView {

    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        addAngleObserver()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        addAngleObserver()
    }

    // Remember, since annotation views can be reused, if the annotation changes,
    // remove the old annotation's observer, if any, and add new one's.

    override var annotation: MKAnnotation? {
        willSet { token = nil        }
        didSet  { addAngleObserver() }
    }

    // add observer

    var token: NSKeyValueObservation!

    private func addAngleObserver() {
        if let annotation = annotation as? MyAnnotation {
            transform = CGAffineTransform(rotationAngle: annotation.angle)
            token = annotation.observe(\.angle) { [weak self] annotation, _ in
                UIView.animate(withDuration: 0.25) {
                    self?.transform = CGAffineTransform(rotationAngle: annotation.angle)
                }
            }
        }
    }
}
private var angleObserverContext = 0

class MyAnnotationView : MKAnnotationView {
    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        addAngleObserver()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        addAngleObserver()
    }

    // add observer

    private func addAngleObserver() {
        if let annotation = annotation as? MyAnnotation {
            transform = CGAffineTransform(rotationAngle: annotation.angle)
            annotation.addObserver(self, forKeyPath: #keyPath(MyAnnotation.angle), options: [.new, .old], context: &angleObserverContext)
        }
    }

    // remove observer

    private func removeAngleObserver() {
        if let annotation = annotation as? MyAnnotation {
            annotation.removeObserver(self, forKeyPath: #keyPath(MyAnnotation.angle))
        }
    }

    // remember to remove observer when annotation view is deallocated

    deinit {
        removeAngleObserver()
    }

    // Remember, since annotation views can be reused, if the annotation changes,
    // remove the old annotation's observer, if any, and add new one's.

    override var annotation: MKAnnotation? {
        willSet { removeAngleObserver() }
        didSet  { addAngleObserver()    }
    }

    // Handle observation events for the annotation's `angle`, rotating as appropriate

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard context == &angleObserverContext else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
            return
        }

        UIView.animate(withDuration: 0.5) {
            if let angleNew = change![.newKey] as? CGFloat {
                self.transform = CGAffineTransform(rotationAngle: angleNew)
            }
        }
    }
}
class ViewController: UIViewController {

    @IBOutlet weak var mapView: MKMapView!

    var annotation = MyAnnotation()

    private let reuseIdentifer = Bundle.main.bundleIdentifier! + ".annotation"

    private lazy var manager: CLLocationManager = {
        let manager = CLLocationManager()
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        return manager
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        mapView.register(MyAnnotationView.self, forAnnotationViewWithReuseIdentifier: reuseIdentifer)

        manager.requestWhenInUseAuthorization()
        manager.startUpdatingHeading()
        manager.startUpdatingLocation()

        mapView.addAnnotation(annotation)
    }

}

extension ViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        if annotation is MKUserLocation { return nil }

        return mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifer, for: annotation)
    }
}

extension ViewController: CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last,
            location.horizontalAccuracy >= 0 else {
                return
        }
        annotation.coordinate = location.coordinate
    }

    func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
        guard newHeading.headingAccuracy >= 0 else { return }
        annotation.angle = CGFloat(newHeading.trueHeading * .pi / 180)
    }
}