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

可可坚果

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

但是,问题是,从多个总线的阵列中,我只能使单个总线旋转查看xml数据,它总是xml表中正在旋转的第一条总线。早些时候,我甚至在旋转单个总线时都遇到了麻烦,因此用户“ Good Doug”帮助了我,并使它正常运行。您可以在此处看到该帖子:自定义注释图像仅在程序(Swift-iOS)的开头旋转我试图通过为每条总线制作MKAnnotationView数组来使用相同的解决方案。我不确定这是否是正确的方法。如果有人可以帮助我,我会很高兴:)

首先,这是XML工作表的外观(在此示例中,有两种工具,因此我们只需要跟踪其中两种):

<body>
        <vehicle id="3815" routeTag="connector" dirTag="loop" lat="44.98068" lon="-93.18071" secsSinceReport="3" predictable="true" heading="335" speedKmHr="12" passengerCount="16"/>
        <vehicle id="3810" routeTag="connector" dirTag="loop" lat="44.97313" lon="-93.24041" secsSinceReport="3" predictable="true" heading="254" speedKmHr="62" passengerCount="1"/>
</body> 

这是我对单独的Bus类的实现(在Bus.swift文件中)。这可能需要一些改进。

class Bus : MKPointAnnotation, MKAnnotation  {
    var oldCoord : CLLocationCoordinate2D!
    var addedToMap = false

    init(coord: CLLocationCoordinate2D) {
        self.oldCoord = coord
    }
}

这是我的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     
                        }
                }
   }

viewForAnnotation是我实现的:

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {

        let reuseId = "pin\(self.vehicleIndex)"
        busViewArray[self.vehicleIndex] = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)

        if busViewArray[self.vehicleIndex] == nil {          
                self.busViewArray[self.vehicleIndex] = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)       
                busViewArray[vehicleIndex]!.image = imageWithImage(UIImage(named:"arrow.png")!, scaledToSize: CGSize(width: 21.0, height: 21.0))     
                self.view.addSubview(busViewArray[self.vehicleIndex]!)
        }
        else {
                busViewArray[self.vehicleIndex]!.annotation = annotation
        }  
        return busViewArray[self.vehicleIndex]
}

我对自己的viewForAnnotation执行情况表示怀疑我也不确定是否可以有一个MKAnnotationViews数组也许,我对注释视图如何在iOS中工作的理解是错误的。我很高兴能有人帮我解决这个问题,因为我已经坚持了一段时间。即使整个实现需要更改,我也很乐意尝试一下。这是问题的屏幕截图。

屏幕截图

再次请注意,所有的公交车都出现在正确的位置并平稳地移动,但实际上只有其中一辆在旋转。提前致谢。

I don't think it's appropriate for the parsing code to manipulate annotation views directly. You don't know if they're visible, whether they've been instantiated yet, etc. The mapview is responsible for managing the annotation views, not you.

If you need to maintain cross reference between busses and annotations, do that, but don't maintain references to annotation views. Your app's interaction with the annotations should be limited to the annotations themselves. So create an annotation subclass that has a angle property.

class MyAnnotation : MKPointAnnotation {
    @objc dynamic var angle: CGFloat = 0.0
}

Then you can then have the annotation view subclass "observe" the custom annotation subclass, rotating as the annotation's angle changes. For example, in 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)
                }
            }
        }
    }
}

Or in 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)
            }
        }
    }
}

现在,您的应用程序可以维护对已添加到地图的批注的引用,并进行设置,并且angle将在可视的情况下在地图视图中直观地表示。


并且,是一个使用此方法的简单示例:

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示例,请参见此答案的先前版本

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

是否有可能创建一组Java注释的自定义注解?

Xpath的添加一个“自定义字符串”上转移到其他元素

创建一个自定义的Jackson注释

JUnit自定义注释

具有数组元素的Java自定义注释

如何使用在其他类文件中定义的常量作为自定义注释中的默认值

编写带有参数的自定义spring注释?

创建一个自定义类的数组

我如何创建一个自定义ArgumentMatcher来接受其他匹配器的参数?

查找所有具有自定义注释的bean

改造 - 抛出异常java.lang.IllegalArgumentException异常:只有一个编码注释允许

moshi自定义限定符注释仅在一个属性上序列化null

使带有数组的自定义方法打印一个值

Pandas数据框:使用其他2列创建一个新列,该列是自定义函数

定义自定义验证注释以扩展现有的注释-JavaEE

插入只有一个元素的自定义类型的表列

如何在ggplot中旋转自定义注释?

具有动态参数的自定义注释

具有自定义注释的目的

带有自定义注释的Guice动态注入

自定义注释-定义@Target

@SuppressWarnings和其他自定义注释在内部如何工作?

如何使用其他自定义指令中的元素标签作为另一个自定义指令的模板

自定义Java注释-没有匹配的ElementType

在自定义注释中使用数组

如何添加几个自定义异常并将所有其他url-s重定向到htaccess中的另一个url?

如何在 Scala 案例类的字段成员上列出注释(自定义 Java 注释和其他注释)

仅在 ggplot 的一个方面创建自定义注释

在 Xamarin.Mac 中从其他人打开一个新的自定义窗口