Skip to content

Commit cb292eb

Browse files
authored
Merge pull request #478 from Esri/Caleb/Fix-ManageOperationalLayersDisappearingLayers
[Fix] Refactor `Manage operational layers`
2 parents c7e1f57 + 08f604d commit cb292eb

File tree

4 files changed

+75
-81
lines changed

4 files changed

+75
-81
lines changed

Shared/Samples/Manage operational layers/ManageOperationalLayersView.swift

Lines changed: 70 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import ArcGIS
1616
import SwiftUI
1717

1818
struct ManageOperationalLayersView: View {
19-
/// A map with a topographic basemap and centered on western USA.
20-
@State private var map = {
19+
/// A map with a topographic basemap centered on western USA.
20+
@State private var map: Map = {
2121
let map = Map(basemapStyle: .arcGISTopographic)
2222
map.initialViewpoint = Viewpoint(
2323
center: Point(x: -133e5, y: 45e5, spatialReference: .webMercator),
@@ -26,128 +26,122 @@ struct ManageOperationalLayersView: View {
2626
return map
2727
}()
2828

29-
/// A Boolean value indicating whether to show the manage layers sheet.
30-
@State private var isShowingSheet = false
29+
/// The operational layers for the sample.
30+
@State private var operationalLayers: [Layer] = [
31+
ArcGISMapImageLayer(url: .worldElevations),
32+
ArcGISMapImageLayer(url: .censusTiles)
33+
]
3134

32-
/// The error shown in the error alert.
33-
@State private var error: Error?
35+
/// A Boolean value indicating whether the layers manager is presented.
36+
@State private var layersManagerIsPresented = false
3437

3538
var body: some View {
3639
MapView(map: map)
3740
.task {
38-
do {
39-
// Add layers from urls.
40-
let elevationImageLayer = ArcGISMapImageLayer(url: .worldElevations)
41-
try await elevationImageLayer.load()
42-
43-
let censusTiledLayer = ArcGISMapImageLayer(url: .censusTiles)
44-
try await censusTiledLayer.load()
45-
46-
map.addOperationalLayers([elevationImageLayer, censusTiledLayer])
47-
} catch {
48-
self.error = error
49-
}
41+
// Loads and adds the operational layers to the map.
42+
await operationalLayers.load()
43+
map.addOperationalLayers(operationalLayers)
5044
}
5145
.toolbar {
5246
ToolbarItem(placement: .bottomBar) {
5347
Button("Manage Layers") {
54-
isShowingSheet = true
48+
layersManagerIsPresented = true
5549
}
56-
.popover(isPresented: $isShowingSheet) {
57-
ManageLayersSheetView(map: map)
58-
.presentationDetents([.fraction(0.5)])
59-
.frame(idealWidth: 320, idealHeight: 360)
50+
.popover(isPresented: $layersManagerIsPresented) {
51+
LayersManager(map: map, layers: operationalLayers)
52+
.presentationDetents([.fraction(0.4)])
53+
.frame(idealWidth: 320, idealHeight: 260)
6054
}
6155
}
6256
}
63-
.errorAlert(presentingError: $error)
6457
}
6558
}
6659

67-
struct ManageLayersSheetView: View {
68-
/// The map with the operational layers.
60+
/// A view for managing the layers of a given map.
61+
private struct LayersManager: View {
62+
/// The map to manage.
6963
let map: Map
7064

71-
/// The action to dismiss the manage layers sheet.
65+
/// The layers to add to and remove from the map.
66+
let layers: [Layer]
67+
68+
/// The action to dismiss the view.
7269
@Environment(\.dismiss) private var dismiss
7370

74-
/// An array for all the layers currently on the map.
71+
/// The layers currently added to the map.
7572
@State private var operationalLayers: [Layer] = []
7673

77-
/// An array for all the layers removed from the map.
74+
/// The layers removed from the map.
7875
@State private var removedLayers: [Layer] = []
7976

8077
var body: some View {
81-
VStack {
82-
ZStack {
83-
Text("Manage Layers")
84-
.bold()
85-
HStack {
86-
EditButton()
87-
Spacer()
88-
Button("Done") {
89-
dismiss()
90-
}
91-
}
92-
}
93-
.padding([.top, .leading, .trailing])
94-
95-
List {
78+
NavigationStack {
79+
Form {
9680
Section {
9781
ForEach(operationalLayers, id: \.id) { layer in
9882
HStack {
99-
Image(systemName: "minus.circle.fill")
100-
.foregroundStyle(.red)
101-
.imageScale(.large)
102-
.clipped()
103-
.onTapGesture {
104-
// Remove layer from map on minus press.
105-
map.removeOperationalLayer(layer)
106-
removedLayers.append(layer)
83+
Button("Remove Layer", systemImage: "minus.circle.fill") {
84+
map.removeOperationalLayer(layer)
85+
86+
withAnimation {
10787
operationalLayers.removeAll(where: { $0.id == layer.id })
88+
removedLayers.append(layer)
10889
}
90+
}
91+
.foregroundStyle(.red)
92+
10993
Text(layer.name)
11094
}
11195
}
112-
.onMove { fromOffsets, toOffset in
113-
// Reorder the map's operational layers on list row move.
114-
operationalLayers.move(fromOffsets: fromOffsets, toOffset: toOffset)
115-
map.removeAllOperationalLayers()
116-
map.addOperationalLayers(operationalLayers)
117-
}
11896
} header: {
119-
Text("Operational Layers")
120-
#if targetEnvironment(macCatalyst)
121-
.padding(.top)
122-
#endif
123-
} footer: {
124-
Text("Tap \"Edit\" to reorder the layers.")
97+
HStack {
98+
Text("Operational Layers")
99+
Spacer()
100+
Button("Swap Layers", systemImage: "arrow.up.arrow.down.circle") {
101+
let layer = operationalLayers.first!
102+
map.removeOperationalLayer(layer)
103+
map.addOperationalLayer(layer)
104+
105+
withAnimation {
106+
operationalLayers.reverse()
107+
}
108+
}
109+
.disabled(operationalLayers.count < 2)
110+
}
125111
}
126112

127-
Section {
113+
Section("Removed Layers") {
128114
ForEach(removedLayers, id: \.id) { layer in
129115
HStack {
130-
Image(systemName: "plus.circle.fill")
131-
.foregroundStyle(.green)
132-
.imageScale(.large)
133-
.clipped()
134-
.onTapGesture {
135-
// Add layer to map on plus press.
136-
map.addOperationalLayer(layer)
116+
Button("Add Layer", systemImage: "plus.circle.fill") {
117+
map.addOperationalLayer(layer)
118+
119+
withAnimation {
120+
removedLayers.removeAll { $0.id == layer.id }
137121
operationalLayers.append(layer)
138-
removedLayers.removeAll(where: { $0.id == layer.id })
139122
}
123+
}
124+
.foregroundStyle(.green)
125+
140126
Text(layer.name)
141127
}
142128
}
143-
} header: {
144-
Text("Removed Layers")
129+
}
130+
}
131+
.labelStyle(.iconOnly)
132+
.navigationTitle("Manage Layers")
133+
.navigationBarTitleDisplayMode(.inline)
134+
.toolbar {
135+
ToolbarItem(placement: .confirmationAction) {
136+
Button("Done", action: dismiss.callAsFunction)
145137
}
146138
}
147139
}
148-
.background(Color(.systemGroupedBackground))
149140
.onAppear {
150141
operationalLayers = map.operationalLayers
142+
removedLayers = layers.filter { layer in
143+
!operationalLayers.contains { $0.id == layer.id }
144+
}
151145
}
152146
}
153147
}

Shared/Samples/Manage operational layers/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@ Add, remove, and reorder operational layers in a map.
99

1010
Operational layers display the primary content of the map and usually provide dynamic content for the user to interact with (as opposed to basemap layers that provide context).
1111

12-
The order of operational layers in a map determines the visual hierarchy of layers in the view. You can bring attention to a specific layer by rendering above other layers.
12+
The order of operational layers in a map determines the visual hierarchy of layers in the view. You can bring attention to a specific layer by rendering it above other layers.
1313

1414
## How to use the sample
1515

16-
Tap the "Manage Layers" button to display the operational layers that are currently on the map. In the first section, tap the "-" button to remove a layer, or tap "Edit" to drag and reorder the layers. The map will be updated automatically.
16+
Tap the "Manage Layers" button to display the operational layers that are currently on the map. In the first section, tap the minus button to remove a layer or tap the swap button to change which layer is rendered on top. The map will update automatically.
1717

18-
The second section shows layers that have been removed from the map. Tap the "+" button to add a layer back to the map.
18+
The second section shows layers that have been removed from the map. Tap the plus button to add a layer back to the map.
1919

2020
## How it works
2121

2222
1. Get the operational layers from the map's `operationalLayers` property.
23-
2. Add a layer using `map.addOperationalLayer(newOperationalLayer:)` or remove a layer using `map.removeOperationalLayer(operationalLayer:)`. The last layer in the array will be rendered on top.
23+
2. Add a layer using `Map.addOperationalLayer(_:)` or remove a layer using `Map.removeOperationalLayer(_:)`. The last layer in the array will be rendered on top.
2424

2525
## Relevant API
2626

@@ -29,7 +29,7 @@ The second section shows layers that have been removed from the map. Tap the "+"
2929

3030
## Additional information
3131

32-
You cannot add the same layer to the map multiple times or add the same layer to multiple maps. Instead, clone the layer with `layer.clone()` to create a new instance.
32+
You cannot add the same layer to the map multiple times or add the same layer to multiple maps. Instead, clone the layer with `Layer.clone()` to create a new instance.
3333

3434
## Tags
3535

23.4 KB
Loading
18 KB
Loading

0 commit comments

Comments
 (0)