MKMapKit 可拖动注释和绘制多边形

2023-11-29

我目前正在尝试允许用户向地图添加图钉,然后绘制连接这些图钉的多边形。但是我想扩展它以允许用户能够拖动引脚并且多边形将相应地更新。 MKMapView 根据坐标数组中的排列从坐标数组中绘制多边形(如果我没有记错的话)。我现在面临的问题是在用户重新定位引脚后如何更新多边形。

var touchCoordinatesWithOrder: [(coordinate: CLLocationCoordinate2D, order: Int)] = []
var counter = 0

func addLongPressGesture() {
    let longPressRecogniser = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
    longPressRecogniser.minimumPressDuration = 1.0
    mapView.addGestureRecognizer(longPressRecogniser)

}

func handleLongPress(gestureRecognizer: UIGestureRecognizer) {
    if gestureRecognizer.state != .Began {
        return
    }

    let touchPoint = gestureRecognizer.locationInView(self.mapView)
    let touchMapCoordinate = mapView.convertPoint(touchPoint, toCoordinateFromView: mapView)

    let annotation = MKPointAnnotation()
    annotation.coordinate = touchMapCoordinate
    mapView.addAnnotation(annotation)

    touchCoordinatesWithOrder.append((coordinate: touchMapCoordinate, order: counter))
    counter += 1

}


@IBAction func drawAction(sender: AnyObject) {
    if touchCoordinatesWithOrder.count <= 2 {
        print("Not enough coordinates")
        return
    }

    var coords = [CLLocationCoordinate2D]()
    for i in 0..<touchCoordinatesWithOrder.count {
        coords.append(touchCoordinatesWithOrder[i].coordinate)
    }

    let polygon = MKPolygon(coordinates: &coords, count: coords.count)
    mapView.addOverlay(polygon)
    counter = 0
}

func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
    // if the user repositioned pin number2 then how to I update my array?
}

func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
    if overlay is MKPolygon {
        let polygonView = MKPolygonRenderer(overlay: overlay)
        polygonView.strokeColor = UIColor.blackColor()
        polygonView.lineWidth = 0.5
        return polygonView
    }
    return MKPolylineRenderer()
}

要使引脚可拖动,您需要设置draggable = true on the MKAnnotationView。实施viewForAnnotation并出列或创建注释,然后设置draggable = true。确保MKMapView设置委托,否则不会调用任何委托方法。

您可能还会发现将注释存储在数组中比仅存储坐标更容易。地图视图保留对数组中注释的引用,因此当点在地图中移动时,注释会自动更新。

你的问题没有说是否需要画一条路径around点,或通过点。如果您想绘制围绕点的叠加层,那么您还需要计算坐标的凸包。代码示例就是这样做的,尽管它很容易删除。

Example:

class MapAnnotationsOverlayViewController: UIViewController, MKMapViewDelegate {

    @IBOutlet var mapView: MKMapView!

    // Array of annotations - modified when the points are changed.
    var annotations = [MKPointAnnotation]()

    // Current polygon displayed in the overlay.
    var polygon: MKPolygon?

    override func viewDidLoad() {
        super.viewDidLoad()
        mapView.delegate = self    
        addLongPressGesture()
    }

    func addLongPressGesture() {
        let longPressRecogniser = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
        longPressRecogniser.minimumPressDuration = 0.25
        mapView.addGestureRecognizer(longPressRecogniser)

    }

    func handleLongPress(gestureRecognizer: UIGestureRecognizer) {

        guard gestureRecognizer.state == .Began else {
            return
        }

        let touchPoint = gestureRecognizer.locationInView(self.mapView)
        let touchMapCoordinate = mapView.convertPoint(touchPoint, toCoordinateFromView: mapView)


        let annotation = MKPointAnnotation()

        // The annotation must have a title in order for it to be selectable.
        // Without a title the annotation is not selectable, and therefore not draggable.
        annotation.title = "Point \(annotations.count)"
        annotation.coordinate = touchMapCoordinate
        mapView.addAnnotation(annotation)

        // Add the new annotation to the list.
        annotations.append(annotation)

        // Redraw the overlay.
        updateOverlay()
    }

    @IBAction func drawAction(sender: AnyObject) {
        updateOverlay()
    }

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

        var view = mapView.dequeueReusableAnnotationViewWithIdentifier("pin")

        if let view = view {
            view.annotation = annotation
        }
        else {
            view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")

            // Allow the pin to be repositioned.
            view?.draggable = true
        }

