How to use the new inspector SwiftUI view modifier
Making a secondary column has never been simpler with iOS 17+
The latest version of SwiftUI introduced an interesting new modifier, .inspector
.
On iPad and macOS, you can now easily open an inspector with full opening/closing animation. What is an inspector? Well, think about Xcode, how you can close and open the panel on the side.
Here is how it behaves:
It’s also behaving the same on macOS, switching between a column and an overlapping inspector as you resize the window.
It’s a great addition to SwiftUI. On Ice Cubes for macOS / iPadOS, I used to draw my secondary column myself with an HStack
and a Divider
. It makes it super easy to provide a secondary column to your apps.
Here is my previous code:
@main
struct IceCubesApp: App {
var body: some Scene {
WindowGroup {
appView
}
}
@ViewBuilder
private var appView: some View {
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
sidebarView
} else {
tabBarView
}
}
private var sidebarView: some View {
SideBarView(selectedTab: $selectedTab,
popToRootTab: $popToRootTab,
tabs: availableTabs)
{
HStack(spacing: 0) {
/ ... tab content .../
if userPreferences.showiPadSecondaryColumn {
Divider().edgesIgnoringSafeArea(.all)
notificationsSecondaryColumn
}
}
}
private var notificationsSecondaryColumn: some View {
NotificationsTab(popToRootTab: $popToRootTab, lockedType: nil)
.environment(\.isSecondaryColumn, true)
}
}
Here is my new code:
@main
struct IceCubesApp: App {
var body: some Scene {
WindowGroup {
appView
}
}
@ViewBuilder
private var appView: some View {
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
sidebarView
} else {
tabBarView
}
}
private var sidebarView: some View {
SideBarView(selectedTab: $selectedTab,
popToRootTab: $popToRootTab,
tabs: availableTabs)
{
/ ... tab content .../
}
.inspector(isPresented: $userPreferences.showiPadSecondaryColumn) {
notificationsSecondaryColumn
}
}
private var notificationsSecondaryColumn: some View {
NotificationsTab(popToRootTab: $popToRootTab, lockedType: nil)
.environment(\.isSecondaryColumn, true)
.inspectorColumnWidth(.secondaryColumnWidth)
}
}
You can give width to your column using .inspectorColumnWidth
. In my case, it’s fixed, but you could even provide a more fine-tuned width using the min
, max
and ideal
width parameters.
You’re still responsible for providing the button to open it and close it yourself.
struct NotificationsTab: View {
@Environment(\.isSecondaryColumn) private var isSecondaryColumn: Bool
var body: some View {
NavigationStack {
NotificationsListView()(
.toolbar {
if UIDevice.current.userInterfaceIdiom == .pad {
if (!isSecondaryColumn && !userPreferences.showiPadSecondaryColumn) || isSecondaryColumn {
SecondaryColumnToolbarItem()
}
}
}
.toolbarBackground(theme.primaryBackgroundColor.opacity(0.50), for: .navigationBar)
}
}
}
public struct SecondaryColumnToolbarItem: ToolbarContent {
@Environment(\.isSecondaryColumn) private var isSecondaryColumn
@Environment(UserPreferences.self) private var preferences
public init() {}
public var body: some ToolbarContent {
ToolbarItem(placement: isSecondaryColumn ? .navigationBarLeading : .navigationBarTrailing) {
Button {
withAnimation {
preferences.showiPadSecondaryColumn.toggle()
}
} label: {
Image(systemName: "sidebar.right")
}
}
}
}
SF Symbols have a lot of icons to match your sidebar content and side. I’m using sidebar.right
in my case.
You can browse the full code source of Ice Cubes on Github
And download it on the App Store
And that’s it; you can now add an inspector to your SwiftUI apps! 🚀