Display a composite layer of all the subtype values in a feature class.

Use case
This is useful for controlling labeling, visibility, and symbology of a given subtype as though they are distinct layers on the map.
How to use the sample
The sample loads with the sublayer visible on the map. Toggle its visibility by tapping the first switch. Toggle between the sublayer’s original renderer and an alternate renderer using the second switch. Tap the “Set Current to Minimum Scale” button to set the sublayer’s minimum scale to the current map scale.
How it works
- Create a
SubtypeFeatureLayerfrom aServiceFeatureTablethat defines a subtype, and add it to theMap. - Get a
SubtypeSublayerfrom the subtype feature layer using its name. - Enable the sublayer’s labels and define them with
LabelDefinition.- Use
SimpleLabelExpressionto set the expression for label definitions.
- Use
- Make a switch to toggle the sublayer’s visibility.
- Create an alternate renderer by making a
SimpleRenderer. - Get the current map scale and make it the minimum map scale.
Relevant API
- LabelDefinition
- ServiceFeatureTable
- SimpleLabelExpression
- SubtypeFeatureLayer
- SubtypeSublayer
About the data
The feature service layer in this sample represents an electric network in Naperville, Illinois, which contains a utility network with asset classification for different devices.
Additional information
Help regarding the Arcade label expression script for defining a label definition can be found on the Esri Developers website.
Tags
asset group, feature layer, labeling, sublayer, subtype, symbology, utility network, visible scale range
Sample code
// Copyright 2023 Esri//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
import ArcGISimport SwiftUI
struct SetVisibilityOfSubtypeSublayerView: View { /// The view model for the sample. @StateObject private var model = Model()
/// A Boolean value indicating whether the settings should be presented. @State private var isShowingSettings = false
/// The error shown in the error alert. @State private var error: (any Error)?
var body: some View { MapView(map: model.map) .onScaleChanged { scale in model.currentScale = scale model.formatCurrentScaleText() } .overlay(alignment: .top) { Text("Current scale: \(model.currentScaleText)") .frame(maxWidth: .infinity, alignment: .center) .padding(8) .background(.ultraThinMaterial, ignoresSafeAreaEdges: .horizontal) .multilineTextAlignment(.center) } .toolbar { ToolbarItem(placement: .bottomBar) { Button("Visibility Settings") { isShowingSettings.toggle() } .popover(isPresented: $isShowingSettings) { NavigationStack { SettingsView(model: model) } .presentationDetents([.fraction(0.5)]) .frame(idealWidth: 320, idealHeight: 340) } } } .task { do { try await model.setup() } catch { // Presents an error message if the subtype layer fails to load. self.error = error } } .errorAlert(presentingError: $error) .onTeardown { model.tearDown() } }}
#Preview { NavigationStack { SetVisibilityOfSubtypeSublayerView() }}// Copyright 2023 Esri//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
import ArcGISimport Foundation
extension SetVisibilityOfSubtypeSublayerView { /// The view model for this sample. @MainActor class Model: ObservableObject { /// A map with a streets basemap style. let map = Map(basemapStyle: .arcGISStreetsNight)
/// A feature table for the electrical network in this sample. private let featureTable = ServiceFeatureTable(url: .featureServiceURL)
/// A subtype feature layer created from the service feature table. private let subtypeFeatureLayer: SubtypeFeatureLayer
/// The subtype sublayer of the subtype feature layer in this sample. private var subtypeSublayer: SubtypeSublayer?
/// The renderer of the subtype feature layer. private var originalRenderer: Renderer?
/// The subtype sublayer's label definition. private let labelDefinition: LabelDefinition = { // Make and stylize the text symbol. let textSymbol = TextSymbol(color: .blue, size: 10.5) textSymbol.backgroundColor = .clear textSymbol.outlineColor = .white textSymbol.haloColor = .white textSymbol.haloWidth = 2 // Make a label definition and adjust its properties. let labelExpression = SimpleLabelExpression(simpleExpression: "[nominalvoltage]") let labelDefinition = LabelDefinition(labelExpression: labelExpression, textSymbol: textSymbol) labelDefinition.placement = .pointAboveRight labelDefinition.usesCodedValues = true return labelDefinition }()
/// The current scale of the map. var currentScale: Double = .zero
/// The map's current scale value in text. @Published var currentScaleText: String = ""
/// The subtype sublayer's minimum scale value in text. @Published private(set) var minimumScaleText: String = "Not Set"
init() { map.initialViewpoint = .initialViewpoint subtypeFeatureLayer = SubtypeFeatureLayer(featureTable: featureTable) subtypeFeatureLayer.scalesSymbols = false }
/// Performs important tasks including adding credentials, loading and adding operational layers. func setup() async throws { try await ArcGISEnvironment.authenticationManager.arcGISCredentialStore.add(.publicSample) try await subtypeFeatureLayer.load() map.addOperationalLayer(subtypeFeatureLayer) guard let subtypeSublayer = subtypeFeatureLayer.sublayer(withSubtypeName: "Street Light") else { throw SetupError.cannotFindSublayer } subtypeSublayer.labelsAreEnabled = true originalRenderer = subtypeSublayer.renderer subtypeSublayer.addLabelDefinition(labelDefinition) self.subtypeSublayer = subtypeSublayer }
/// Cleans up the model's setup. func tearDown() { ArcGISEnvironment.authenticationManager.arcGISCredentialStore.removeAll() }
func toggleSublayer(isVisible: Bool) { subtypeSublayer?.isVisible = isVisible }
func toggleRenderer(showsOriginalRenderer: Bool) { if showsOriginalRenderer { subtypeSublayer?.renderer = originalRenderer } else { let symbol = SimpleMarkerSymbol(style: .diamond, color: .systemPink, size: 20) let alternativeRenderer = SimpleRenderer(symbol: symbol) subtypeSublayer?.renderer = alternativeRenderer } }
func formatCurrentScaleText() { currentScaleText = "1:\(currentScale.formatted(.decimal))" }
func setMinimumScale() { minimumScaleText = currentScaleText subtypeSublayer?.minScale = currentScale } }}
private extension SetVisibilityOfSubtypeSublayerView.Model { enum SetupError: LocalizedError { case cannotFindSublayer
var errorDescription: String? { switch self { case .cannotFindSublayer: return NSLocalizedString( "Cannot find subtype sublayer.", comment: "Error thrown when subtype sublayer cannot be found." ) } } }}
private extension ArcGISCredential { /// The public credentials for the data in this sample. /// - Note: Never hardcode login information in a production application. This is done solely /// for the sake of the sample. static var publicSample: ArcGISCredential { get async throws { try await TokenCredential.credential( for: URL.featureServiceURL, username: "viewer01", password: "I68VGU^nMurF" ) } }}
private extension FormatStyle where Self == FloatingPointFormatStyle<Double> { /// Formats the double with zero decimals places of precision. static var decimal: Self { Self.number.precision(.fractionLength(0)) }}
private extension URL { static var featureServiceURL: URL { URL(string: "https://sampleserver7.arcgisonline.com/server/rest/services/UtilityNetwork/NapervilleElectric/FeatureServer/0")! }}
private extension Viewpoint { /// The initial viewpoint to be displayed when the sample is first opened. static var initialViewpoint: Viewpoint { .init( boundingGeometry: Envelope( xRange: (-9812691.11079696)...(-9812377.9447607), yRange: (5128687.20710657)...(5128865.36767282), spatialReference: .webMercator ) ) }}// Copyright 2023 Esri//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
import SwiftUI
extension SetVisibilityOfSubtypeSublayerView { struct SettingsView: View { /// The view model for the sample. @ObservedObject var model: Model
/// The action to dismiss the view. @Environment(\.dismiss) private var dismiss
/// A Boolean value indicating whether to show the subtype sublayer. @State private var showsSublayer = true
/// A Boolean value indicating whether to show the subtype sublayer's renderer. @State private var showsOriginalRenderer = true
var body: some View { Form { Section("Layers") { Toggle("Show Sublayer", isOn: $showsSublayer) .onChange(of: showsSublayer) { model.toggleSublayer(isVisible: showsSublayer) } Toggle("Show Original Renderer", isOn: $showsOriginalRenderer) .onChange(of: showsOriginalRenderer) { model.toggleRenderer(showsOriginalRenderer: showsOriginalRenderer) } } Section("Sublayer Minimum Scale") { LabeledContent("Minimum Scale", value: model.minimumScaleText) HStack { Button("Set Current to Minimum Scale") { model.setMinimumScale() } .frame(maxWidth: .infinity, alignment: .center) } } } .navigationTitle("Visibility Settings") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .confirmationAction) { Button("Done") { dismiss() } } } } }}