Creating Menus, Pickers, Toggles, and Selection Controls in iOS Development
Course: iOS App Development
Topic: Creating Menus, Pickers, Toggles, and Selection Controls
Course Link: iOS Training Course
Demo Link: Register for a Free Course Demo
Website: JustAcademy
1. Introduction to Menus, Pickers, Toggles, and Selection Controls
In iOS application development, menus, pickers, toggles, and selection controls help users interact with an application efficiently. They allow users to select options, change settings, choose dates, filter information, and perform actions without entering unnecessary text.
SwiftUI provides built-in components such as Menu, Picker, Toggle, and selection-based controls. UIKit also offers corresponding components and patterns for applications developed with UIKit.
These controls improve usability by providing familiar interactions, consistent layouts, and clear feedback when a user changes a setting or selects an option.
Learning Objectives
- Understand the purpose of menus and contextual actions in iOS applications.
- Create menus using SwiftUI's Menu component.
- Use Picker to allow users to select values from available options.
- Implement Toggle controls for Boolean settings.
- Understand selection controls, including segmented controls and radio-button-style interfaces.
- Manage control values using SwiftUI state properties.
- Apply accessibility and usability principles when designing interactive interfaces.
2. Understanding Menus in iOS
A menu is a user interface component that displays a list of available actions or commands. Instead of placing every action directly on the screen, developers can group related actions inside a menu.
Menus are useful for secondary actions, editing commands, sorting options, sharing features, and contextual operations.
2.1 Common Types of Menus
| Menu Type |
Description |
Example |
| Action Menu |
Displays actions that a user can perform. |
Edit, Delete, Share |
| Contextual Menu |
Provides actions associated with a particular item or content. |
Copy or rename a document |
| Overflow Menu |
Groups additional actions when screen space is limited. |
More options on a profile screen |
| Nested Menu |
Organizes related actions into submenus. |
Text formatting options |
2.2 Creating a Menu Using SwiftUI
SwiftUI provides the Menu view for displaying a collection of actions. A menu typically contains buttons representing the available actions.
import SwiftUI
struct MenuExampleView: View {
var body: some View {
Menu("More Options") {
Button("Edit Profile") {
print("Edit Profile selected")
}
Button("Share Profile") {
print("Share Profile selected")
}
Button("Delete Profile", role: .destructive) {
print("Delete Profile selected")
}
}
.padding()
}
}
Explanation
Menu("More Options") creates a menu with a visible label.
- Each
Button represents an action available to the user.
- The action closure executes when the corresponding option is selected.
role: .destructive indicates an action that may delete or remove content.
.padding() adds space around the menu.
2.3 Using a Menu with an Image
Menus can use an icon to communicate their purpose. SF Symbols provide system icons that integrate with the iOS interface.
import SwiftUI
struct IconMenuView: View {
var body: some View {
Menu {
Button("Copy", systemImage: "doc.on.doc") {
print("Copy selected")
}
Button("Share", systemImage: "square.and.arrow.up") {
print("Share selected")
}
Button("Delete", systemImage: "trash",
role: .destructive) {
print("Delete selected")
}
} label: {
Image(systemName: "ellipsis.circle")
.font(.title)
}
.padding()
}
}
This example uses an ellipsis icon to indicate that additional actions are available. The menu options use familiar system symbols to improve recognition.
2.4 Menu Best Practices
- Use clear and concise action names.
- Group related actions together.
- Use destructive roles for potentially destructive operations.
- Keep frequently used actions easy to discover.
- Ask for confirmation before performing important irreversible operations when appropriate.
- Avoid placing too many unrelated actions in a single menu.
3. Understanding Pickers in iOS
A picker allows users to choose one value from a collection of options. Depending on its style, a picker can appear as a list, wheel, compact menu, or segmented selection interface.
Pickers are commonly used for choosing a country, language, notification preference, date, time, category, or other predefined value.
3.1 Creating a Basic Picker
import SwiftUI
struct BasicPickerView: View {
@State private var selectedColor = "Blue"
let colors = ["Red", "Green", "Blue", "Yellow"]
var body: some View {
VStack(spacing: 20) {
Text("Choose a Color")
.font(.headline)
Picker("Color", selection: $selectedColor) {
ForEach(colors, id: \.self) { color in
Text(color).tag(color)
}
}
Text("Selected Color: \(selectedColor)")
}
.padding()
}
}
Explanation
@State stores the currently selected color in the view.
colors contains the available options.
Picker displays the options and allows the user to select one.
$selectedColor creates a binding that allows the picker to update the state.
ForEach creates a view for each option.
.tag(color) associates each option with its selection value.
3.2 Picker Styles
| Picker Style |
Purpose |
Suitable Use Case |
.menu |
Displays options through a compact menu. |
Country or category selection |
.wheel |
Displays options in a scrolling wheel. |
Time or numeric choices |
.segmented |
Displays a small set of options as segments. |
Day, week, month filters |
.inline |
Displays options directly within the surrounding interface when supported. |
Form settings |
.navigationLink |
Provides a navigation-based selection interface where supported. |
Longer selection lists |
Picker styles are platform- and context-dependent. A particular style may not be appropriate or available in every layout.
3.3 Creating a Menu-Style Picker
import SwiftUI
struct CountryPickerView: View {
@State private var country = "India"
var body: some View {
Form {
Picker("Select Country", selection: $country) {
Text("India").tag("India")
Text("United States").tag("United States")
Text("United Kingdom").tag("United Kingdom")
Text("Australia").tag("Australia")
}
.pickerStyle(.menu)
Text("Selected Country: \(country)")
}
}
}
The menu-style picker is useful when the available options do not need to remain visible at all times. It keeps forms compact while still making selection straightforward.
3.4 Creating a Segmented Picker
import SwiftUI
struct PeriodPickerView: View {
@State private var selectedPeriod = "Week"
let periods = ["Day", "Week", "Month"]
var body: some View {
VStack(spacing: 16) {
Picker("Period", selection: $selectedPeriod) {
ForEach(periods, id: \.self) { period in
Text(period).tag(period)
}
}
.pickerStyle(.segmented)
Text("Showing data for: \(selectedPeriod)")
}
.padding()
}
}
A segmented picker works best when there are only a few short options. It is useful for switching between related views or filtering displayed information.
4. Understanding Toggle Controls
A toggle is a control that switches a setting between two states, usually on and off. It is ideal for Boolean preferences such as dark mode, notifications, location sharing, or automatic synchronization.
In SwiftUI, the Toggle view uses a binding to read and update a Boolean value.
4.1 Creating a Basic Toggle
import SwiftUI
struct BasicToggleView: View {
@State private var notificationsEnabled = true
var body: some View {
VStack(spacing: 20) {
Toggle(
"Enable Notifications",
isOn: $notificationsEnabled
)
Text(
notificationsEnabled
? "Notifications are enabled"
: "Notifications are disabled"
)
}
.padding()
}
}
Explanation
@State private var notificationsEnabled = true defines the initial toggle state.
Toggle displays a control for switching between enabled and disabled.
isOn: $notificationsEnabled connects the toggle to the stored Boolean value.
- The conditional expression displays different messages based on the current state.
4.2 Using Multiple Toggles
import SwiftUI
struct SettingsView: View {
@State private var notifications = true
@State private var darkMode = false
@State private var autoSync = true
var body: some View {
Form {
Section("App Preferences") {
Toggle("Notifications", isOn: $notifications)
Toggle("Dark Mode", isOn: $darkMode)
Toggle("Automatic Sync", isOn: $autoSync)
}
Section("Current Settings") {
Text("Notifications: \(notifications ? "On" : "Off")")
Text("Dark Mode: \(darkMode ? "On" : "Off")")
Text("Automatic Sync: \(autoSync ? "On" : "Off")")
}
}
}
}
This example demonstrates how multiple toggles can manage separate application settings. Each toggle has its own state variable, so changing one does not automatically change the others.
4.3 Performing an Action When a Toggle Changes
Sometimes an application must perform additional work when a toggle changes. The onChange modifier can respond to state changes.
import SwiftUI
struct SyncSettingsView: View {
@State private var autoSync = false
var body: some View {
Toggle("Automatic Sync", isOn: $autoSync)
.onChange(of: autoSync) { _, newValue in
if newValue {
print("Automatic sync enabled")
} else {
print("Automatic sync disabled")
}
}
.padding()
}
}
The two-parameter onChange closure shown here is available in modern SwiftUI versions. Older deployment targets may require the earlier closure form. Real synchronization functionality should be implemented separately from the user interface.
4.4 Toggle Design Considerations
- Use toggles for settings with two states.
- Write labels that clearly explain what the control enables or disables.
- Ensure the initial value accurately reflects the current setting.
- Persist preferences when they need to remain after the application restarts.
- Request system permissions separately when a feature requires them.
- Do not use a toggle to represent a choice that requires selecting one option from several alternatives.
5. Understanding Selection Controls
Selection controls allow users to choose one or more options from a group. The appropriate component depends on the number of options, the available screen space, and whether multiple selections are allowed.
5.1 Single Selection
Single-selection interfaces allow the user to select only one option at a time. A standard picker or segmented picker is often suitable.
import SwiftUI
struct LanguageSelectionView: View {
@State private var language = "English"
let languages = ["English", "Hindi", "Marathi"]
var body: some View {
Form {
Picker("Preferred Language", selection: $language) {
ForEach(languages, id: \.self) { item in
Text(item).tag(item)
}
}
Text("Selected Language: \(language)")
}
}
}
5.2 Multiple Selection with Checkboxes
SwiftUI does not provide a dedicated cross-platform checkbox view for every iOS interface. When users must choose several independent options, developers can create checkbox-style rows using a button and an appropriate symbol.
import SwiftUI
struct InterestSelectionView: View {
@State private var selectedInterests: Set<String> = []
let interests = [
"Design",
"Programming",
"Photography",
"Travel"
]
var body: some View {
Form {
Section("Select Your Interests") {
ForEach(interests, id: \.self) { interest in
Button {
if selectedInterests.contains(interest) {
selectedInterests.remove(interest)
} else {
selectedInterests.insert(interest)
}
} label: {
HStack {
Text(interest)
Spacer()
Image(
systemName: selectedInterests.contains(interest)
? "checkmark.square.fill"
: "square"
)
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
}
}
Text("Selected: \(selectedInterests.sorted().joined(separator: ", "))")
}
}
}
Explanation
Set<String> stores unique selected values.
contains() checks whether an option has already been selected.
insert() adds a selected option.
remove() removes an option from the selection.
- The displayed symbol changes according to the selection state.
For production applications, make custom checkbox-style controls accessible by providing meaningful accessibility labels and selected-state information. Prefer familiar iOS interaction patterns where possible.
5.3 Creating a Custom Radio-Button-Style Selection
Radio buttons are commonly associated with selecting exactly one option from a group. In iOS, developers can represent this interaction with a picker, a segmented control, or a custom single-selection list.
import SwiftUI
struct DeliverySelectionView: View {
@State private var delivery = "Standard"
let methods = ["Standard", "Express", "Same Day"]
var body: some View {
Form {
Section("Delivery Method") {
ForEach(methods, id: \.self) { method in
Button {
delivery = method
} label: {
HStack {
Text(method)
Spacer()
Image(
systemName: delivery == method
? "largecircle.fill.circle"
: "circle"
)
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
}
}
Text("Selected Delivery: \(delivery)")
}
}
}
Because all rows share the same state variable, selecting one delivery method automatically makes it the only selected option.
6. Managing State and Data Binding
State management is essential when creating interactive controls. A control must display the current value and update the application when the user makes a selection.
6.1 Understanding @State
@State is a SwiftUI property wrapper used for storing values owned by a view. When a state value changes, SwiftUI can update the affected interface.
@State private var isEnabled = false
@State private var selectedOption = "Option A"
@State private var selectedItems: Set<String> = []
6.2 Understanding Binding
A binding provides read-and-write access to a value owned elsewhere. The dollar sign creates a binding from a suitable property-wrapper value, such as $isEnabled or $selectedOption.
Toggle("Enable Feature", isOn: $isEnabled)
Picker("Choose Option", selection: $selectedOption) {
Text("Option A").tag("Option A")
Text("Option B").tag("Option B")
}
Using bindings keeps the control and its state synchronized without requiring developers to manually refresh the interface.
6.3 When to Use Other State Management Tools
| Tool |
Purpose |
Typical Use |
@State |
Stores local view state. |
A selected picker option |
@Binding |
Allows a child view to read and modify a value owned by a parent. |
A reusable settings row |
@Observable |
Supports observable reference types in modern SwiftUI applications. |
Shared application settings model |
@StateObject |
Owns an observable object in older ObservableObject-based designs. |
A view model with managed lifecycle |
@AppStorage |
Connects simple values to UserDefaults. |
Persisting a preferred language |
The appropriate choice depends on the application's architecture and deployment target. Do not use local state as a substitute for secure storage of sensitive information.
7. Combining Menus, Pickers, and Toggles in a Settings Screen
A real application often combines multiple controls on a single screen. For example, a settings page might provide a language picker, a notification toggle, and a menu containing additional account actions.
import SwiftUI
struct AppSettingsView: View {
@State private var language = "English"
@State private var notificationsEnabled = true
let languages = ["English", "Hindi", "Marathi"]
var body: some View {
NavigationStack {
Form {
Section("Preferences") {
Picker("Language", selection: $language) {
ForEach(languages, id: \.self) { item in
Text(item).tag(item)
}
}
Toggle(
"Enable Notifications",
isOn: $notificationsEnabled
)
}
Section("Account Actions") {
Menu("More Options") {
Button("View Profile") {
print("View profile")
}
Button("Help") {
print("Open help")
}
}
}
Section("Summary") {
Text("Language: \(language)")
Text(
"Notifications: "
+ (notificationsEnabled ? "On" : "Off")
)
}
}
.navigationTitle("Settings")
}
}
}
How the Example Works
- The navigation container provides a standard settings-screen structure.
- The preferences section contains a picker and a toggle.
- The account actions section groups secondary commands inside a menu.
- The summary section reflects the current values of the controls.
- SwiftUI updates the interface when the bound state changes.
8. Using Controls in UIKit
Although SwiftUI offers declarative controls, many iOS applications use UIKit. UIKit provides components such as UISwitch, UISegmentedControl, and UIMenu. A picker-style interface can be implemented using a UIPickerView or another suitable selection interface.
8.1 Creating a UISwitch
import UIKit
class SettingsViewController: UIViewController {
private let notificationSwitch = UISwitch()
override func viewDidLoad() {
super.viewDidLoad()
notificationSwitch.isOn = true
notificationSwitch.addTarget(
self,
action: #selector(notificationChanged(_:)),
for: .valueChanged
)
view.addSubview(notificationSwitch)
}
@objc private func notificationChanged(_ sender: UISwitch) {
if sender.isOn {
print("Notifications enabled")
} else {
print("Notifications disabled")
}
}
}
This example demonstrates the switch's state and its value-change event. In a complete UIKit interface, the switch should also be positioned using Auto Layout constraints.
8.2 Creating a UISegmentedControl
import UIKit
class FilterViewController: UIViewController {
private let filterControl = UISegmentedControl(
items: ["All", "Recent", "Favorites"]
)
override func viewDidLoad() {
super.viewDidLoad()
filterControl.selectedSegmentIndex = 0
filterControl.addTarget(
self,
action: #selector(filterChanged(_:)),
for: .valueChanged
)
view.addSubview(filterControl)
}
@objc private func filterChanged(_ sender: UISegmentedControl) {
let selectedIndex = sender.selectedSegmentIndex
print("Selected filter index: \(selectedIndex)")
}
}
The selected segment index identifies the active option. As with other UIKit views, production layouts should use suitable constraints and provide meaningful accessibility information.
9. Accessibility and User Experience Best Practices
Controls should be understandable and usable by people with different abilities, input methods, and screen configurations.
- Clear labels: Use meaningful names such as "Enable Notifications" instead of vague labels such as "Option".
- VoiceOver support: Ensure that controls expose understandable labels, values, and states.
- Dynamic Type: Support larger text sizes and avoid layouts that break when text is enlarged.
- Visual consistency: Use standard iOS components whenever they meet the requirements.
- Selection feedback: Make the currently selected value easy to identify.
- Touch targets: Provide sufficiently large and well-spaced interactive areas.
- Destructive actions: Clearly communicate the consequences of deleting or removing content.
- Color independence: Do not rely on color alone to communicate selection or status.
- Persistence: Store settings when they should survive an application restart.
- Platform adaptation: Test the appearance and behavior on different iPhone and iPad configurations.
10. Common Errors and Troubleshooting
| Problem |
Possible Cause |
Solution |
| Picker selection does not update. |
Incorrect binding or missing tags for the selection values. |
Check the binding and ensure that each option has a matching tag. |
| Toggle changes are not reflected. |
The control is not connected to the correct state. |
Use a valid Boolean binding such as $isEnabled. |
| Menu actions do nothing. |
The action closure has no implementation. |
Connect the action to the required operation. |
| Wrong option appears selected. |
The initial selection does not match any available option. |
Set the initial value to a valid option. |
| Multiple options remain selected in a single-selection list. |
Each row stores independent selection state. |
Use one shared selected-value variable. |
| Checkbox-style control is inaccessible. |
Custom visuals do not communicate selection state clearly. |
Add suitable accessibility labels and selected-state information. |
| Settings reset after restarting. |
The value is stored only in temporary view state. |
Persist appropriate preferences using UserDefaults, AppStorage, or another suitable storage mechanism. |
| Interface does not fit on smaller screens. |
Too many visible controls or inflexible layouts. |
Group related settings in a Form or use compact selection interfaces. |
11. Practical Exercises
Exercise 1: Profile Options Menu
- Create a profile screen with an options menu.
- Add Edit Profile, Share Profile, and Sign Out actions.
- Use suitable icons and clear action labels.
Exercise 2: Country and Language Picker
- Create a form with country and language pickers.
- Provide at least four country options and three language options.
- Display the selected values below the controls.
Exercise 3: Notification Settings
- Create toggles for email notifications, push notifications, and promotional updates.
- Display a summary of enabled settings.
- Persist the preferences across application launches.
Exercise 4: Multiple Interest Selection
- Display at least five interests using checkbox-style rows.
- Allow users to select and deselect several interests.
- Show the final list of selected interests.
- Make the selection state accessible.
Exercise 5: Mini Settings Application
Build a settings screen that combines a language picker, a theme preference, a notification toggle, a segmented filter, and an account actions menu. Use reusable components where appropriate and test the layout on multiple screen sizes.
12. Interview Questions and Answers
Q1. What is a Menu in SwiftUI?
A Menu is a view that presents a collection of actions or commands when the user opens it.
Q2. What is the purpose of a Picker?
A Picker allows users to select a value from a collection of available options.
Q3. What is a Toggle?
A Toggle is a control that switches a Boolean value between enabled and disabled states.
Q4. Why is @State used with interactive controls?
@State stores view-owned data. When the value changes, SwiftUI updates the relevant parts of the interface.
Q5. What is the difference between @State and @Binding?
@State stores a value owned by a view, while @Binding provides access to a value owned elsewhere so that another view can read and update it.
Q6. When should a segmented picker be used?
A segmented picker is appropriate when users need to switch between a small number of related options, such as All, Recent, and Favorites.
Q7. How can a user select multiple values?
A developer can maintain a collection such as Set<String> and provide a selection interface that adds or removes individual values.
Q8. How is a toggle different from a picker?
A toggle represents a two-state setting, whereas a picker allows the user to choose among multiple available values.
Q9. What is the purpose of a tag in a Picker?
A tag associates an option with the value used by the picker's selection binding.
Q10. Which UIKit controls are similar to SwiftUI selection controls?
UISwitch is used for Boolean settings, UISegmentedControl supports segmented choices, UIMenu provides menus, and UIPickerView provides a picker-style selection interface.
Q11. How can an application save a user's settings?
Appropriate preferences can be stored using @AppStorage, UserDefaults, or a suitable persistent data solution.
Q12. Why is accessibility important for selection controls?
Accessibility helps users understand and operate controls through VoiceOver, larger text, assistive technologies, and alternative interaction methods.
13. Quick Revision Checklist
- Understand the purpose of menus and action buttons.
- Create a SwiftUI menu with multiple actions.
- Build a picker with valid selection values and tags.
- Apply menu, segmented, and other suitable picker styles.
- Use toggles for Boolean preferences.
- Manage values with
@State and @Binding.
- Implement single-selection and multiple-selection interfaces.
- Understand equivalent UIKit components.
- Test controls with VoiceOver and different screen sizes.
- Persist settings when required by the application.
14. Conclusion
Menus, pickers, toggles, and selection controls are essential building blocks for interactive iOS applications. Menus organize actions, pickers provide predefined choices, toggles manage Boolean settings, and selection interfaces allow users to choose one or several values.
By combining SwiftUI components with appropriate state management, developers can create responsive, maintainable, and accessible interfaces. Understanding equivalent UIKit controls also helps developers work effectively with existing iOS applications and mixed SwiftUI/UIKit projects.
For further learning, explore the JustAcademy iOS Training Course, visit the JustAcademy website, or register for a course demo.