Skip to content

Commit 992ae7b

Browse files
committed
Add source code.
1 parent 0798aff commit 992ae7b

File tree

4 files changed

+254
-5
lines changed

4 files changed

+254
-5
lines changed

Samples.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,8 @@
367367
D77D9C012BB2439400B38A6C /* AugmentRealityToShowHiddenInfrastructureView.ARSceneView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D77D9BFF2BB2438200B38A6C /* AugmentRealityToShowHiddenInfrastructureView.ARSceneView.swift */; };
368368
D78666AD2A2161F100C60110 /* FindNearestVertexView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78666AC2A2161F100C60110 /* FindNearestVertexView.swift */; };
369369
D78666AE2A21629200C60110 /* FindNearestVertexView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D78666AC2A2161F100C60110 /* FindNearestVertexView.swift */; };
370+
D78FA4942C3C88880079313E /* CreateDynamicBasemapGalleryView.Views.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78FA4932C3C88880079313E /* CreateDynamicBasemapGalleryView.Views.swift */; };
371+
D78FA4952C3C8E8A0079313E /* CreateDynamicBasemapGalleryView.Views.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D78FA4932C3C88880079313E /* CreateDynamicBasemapGalleryView.Views.swift */; };
370372
D79482D42C35D872006521CD /* CreateDynamicBasemapGalleryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79482D02C35D872006521CD /* CreateDynamicBasemapGalleryView.swift */; };
371373
D79482D72C35D8A3006521CD /* CreateDynamicBasemapGalleryView.swift in Copy Source Code Files */ = {isa = PBXBuildFile; fileRef = D79482D02C35D872006521CD /* CreateDynamicBasemapGalleryView.swift */; };
372374
D79EE76E2A4CEA5D005A52AE /* SetUpLocationDrivenGeotriggersView.Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79EE76D2A4CEA5D005A52AE /* SetUpLocationDrivenGeotriggersView.Model.swift */; };
@@ -518,6 +520,7 @@
518520
dstPath = "";
519521
dstSubfolderSpec = 7;
520522
files = (
523+
D78FA4952C3C8E8A0079313E /* CreateDynamicBasemapGalleryView.Views.swift in Copy Source Code Files */,
521524
D79482D72C35D8A3006521CD /* CreateDynamicBasemapGalleryView.swift in Copy Source Code Files */,
522525
95E980742C26189E00CB8912 /* BrowseOGCAPIFeatureServiceView.swift in Copy Source Code Files */,
523526
955AFAC62C110B8A009C8FE5 /* ApplyMosaicRuleToRastersView.swift in Copy Source Code Files */,
@@ -921,6 +924,7 @@
921924
D77BC5362B59A2D3007B49B6 /* StylePointWithDistanceCompositeSceneSymbolView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StylePointWithDistanceCompositeSceneSymbolView.swift; sourceTree = "<group>"; };
922925
D77D9BFF2BB2438200B38A6C /* AugmentRealityToShowHiddenInfrastructureView.ARSceneView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AugmentRealityToShowHiddenInfrastructureView.ARSceneView.swift; sourceTree = "<group>"; };
923926
D78666AC2A2161F100C60110 /* FindNearestVertexView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindNearestVertexView.swift; sourceTree = "<group>"; };
927+
D78FA4932C3C88880079313E /* CreateDynamicBasemapGalleryView.Views.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateDynamicBasemapGalleryView.Views.swift; sourceTree = "<group>"; };
924928
D79482D02C35D872006521CD /* CreateDynamicBasemapGalleryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateDynamicBasemapGalleryView.swift; sourceTree = "<group>"; };
925929
D79EE76D2A4CEA5D005A52AE /* SetUpLocationDrivenGeotriggersView.Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetUpLocationDrivenGeotriggersView.Model.swift; sourceTree = "<group>"; };
926930
D7A737DC2BABB9FE00B7C7FC /* AugmentRealityToShowHiddenInfrastructureView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AugmentRealityToShowHiddenInfrastructureView.swift; sourceTree = "<group>"; };
@@ -2317,6 +2321,7 @@
23172321
isa = PBXGroup;
23182322
children = (
23192323
D79482D02C35D872006521CD /* CreateDynamicBasemapGalleryView.swift */,
2324+
D78FA4932C3C88880079313E /* CreateDynamicBasemapGalleryView.Views.swift */,
23202325
);
23212326
path = "Create dynamic basemap gallery";
23222327
sourceTree = "<group>";
@@ -3059,6 +3064,7 @@
30593064
D77570C02A2942F800F490CD /* AnimateImagesWithImageOverlayView.swift in Sources */,
30603065
D7054AE92ACCCB6C007235BA /* Animate3DGraphicView.SettingsView.swift in Sources */,
30613066
D7BA8C442B2A4DAA00018633 /* Array+RawRepresentable.swift in Sources */,
3067+
D78FA4942C3C88880079313E /* CreateDynamicBasemapGalleryView.Views.swift in Sources */,
30623068
E0EA0B772866390E00C9621D /* ProjectGeometryView.swift in Sources */,
30633069
D74C8BFE2ABA5605007C76B8 /* StyleSymbolsFromMobileStyleFileView.swift in Sources */,
30643070
D7E7D0812AEB39D5003AAD02 /* FindRouteInTransportNetworkView.swift in Sources */,
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright 2024 Esri
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import ArcGIS
16+
import SwiftUI
17+
18+
extension CreateDynamicBasemapGalleryView {
19+
/// A view for selecting a basemap.
20+
struct BasemapGallery: View {
21+
/// The view model for the sample.
22+
@ObservedObject var model: Model
23+
24+
var body: some View {
25+
Form {
26+
Picker("Style", selection: $model.basemapStyle) {
27+
ForEach(model.stylesInfo, id: \.style) { styleInfo in
28+
HStack {
29+
if let image = styleInfo.thumbnail?.image {
30+
Image(uiImage: image)
31+
.resizable()
32+
.scaledToFit()
33+
.frame(height: 50)
34+
}
35+
36+
Text(styleInfo.styleName)
37+
}
38+
.tag(styleInfo.style)
39+
}
40+
}
41+
.pickerStyle(.navigationLink)
42+
43+
Picker("Language", selection: $model.basemapStyleLanguage) {
44+
Section("Strategy") {
45+
ForEach(model.languageStrategies, id: \.self) { strategy in
46+
Text(strategy.label)
47+
.tag(BasemapStyleLanguage.strategic(strategy))
48+
}
49+
}
50+
51+
Section("Specific") {
52+
ForEach(model.languages, id: \.self) { language in
53+
Text(language.label ?? "Unknown")
54+
.tag(BasemapStyleLanguage.specific(language))
55+
}
56+
}
57+
}
58+
59+
Picker("Worldview", selection: $model.worldviewCode) {
60+
ForEach(model.worldviews, id: \.?.code) { worldview in
61+
Text(worldview?.displayName ?? "")
62+
.tag(worldview?.code)
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}
69+
70+
extension BasemapStyleLanguage: Hashable {
71+
public func hash(into hasher: inout Hasher) {
72+
switch self {
73+
case .strategic(let strategy):
74+
hasher.combine(strategy)
75+
case .specific(let language):
76+
hasher.combine(language)
77+
@unknown default:
78+
fatalError("Unknown basemap style language.")
79+
}
80+
}
81+
}
82+
83+
private extension BasemapStyleLanguage {
84+
/// A human-readable label for the basemap style language.
85+
var label: String? {
86+
switch self {
87+
case .strategic(let strategy):
88+
strategy.label
89+
case .specific(let language):
90+
language.label
91+
@unknown default:
92+
fatalError("Unknown basemap style language.")
93+
}
94+
}
95+
}
96+
97+
private extension BasemapStyleLanguageStrategy {
98+
/// A human-readable label for the basemap style language strategy.
99+
var label: String {
100+
switch self {
101+
case .default: "Default"
102+
case .global: "Global"
103+
case .local: "Local"
104+
case .applicationLocale: "System Locale"
105+
@unknown default:
106+
fatalError("Unknown basemap style language strategy.")
107+
}
108+
}
109+
}
110+
111+
private extension Locale.Language {
112+
/// A human-readable label for the language.
113+
var label: String? {
114+
Locale.current.localizedString(forIdentifier: self.maximalIdentifier)
115+
}
116+
}

Shared/Samples/Create dynamic basemap gallery/CreateDynamicBasemapGalleryView.swift

Lines changed: 130 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,140 @@ import ArcGIS
1616
import SwiftUI
1717

1818
struct CreateDynamicBasemapGalleryView: View {
19-
/// A map with a topographic basemap.
20-
@State private var map = Map(basemapStyle: .arcGISTopographic)
19+
/// The view model for the sample.
20+
@StateObject private var model = Model()
21+
22+
/// A Boolean value indicating whether the basemap gallery is showing.
23+
@State private var isShowingBasemapGallery = false
24+
25+
/// The error shown in the error alert.
26+
@State private var error: Error?
2127

2228
var body: some View {
23-
MapView(map: map)
29+
MapView(map: model.map)
30+
.toolbar {
31+
ToolbarItemGroup(placement: .bottomBar) {
32+
Button("Basemap") {
33+
isShowingBasemapGallery.toggle()
34+
}
35+
.popover(isPresented: $isShowingBasemapGallery) {
36+
NavigationStack {
37+
BasemapGallery(model: model)
38+
.navigationTitle("Basemap")
39+
.navigationBarTitleDisplayMode(.inline)
40+
.toolbar {
41+
ToolbarItem(placement: .confirmationAction) {
42+
Button("Done") { isShowingBasemapGallery = false }
43+
}
44+
}
45+
}
46+
.presentationDetents([.fraction(0.5)])
47+
.frame(idealWidth: 320, idealHeight: 380)
48+
}
49+
}
50+
}
51+
.task {
52+
do {
53+
try await model.loadStylesInfo()
54+
} catch {
55+
self.error = error
56+
}
57+
}
58+
.errorAlert(presentingError: $error)
59+
}
60+
}
61+
62+
extension CreateDynamicBasemapGalleryView {
63+
/// The view model for the sample.
64+
@MainActor
65+
final class Model: ObservableObject {
66+
/// The map shown in the map view.
67+
let map = Map()
68+
69+
/// The basemap style of the map's basemap.
70+
@Published var basemapStyle = Basemap.Style.arcGISNavigation {
71+
didSet {
72+
guard basemapStyle != oldValue else { return }
73+
74+
basemapStyleLanguage = .strategic(.default)
75+
worldviewCode = nil
76+
basemapStyleInfo = stylesInfo.first { $0.style == basemapStyle }
77+
78+
updateBasemap()
79+
}
80+
}
81+
82+
/// The basemap style language of the map's basemap.
83+
@Published var basemapStyleLanguage = BasemapStyleLanguage.strategic(.default) {
84+
didSet {
85+
guard basemapStyleLanguage != oldValue else { return }
86+
updateBasemap()
87+
}
88+
}
89+
90+
/// The worldview code of the map's basemap.
91+
@Published var worldviewCode: String? {
92+
didSet {
93+
guard worldviewCode != oldValue else { return }
94+
updateBasemap()
95+
}
96+
}
97+
98+
/// The basemap styles info from the basemap styles service.
99+
@Published private(set) var stylesInfo: [BasemapStyleInfo] = []
100+
101+
/// The basemap style info for the basemap style.
102+
@Published private var basemapStyleInfo: BasemapStyleInfo?
103+
104+
/// The basemap style language strategy options for the basemap style info.
105+
var languageStrategies: [BasemapStyleLanguageStrategy] {
106+
return [.default] + (basemapStyleInfo?.languageStrategies ?? [])
107+
}
108+
109+
/// The language options for the basemap style info.
110+
var languages: [Locale.Language] {
111+
basemapStyleInfo?.languages ?? []
112+
}
113+
114+
/// The worldview options for the basemap style info.
115+
var worldviews: [Worldview?] {
116+
return [nil] + (basemapStyleInfo?.worldviews ?? [])
117+
}
118+
119+
init() {
120+
map.basemap = Basemap(style: basemapStyle)
121+
map.initialViewpoint = Viewpoint(latitude: 52.3433, longitude: -1.5796, scale: 25e5)
122+
}
123+
124+
/// Loads the styles info from the basemap styles service.
125+
func loadStylesInfo() async throws {
126+
let service = BasemapStylesService()
127+
try await service.load()
128+
129+
guard let info = service.info else { return }
130+
stylesInfo = info.stylesInfo
131+
basemapStyleInfo = stylesInfo.first { $0.style == basemapStyle }
132+
133+
// Loads the styles info thumbnails.
134+
await stylesInfo.compactMap(\.thumbnail).load()
135+
}
136+
137+
/// Updates the map's basemap with the `basemapStyle`, `basemapStyleLanguage` and `worldviewCode`.
138+
private func updateBasemap() {
139+
let basemapStyleParameters = BasemapStyleParameters(language: basemapStyleLanguage)
140+
basemapStyleParameters.worldview = if let worldviewCode {
141+
Worldview(code: worldviewCode)
142+
} else {
143+
nil
144+
}
145+
146+
map.basemap = Basemap(style: basemapStyle, parameters: basemapStyleParameters)
147+
}
24148
}
25149
}
26150

27151
#Preview {
28-
CreateDynamicBasemapGalleryView()
152+
NavigationStack {
153+
CreateDynamicBasemapGalleryView()
154+
}
29155
}

Shared/Samples/Create dynamic basemap gallery/README.metadata.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"Worldview"
2626
],
2727
"snippets": [
28-
"CreateDynamicBasemapGalleryView.swift"
28+
"CreateDynamicBasemapGalleryView.swift",
29+
"CreateDynamicBasemapGalleryView.Views.swift"
2930
],
3031
"title": "Create dynamic basemap gallery"
3132
}

0 commit comments

Comments
 (0)