        return view
    }

    func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {

    // The map view retains a reference to the same annotations in the array.
    // The annotation in the array is automatically updated when the pin is moved.

        updateOverlay()
    }

    func updateOverlay() {

        // Remove existing overlay.
        if let polygon = self.polygon {
            mapView.removeOverlay(polygon)
        }

        self.polygon = nil

        if annotations.count < 3 {
            print("Not enough coordinates")
            return
        }

        // Create coordinates for new overlay.
        let coordinates = annotations.map({ $0.coordinate })

        // Sort the coordinates to create a path surrounding the points.
        // Remove this if you only want to draw lines between the points.
        var hull = sortConvex(coordinates)

        let polygon = MKPolygon(coordinates: &hull, count: hull.count)
        mapView.addOverlay(polygon)

        self.polygon = polygon
    }

    func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
        if overlay is MKPolygon {
            let polygonView = MKPolygonRenderer(overlay: overlay)
            polygonView.strokeColor = UIColor.blackColor()
            polygonView.lineWidth = 0.5
            return polygonView
        }
        return MKPolylineRenderer()
    }
}

这是凸包排序算法(改编自此GitHub 上的要点).

func sortConvex(input: [CLLocationCoordinate2D]) -> [CLLocationCoordinate2D] {

    // X = longitude
    // Y = latitude

    // 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
    // Returns a positive value, if OAB makes a counter-clockwise turn,
    // negative for clockwise turn, and zero if the points are collinear.
    func cross(P: CLLocationCoordinate2D, _ A: CLLocationCoordinate2D, _ B: CLLocationCoordinate2D) -> Double {
        let part1 = (A.longitude - P.longitude) * (B.latitude - P.latitude)
        let part2 = (A.latitude - P.latitude) * (B.longitude - P.longitude)
        return part1 - part2;
    }

    // Sort points lexicographically
    let points = input.sort() {
        $0.longitude == $1.longitude ? $0.latitude < $1.latitude : $0.longitude < $1.longitude
    }

    // Build the lower hull
    var lower: [CLLocationCoordinate2D] = []
    for p in points {
        while lower.count >= 2 && cross(lower[lower.count-2], lower[lower.count-1], p) <= 0 {
            lower.removeLast()
        }
        lower.append(p)
    }

    // Build upper hull
    var upper: [CLLocationCoordinate2D] = []
    for p in points.reverse() {
        while upper.count >= 2 && cross(upper[upper.count-2], upper[upper.count-1], p) <= 0 {
            upper.removeLast()
        }
        upper.append(p)
    }

    // Last point of upper list is omitted because it is repeated at the
    // beginning of the lower list.
    upper.removeLast()

    // Concatenation of the lower and upper hulls gives the convex hull.
    return (upper + lower)
}

这就是凸包排序的样子(围绕点绘制的路径):

Convex hull surrounding points

这是没有排序的情况(按顺序从点到点绘制的路径):

Point-to-point path

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

