Are there any examples of building a layer list from a web map, particularly one with group layers? I am get the layers from map.operationalLayers to show in a List view, but am struggling with getting the sublayer for group layers to appear as children, since the . The layer list is not part of the toolkit, are there plans to have add one in the future?
Solved! Go to Solution.
Was a bit easier than I had thought. Here is a code sample:
import SwiftUI
import ArcGIS
struct LayerView: View {
var map: Map
var body: some View {
List {
ForEach(map.operationalLayers, id:\.self) { layer in
SublayerView(layer: layer)
}
}
.onAppear {
//make all group layers visible by default
map.operationalLayers.forEach { layer in
if (layer.subLayerContents.count > 0) {
layer.isVisible = true
layer.subLayerContents.forEach { sublayer in
if (sublayer.subLayerContents.count > 0) {
sublayer.isVisible = true
}
}
}
}
}
}
}
struct SublayerView: View {
var layer: Layer
@State private var expanded: Bool = false
var body: some View {
if (layer.subLayerContents.count > 0) {
DisclosureGroup(layer.name, isExpanded: $expanded) {
ForEach(layer.subLayerContents.reversed(), id:\.self.name) {
sublayer in
if (sublayer.subLayerContents.count > 0) {
SublayerView(layer: sublayer as! Layer)
} else {
Toggle(isOn: Binding<Bool>(get: {sublayer.isVisible}, set: {sublayer.isVisible = $0
})) {
Text(sublayer.name)
}
}
}
}
} else {
Toggle(isOn: Binding<Bool>(get: {layer.isVisible}, set: {layer.isVisible = $0})) {
Text(layer.name)
}
}
}
}
Was a bit easier than I had thought. Here is a code sample:
import SwiftUI
import ArcGIS
struct LayerView: View {
var map: Map
var body: some View {
List {
ForEach(map.operationalLayers, id:\.self) { layer in
SublayerView(layer: layer)
}
}
.onAppear {
//make all group layers visible by default
map.operationalLayers.forEach { layer in
if (layer.subLayerContents.count > 0) {
layer.isVisible = true
layer.subLayerContents.forEach { sublayer in
if (sublayer.subLayerContents.count > 0) {
sublayer.isVisible = true
}
}
}
}
}
}
}
struct SublayerView: View {
var layer: Layer
@State private var expanded: Bool = false
var body: some View {
if (layer.subLayerContents.count > 0) {
DisclosureGroup(layer.name, isExpanded: $expanded) {
ForEach(layer.subLayerContents.reversed(), id:\.self.name) {
sublayer in
if (sublayer.subLayerContents.count > 0) {
SublayerView(layer: sublayer as! Layer)
} else {
Toggle(isOn: Binding<Bool>(get: {sublayer.isVisible}, set: {sublayer.isVisible = $0
})) {
Text(sublayer.name)
}
}
}
}
} else {
Toggle(isOn: Binding<Bool>(get: {layer.isVisible}, set: {layer.isVisible = $0})) {
Text(layer.name)
}
}
}
}
Hello! Thank you for your question and I'm glad you found a solution. A legend or "layers list" is something that we will be implementing in the Swift Toolkit at some point, but there's no firm release date yet.
In the meantime, you solution looks good! You can also take a look at the v100.15 Toolkit's LegendViewController, and the actual code.
There are a few things in that code that may be of interest, depending on your requirements:
- Including base maps in the layers list
- Ensuring the Map and Layers are loaded prior to displaying the list
- Displaying symbol swatches for layers
- Hiding/showing scale-dependent layers depending on map scale (applicable primarily for Legends).
Nice work! If you have any more questions, please reach out!
Mark
Thank you Mark. I was able to incorporate a legend and a opacity slider. Here is some code in case anyone else is trying to do the same.
import SwiftUI
import ArcGIS
class LegendSwatch: ObservableObject, Identifiable , Hashable {
static func == (lhs: LegendSwatch, rhs: LegendSwatch) -> Bool {
return lhs.label == rhs.label && lhs.swatch == rhs.swatch
}
@Published var label: String
@Published var swatch: UIImage
init(label: String, swatch: UIImage) {
self.label = label
self.swatch = swatch
}
func hash(into hasher: inout Hasher) {
hasher.combine(label)
hasher.combine(swatch)
}
}
struct LayerInfoView: View {
@State var layer: Layer
@State private var swatches: [LegendSwatch] = []
var body: some View {
ScrollView {
VStack {
Text("Opacity")
Slider(value: $layer.opacity, in: 0...1, step: 0.1) {
}
minimumValueLabel: {
Text("0%")
} maximumValueLabel: {
Text("100%")
}
ForEach(swatches, id: \.self) { swatch in
HStack {
Image(uiImage: swatch.swatch)
Text(swatch.label)
}
}
}
}
.task {
let infos: [LegendInfo] = try! await layer.legendInfos
infos.forEach { info in
Task {
let swatch = try? await info.symbol?.makeSwatch(scale: 1.0)
self.swatches.append(LegendSwatch(label: info.name, swatch: swatch!))
}
}
}
}
}