只有一个(自定义)注释从一系列其他注释中轮换

2024-03-15

我的应用程序即将进入最后阶段,该阶段显示公交车的实时地图。所以,基本上,我有一个计时器,它定期从提供公交车实时位置的 xml 表中获取公交车的纬度和经度。我能够设置 xml 解析器、为公交车的移动设置动画并为公交车设置自定义(箭头)图像。

然而,问题是,从多辆巴士的数组中,我只能让单辆巴士旋转。查看 xml 数据,它始终是 xml 工作表中的第一条总线正在旋转。早些时候,我在旋转一辆公交车时遇到了麻烦,所以用户“Good Doug”帮助了我,我才得以让它工作。您可以在这里查看该帖子:自定义注释图像仅在程序开始时旋转(Swift-iOS) https://stackoverflow.com/questions/33163155/custom-annotation-image-rotates-only-at-the-beginning-of-the-program-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 中如何工作的理解是错误的。如果有人能帮助我解决这个问题,我会很高兴,因为我已经坚持了一段时间了。即使整体实现需要改变,我也很乐意尝试一下。这是问题的屏幕截图。

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


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

如果您需要维护总线和注释之间的交叉引用,请执行此操作,但不要维护对注释视图的引用。您的应用程序与注释的交互应仅限于注释本身。因此创建一个注释子类,它具有angle财产。

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

然后,您可以让注释视图子类“观察”自定义注释子类,并随着注释的旋转而旋转angle变化。例如,在 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)
            }
        }
    }
}

现在,您的应用程序可以维护对已添加到地图中的注释的引用,并设置它们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)
    }
}

See 此答案的先前修订版 https://stackoverflow.com/revisions/33464788/2以 Swift 2 为例。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

只有一个(自定义)注释从一系列其他注释中轮换 的相关文章

