MapLibre Logo

MapLibreSwiftUI

用于 MapLibre Native 的 Swift DSL,MapLibre Native 是一个免费的开源交互式矢量地图渲染器,旨在更好地与 SwiftUI 集成,并通常使 MapLibre 更易于使用。

注意:此软件包已从 Stadia Maps 迁移到 MapLibre 组织 🎉 如果您之前安装过此软件包,请参阅 CHANGELOG,了解确保 Xcode 正常运行的步骤。

A screen recording demonstrating the declarative SwiftUI DSL reacting to changes live

此软件包是对 MapLibre API 的重新构想,采用适用于 SwiftUI 的现代 DSL。 1.0 之前的状态意味着我们尚未承诺 API 的稳定性,因为我们非常关心为 SwiftUI 用户找到表达事物的最佳方式。 该软件包对于其支持的 MapLibre iOS API 子集来说是稳健的。 任何破坏性的 API 更改都将反映在发行说明中。

目标

  1. 主要目标:使常见用例变得容易,并使复杂的用例成为可能
    • 轻松将 MapLibre 集成到现代 SwiftUI 应用程序中
    • 添加 标记折线 和类似的注释
    • 通过 手势 与要素进行交互
    • 聚类(常见用例,对于初学者来说相当困难)
    • 覆盖层
    • 动态样式
    • 相机控制
    • 逐向导航(请参阅下面的展示集成)
    • 动画
  2. 防止用户在使用较低级别 API 时犯的大多数常见错误(例如:重复添加同一源)
  3. 更深入的 SwiftUI 集成(例如:SwiftUI 标注视图)

快速入门

在普通的 Xcode 项目中

如果您正在使用 Xcode 项目构建应用程序,则添加软件包依赖项的最简单方法是在“文件”菜单中。 使用存储库 URL 搜索软件包:https://github.com/maplibre/swiftui-dsl

在 Swift 软件包中

将以下内容添加到 Package.swift 的主要依赖项部分。

    .package(url: "https://github.com/maplibre/swiftui-dsl", branch: "main"),

然后,对于每个目标,添加 DSL(仅用于 DSL)或两者都添加(用于 SwiftUI 视图)

    .product(name: "MapLibreSwiftDSL", package: "swiftui-dsl"),
    .product(name: "MapLibreSwiftUI", package: "swiftui-dsl"),

简单示例:折线渲染

然后,您可以像这样在 SwiftUI 视图主体中使用它

import MapLibre
import MapLibreSwiftDSL
import SwiftUI
import CoreLocation

struct PolylineMapView: View {
    // You'll need a MapLibre Style for this to work.
    // You can use https://demotiles.maplibre.org/style.json for basic testing.
    // For a list of commercially supported tile providers, check out https://wiki.openstreetmap.org/wiki/Vector_tiles#Providers.
    // These providers all have their own "house styles" as well as custom styling.
    // You can create your own style or modify others (subject to license restrictions) using https://maplibre.org/maputnik/. 
    let styleURL: URL
    
    // Just a list of waypoints (ex: a route to follow)
    let waypoints: [CLLocationCoordinate2D]

    var body: some View {
        MapView(styleURL: styleURL,
                camera: .constant(.center(waypoints.first!, zoom: 14)))
        {
            // Define a data source.
            // It will be automatically if a layer references it.
            let polylineSource = ShapeSource(identifier: "polyline") {
                MLNPolylineFeature(coordinates: waypoints)
            }

            // Add a polyline casing for a stroke effect
            LineStyleLayer(identifier: "polyline-casing", source: polylineSource)
                .lineCap(.round)
                .lineJoin(.round)
                .lineColor(.white)
                .lineWidth(interpolatedBy: .zoomLevel,
                           curveType: .exponential,
                           parameters: NSExpression(forConstantValue: 1.5),
                           stops: NSExpression(forConstantValue: [14: 6, 18: 24]))

            // Add an inner (blue) polyline
            LineStyleLayer(identifier: "polyline-inner", source: polylineSource)
                .lineCap(.round)
                .lineJoin(.round)
                .lineColor(.systemBlue)
                .lineWidth(interpolatedBy: .zoomLevel,
                           curveType: .exponential,
                           parameters: NSExpression(forConstantValue: 1.5),
                           stops: NSExpression(forConstantValue: [14: 3, 18: 16]))
        }
    }
}

查看更多示例以深入了解。

注意:目前这仅适用于 iOS,因为动态框架尚未包含 macOS。

您能提供什么帮助?

您可以做的第一件事是尝试一下! 查看 示例 以获取灵感,将其切换到您自己的 SwiftUI 应用程序中,或者查看一些展示集成以获取灵感。 对其进行 "全面测试" 是我们作为社区达成 "正确" API 的最佳方式。 您的用例可能目前不受支持,在这种情况下,您可以提出问题或贡献 PR。

代码中有许多 TODO,其中大多数可以由任何中级 Swift 程序员解决。 重要的 Issues 应该都在 GitHub 中跟踪。 我们还在 OpenStreetMap US Slack 中有一个 #maplibre-swiftui-compose-playground 频道。

几个核心概念(包括样式图层和源)的框架已经到位,但这些是不完整的。 您可以通过打开 PR 来填充这些框架来提供帮助。 例如,如果您想填写线条样式图层的 API,请前往 文档 并开始填写剩余的属性和修改器。

展示集成

Ferrostar

Ferrostar 在其 Swift 软件包中包含一个 MapLibre UI 模块。 这实际上是构建此软件包的动力,并且核心开发人员正在自食其果。 请参阅 Ferrostar 用户指南的 SwiftUI 自定义 部分,了解如何自定义地图的详细信息。

MapLibre Navigation iOS

该软件包还有助于弥合 MapLibre Navigation iOS 和 SwiftUI 之间的差距! 感谢来自 HudHud 的开发人员为此做出的贡献!

Swift 软件包 添加到您的项目中。 然后添加如下代码

import MapboxCoreNavigation
import MapboxNavigation
import MapLibreSwiftUI

extension NavigationViewController: MapViewHostViewController {
    public typealias MapType = NavigationMapView
}


@State var route: Route?
@State var navigationInProgress: Bool = false

@ViewBuilder
var mapView: some View {
    MapView<NavigationViewController>(makeViewController: NavigationViewController(dayStyleURL: self.styleURL), styleURL: self.styleURL, camera: self.$mapStore.camera) {
        // TODO: Your customizations here; add more layers or whatever you like!
    }
    .unsafeMapViewControllerModifier { navigationViewController in
        navigationViewController.delegate = self.mapStore
        if let route = self.route, self.navigationInProgress == false {
            let locationManager = SimulatedLocationManager(route: route)
            navigationViewController.startNavigation(with: route, locationManager: locationManager)
            self.navigationInProgress = true
        } else if self.route == nil, self.navigationInProgress == true {
            navigationViewController.endNavigation()
            self.navigationInProgress = false
        }

        navigationViewController.mapView.showsUserLocation = self.showUserLocation && self.mapStore.streetView == .disabled
    }
    .cameraModifierDisabled(self.route != nil)
}