MKMapKit 可拖动注释和绘制多边形 的相关文章

  • Xcode 8 / Swift 3:“UIViewController 类型的表达式?未使用”警告

    我有以下函数 它之前编译得很干净 但在 Xcode 8 中生成警告 func exitViewController navigationController popViewController animated true UIViewCon
  • SwiftUI:发送电子邮件

    在正常情况下UIViewController在 Swift 中 我使用此代码发送邮件 let mailComposeViewController configuredMailComposeViewController mailCompose
  • 在 UIScrollview 上显示缩略图的最佳方法是什么(从服务器下载)

    我想在 UIScrollview 如照片应用程序 上显示许多图像 作为缩略图 所有图像将从服务器下载 据我所知 有几种选择 1 通过创建 UIImageviews 然后将它们添加为主滚动视图上的子视图 2 通过子类化一个UIView类 然后
  • 当 isUserInteractionEnabled false 时,SKSpriteNode 不会让触摸通过

    我正在尝试在 SpriteKit 中创建一个覆盖层 方法是使用SKSpriteNode 但是 我希望触摸穿过覆盖层 所以我设置isUserInteractionEnabled为假 然而 当我这样做时 SKSpriteNode似乎仍然吸收所有
  • 如何检测用户是否第一次打开应用程序[重复]

    这个问题在这里已经有答案了 是否可以检测用户是否是第一次打开iOS应用程序 使用Objective C 我想在用户第一次打开应用程序时显示欢迎消息 但之后不再向他们显示 我正在寻找类似的东西 BOOL firstTime AppDelega
  • 如何使用 CNContacts 快速获取手机号码?

    我有一些代码可以检索用户联系人中的所有电话号码 但只想过滤掉手机号码 目前 我只是通过将第一个数字为 或第二个数字为 7 的数字添加到数组中来实现此目的 如下所示 func findContacts gt CNContact let key
  • SwiftUI 意外地自动弹出 NavigationLink

    我有一个简单的用例 其中一个屏幕使用 NavigationLink 推送另一个屏幕 iOS 14 5 有一个奇怪的行为 即推送的屏幕在被推送后立即弹出 Code NavigationLink destination EmptyView Em
  • 在 iOS 应用程序中拨打电话

    我有一些代码尝试在应用程序中进行调用 但它似乎不起作用 UIApplication myApp UIApplication sharedApplication NSString theCall NSString stringWithForm
  • Swift:设置协议的可选属性

    如何设置协议的可选属性 例如 UITextInputTraits 有许多可选的读 写属性 当我尝试以下操作时 出现编译错误 无法分配给 textInputTraits 中的 keyboardType func initializeTextI
  • iPhone X 将对象底部与安全区域对齐会破坏其他设备上的外观

    关于 iPhone X 自动布局怪癖的问题 我有两个按钮 以前这些按钮将与超级视图底部对齐 偏移量为 20 以免它们接触屏幕底部 此后我将链接更改为安全区域而不是超级视图 Here s the original setup Looks go
  • UIViewController 不旋转到横向

    在许多情况下需要旋转控制器但不起作用 现在我遇到了相反的问题 它正在旋转 我想禁用它 在那个 ViewController 中我有这个 BOOL shouldAutorotateToInterfaceOrientation UIInterf
  • 我的 UICollectionView 无法使用 Swift 平滑滚动

    我有一个CollectionView它使单元出队取决于message类型 例如 文本 图像 我遇到的问题是当我向上 向下滚动时滚动确实很不稳定 因此用户体验不是很好 这仅在第一次加载单元格时发生 之后滚动就会平滑 我有什么想法可以解决这个问
  • 如何使用phonegap在iOS应用程序中防止键盘推送webview

    当屏幕底部的输入字段获得焦点时 键盘会向上推我的网络视图 并且页面的上部不再可见 我想防止键盘推高网络视图 有人有主意吗 对焦 设置window scrollTo 0 0 这可以防止键盘完全推高 webview input on focus
  • 在 iOS 上使用 RNCryptor 异步解密大文件

    我需要在 iOS 上使用 RNCryptor 异步解密一个大文件 以便显示进度条 我在任何地方都找不到示例 因此尝试了我猜对的方法 但是 我想出的方法不起作用 解密器的处理程序从未被调用 并且线程在发送所有数据后因 EXC BAD ADDR
  • NSPredicate IN 从数组元素查询

    对于一个古怪的标题表示歉意 我有一个 Int 数组 我想定义一个 NSPredicate 来过滤掉 connectionType 等于数组中包含的值的项目 所以基本上是这样的 fetchRequest predicate NSPredica
  • ios8 键盘高度有所不同

    我使用下面的代码来获取键盘高度 该高度在带有 ios8 的 iPhone 5s 设备中与带有 ios7 的 IPhone4s 设备中有所不同 因此 当我在带有 ios8 的 iPhone5s 中点击它时 我的文本字段移动得非常高 而相同的代
  • UIViewController 内的 UIsearchController 使用自动布局

    有没有人成功实施过UIViewController其中包含两个UISearchController searchBar and a UItableView使用自动布局来布局所有内容 我正在尝试实现类似的目标1密码 https itunes
  • 使用日期 Swift 3 对字典数组进行排序

    我有一个名为 myArray 的数组 其中添加了字典 我希望该字典按时间排序 这是字典中的键 那个时间是在 String 中 时间的日期格式为 yyyy MM dd HH mm ss 我尝试使用下面的代码解决方案 但给出了 从 字符串转换
  • 如何在 UITableView 的 switch 语句中创建变量?

    我正在构建一个包含三个部分的 tableView 我已经完成了前两个工作 但最后一个有点阻力 我的问题似乎涉及尝试在 switch 语句中声明变量 实际上是嵌套的 switch 语句 据我所知 这不是一个好主意 但在这种情况下 这似乎是唯一
  • 节拍匹配算法

    我最近开始尝试创建一个移动应用程序 iOS Android 它将自动击败比赛 http en wikipedia org wiki Beatmatching http en wikipedia org wiki Beatmatching 两