随机推荐

  • 仅在一个WebLogic集群节点上运行@Scheduled任务?

    我们正在集群 WebLogic 10 3 4 环境中运行一个 Spring 3 0 x Web 应用程序 war 其中包含夜间 Scheduled 作业 但是 当应用程序部署到每个节点时 使用 AdminServer 的 Web 控制台中的
  • 超时后中止 Rust 中的评估

    我有一个 Rust 函数 不是我写的 它要么以毫秒为单位返回 要么在失败前等待约 10 分钟 我想将对这个函数的调用包装在返回一个Option这是None如果运行时间超过 10 秒 则包含结果 如果运行时间较短 然而 我还没有找到任何方法来
  • Kotlin 中的记忆功能

    我有一个带有实例方法 buildHierarchyUncached 的现有类 其签名可以在下面找到 private fun buildHierarchyUncached date LocalDate Node 我想向公众提供function
  • 语音回声问题

    我正在尝试使用 Adob e Flex 构建一个视频聊天程序 但回声存在一个巨大的问题 如果参与者没有使用耳机 他们所说的一切都会产生回声 更糟糕的是 它们实际上可以创建回声的正反馈循环 直到麦克风静音为止该循环不会结束 有没有人在 Fle
  • 根据 WooCommerce 结账中的分类术语限制支付网关

    在我的 WooCommerce 商店中 仅当产品具有类别 ID 266 的特定产品类别时 我想限制并显示支付网关 支票 现在我有了这个代码片段 但它的作用相反 它在结账时禁用了特定产品类别的网关 add filter woocommerce
  • JQuery UI 可拖动:超出一侧的限制

    我正在使用 JQuery UI 来实现可调整大小 可拖动的元素 现在我想为这些元素定义一个包含 限制在三个 边上的调整大小 拖动 例如 看看这个JSFiddle 示例 http jsfiddle net zuul e2yfC 5 您可以看到
  • 使用 alamofire 的多部分/表单数据

    我正在进行 post API 调用 并且需要使用 multipart form data 我知道如何使用 JSON 进行调用 但我不熟悉 multipart form data 使用 JSON 这是一个超级简单的调用 只需创建一个类型参数
  • 用于更新 JTable 中给定单元格/列并增加焦点的侦听器类型

    我正在尝试使用预定义第一列的 JTable 用户仅将数据输入到第二列 数量 然后 我通过将 服务 列和 数量 列相乘来计算最终收入 并将其显示在第三列 收入 中 Service Quantity Income 40 00 X 40 00 3
  • Java:HashMap 大小是“质数”还是“2 的幂”?

    许多书籍和教程都说哈希表的大小必须是素数才能将键均匀分布在所有桶中 但是Java的HashMap始终使用 2 的幂的大小 难道不应该使用素数吗 作为哈希表大小 质数 或 2 的幂 哪个更好 使用 2 的幂可以有效地屏蔽哈希码的最高位 因此
  • Blend 2 sp1 中的 WPF 视觉状态管理器

    谁能向我解释如何让视觉状态管理器与 WPF 应用程序一起使用 它刚刚被添加到新的 wpftoolkit 中 我按照说明安装了它 但即使是示例也没有显示 VSM 在 silverlight 中它可以工作 但在 WPF 中不行 如果安装了最新的
  • Oracle 上个月的日期函数

    我有下面的查询 其中日期是硬编码的 我的目标是删除编码日期 查询运行时应提取上个月的数据 select count distinct switch id from email protected cdn cgi l email protec
  • a:active a href 不起作用

    我正在尝试在 a href 上应用 css 基本上我需要在单击 a href 时应用与悬停相同的样式以指示用户所在的页面 有任何想法吗 active意思是 被点击 或以其他方式激活 时 它并不意味着 链接到当前页面 CSS 与之最接近的是
  • 更改 UICollectionViewCell 中的标签位置

    我们有一个UICollectionView故事板上有一个原型单元 该细胞有一个UILabel label 其中 其定位没有自动布局 我们有条件地设置标签的框架 collectionView cellForItemAtIndexPath 像这
  • 根据 Beautifulsoup 中的内容排除标签

    我正在抓取类似于以下内容的 html 数据 div class target content p the content of the p p p the content of the p p p p div
  • G++ CAS (__sync_val_compare_and_swap) 问题需要解释

    这让我很头疼 我正在尝试实现一些 无锁 代码 并使用 CAS gcc sync val compare and swap 来完成繁重的工作 我的问题可以用下面的代码来显示 volatile bool lock void locktest v
  • 如何使用 vba 更新 powerpoint 2010 中嵌入的 Excel 链接

    我的问题是我已将图表粘贴到微软幻灯片软件 questions tagged powerpoint我正在尝试通过 Excel VBA 更新嵌入的链接 我尝试过下面的代码但失败了 code 1 AppPPT Presentations Open
  • jQuery/javascript 替换标签类型

    有没有一种简单的方法可以循环遍历所有 td 标签并将它们更改为 th ETC 我当前的方法是用 th 包裹它们 然后删除 td 但随后我会丢失其他属性等 jQuery replaceTagName 以下是一个 jQuery 插件 用于替换
  • Woocommerce 自定义产品列表分页

    我有这样的代码用于在 woocommerce 中获取和显示产品 args array post type gt product posts per page gt 30 loop new WP Query args if loop gt h
  • 421 4.3.0 临时系统问题。稍后重试 (10) [重复]

    这个问题在这里已经有答案了 我正在使用nodemailer npm 包和gmail 作为nodejs 中的服务 对于某些电子邮件 我收到以下错误 Data command failed 421 4 3 0 Temporary System
  • 只有一个(自定义)注释从一系列其他注释中轮换

    我的应用程序即将进入最后阶段 该阶段显示公交车的实时地图 所以 基本上 我有一个计时器 它定期从提供公交车实时位置的 xml 表中获取公交车的纬度和经度 我能够设置 xml 解析器 为公交车的移动设置动画并为公交车设置自定义 箭头 图像 然