随机推荐

  • 用于在 Visual Studio 中为整个项目或解决方案定义带有参数的预处理器宏的选项

    我希望一些具有参数的宏在项目中的所有文件中可用 或者更好地提供完整的解决方案 在 VS2010 中 如果我使用类似的方法将它们添加到属性 gt 配置属性 gt C C gt 预处理器中的预处理器定义中 SQUARE x x x CUBE x
  • [:-gt:需要一元运算符

    我写的 Bash 不多 所以我对如何解决这个问题有点困惑 我需要检查命令返回的值是否大于x 当它运行时我得到 gt unary operator expected我无法修复 这是我的脚本 bin sh ERROR 0 PHPCPDLevel
  • 有没有更简单的方法将 int 转换为 unicode?

    我目前正在我的 javascript 中执行一个大的 switch 语句来转换 case 176 char u00B0 break case 177 char u00B1 break case 178 char u00B2 break ca
  • AES 在 iOS (Obj-C) 和 Android (Java) 中获得不同的结果

    我对这种加密完全是新手 但我有一个 Java 应用程序和一个 iOS 我希望它们都能够将文本加密为相同的结果 我用的是AES 我找到了这些代码 当然做了一些修改 但它们返回了不同的结果 iOS代码 NSData AESEncryptionW
  • 如何修改SVG图像作为背景图像的填充颜色?

    将 SVG 输出直接与页面代码内联放置 我可以简单地使用 CSS 修改填充颜色 如下所示 polygon mystar fill blue circle mycircle fill green 这很好用 但是我正在寻找一种方法来修改 SVG
  • 两个不同的 jenkins 构建器可以存在于同一个 hpi 中并共享相同的全局配置吗?

    我需要创建两个不同的 Jenkins Builder 类 每个都执行不同的操作 并且每个都需要自己的 jelly 但是 两者都需要相同的全局配置 global jelly 该配置指定主机和一些用户凭据 两种构建器类型的实例在执行执行期间将使
  • 向量值的不同组合

    假设我有一个由 n 个值组成的向量 我想获得其值的不同组合 例如 如果我有 vect a b c 我想要的不同组合是 a b c a b a c b c a b c 请注意 例如 a b 与 b a 相同 因此我不需要同时保留它们 计数自0
  • 如何在 Eclipse 中的可执行 .jar 文件中包含资源文件夹?

    我需要创建一个应用程序 使用各种参数 例如高度 体积或底面积 对各种类型的多边形进行排序 具有多边形参数 排序类型 排序方法的文件名参数将通过命令行传递 该文件位于项目中 src 文件夹外部的资源文件夹中 我已经实现了所有程序 当我通过 e
  • 无法动态创建和附加 div 和 span

    我正在编写一个 HTML 代码 其中有一个 div 说y这是在我的 HTML 正文中 有一个按钮 当用户单击此按钮时 我想要执行以下操作 创建另一个 div 类为smallBar 里面这个div 我想创建3个跨度 添加此 总计smallBa
  • 使用所有时区和有/无 DST 的日期进行单元测试

    如何使此单元测试在所有时区中通过 无论 DST 是否处于活动状态 import static org junit Assert import java text SimpleDateFormat import java util Date
  • 包含新的测试目录 Maven Surefire 插件

    现有结构 src test java gt 所有 java 单元测试 Maven Surefire 插件可以轻松获取此信息 现在 除了这些java单元测试用例之外 我还想包括一些groovy测试用例 并且我想将它们放在src test gr
  • Android - 从光标获取专辑艺术家

    我目前正在创建一个音乐播放器 并且正在使用光标检索设备上的音乐 mCursor getContentResolver query MediaStore Audio Media EXTERNAL CONTENT URI requestedCo
  • python-docx - 显示为普通段落的列表

    我正在尝试将数字和项目符号列表插入到现有的 Word 文档中 但是它们显示为普通段落 Open up existing document document Document existing document docx Add style
  • selenium 无法对网页元素进行屏幕截图

    我可以使用 Firefox get screenshot as file 2 png 对整个页面进行屏幕截图 但是当我使用passage screenshot 1 png 对网页元素进行屏幕截图时 它总是会引发此异常 selenium co
  • 嵌入字体和 11 月字体有什么区别?

    在书中我看到了例子 BaseFont bf BaseFont createFont KozMinPro Regular Identity V BaseFont NOT EMBEDDED Font font new Font bf 20 Ve
  • 将 Blazor .NET 6 WASM 部署到 GitHub 页面

    我正在尝试让 Blazor WASM 在 GitHub 页面中工作 我关注了这个视频 https www youtube com watch v nNxII6jvPvQ 我将它部署到这里 扩展 GH 页面 来源在这里 GH 页面源 我收到此
  • 如何用管道描述推荐基线

    我试图找到复合基线中关联的所有组件基线 我可以使用以下方式实现它 cleartool desc fmt rec bls CXp stream My Integration My PVOB I would save the receommen
  • 异步始终等待激活

    我想弄清楚是什么async await关键字是全部 但输出并不是我所期望的 控制台应用程序如下 class Program static void Main string args Console WriteLine Foo called
  • $and 查询没有返回结果

    好吧 这个简直要了我的命 也许已经晚了 我忘记了一些事情 但这应该有效 出于测试目的 我收集了大约 6000 个文档 有一个属性叫Priority在每个实例中其值为 2 以下两个查询分别返回all6000 个文档 Priority gt 1
  • MKMapKit 可拖动注释和绘制多边形

    我目前正在尝试允许用户向地图添加图钉 然后绘制连接这些图钉的多边形 但是我想扩展它以允许用户能够拖动引脚并且多边形将相应地更新 MKMapView 根据坐标数组中的排列从坐标数组中绘制多边形 如果我没有记错的话 我现在面临的问题是在用户重新