Meet Inspectors — a structural API that can help bring a new level of detail to your apps. We'll take you through the fundamentals of the API and show you how to adopt it. Learn about the latest updates to sheet presentation customizations and find out how you can combine the two to create perfect presentation experiences.
♪ ♪ Nick: Hi, I'm Nick, an engineer on the SwiftUI team. Inspector is an exciting new element in SwiftUI. I'll go over what an inspector is and how to use the API. After that, I'll review modifiers for presentation customizations. Inspector is the name for views that show further detail of selected content. You have probably interacted with one before. Keynote uses an inspector to show formatting details for a selection, in this case, the shape formatter. Here the inspector presents as a trailing sidebar. Another common use case for inspectors is showing content that supplements an app's main content. Shortcuts uses inspector for this purpose. The main interface is the shortcut that the user is editing, and the inspector supplements that with the library of available apps and actions. I will be using this sample app to explore the inspector API. I've been getting to know the animals in and around Apple Park. This app saves the animals I've met, tracks their name, tracks their favorite fruits, and there's this column that I'll get to later labeled "Suspicion Level." And now, meet, SwiftUI's inspector! The inspector shows a read-write view of details about the selected animal. Here I'm adjusting Fabrizio Fish's suspicion level up to "Extremely suspicious." Actually I'll keep him at fishy. Inspector is available to SwiftUI Developers on macOS and iPadOS and iOS too! The inspector API includes programmatic control over column width, allowing you to tune the width of the trailing column. The API includes programmatic control over presented state, allowing hiding and showing of the inspector as needed. Inspector is a higher level abstraction than just a trailing sidebar. In compact size classes, it adapts to a resizable sheet and inspector will automatically overlay in split screen on larger iPads. SwiftUI already has an existing set of structural APIs. Inspector fits in alongside these APIs with characteristics of both navigation components and presentations. Similar to NavigationSplitView and NavigationStack, inspector is used to build the scaffolding of your scene. Similar to Sheet, popover, alert, and confirmation dialogs, inspector is a presentation, dismissing and presenting as needed. It's time to learn how to adopt the new inspector API. Earlier, I showed off the sample app I made for this session. You may have noticed I was tracking each animal's suspicion level. That's because I am trying to solve a high-stakes mystery. Someone is eating all the fruit at Apple Park! I added inspector to my app so that I, Inspector Nick, can gather the details of each animal. I'll show you how easy it was to adopt the inspector API, and maybe I can solve this fruity mystery along the way. Quickly, to Xcode! The game is a-fruit! The first step to adding inspector is using the new modifier, inspector. Like some other presentations, this takes a Bool presented binding, and then the inspector's content in the trailing view builder. For the inspector content, I have a ready made AnimalInspectorForm. I have some custom methods for passing in the animal currently being inspected. And there's my inspector, presenting as a trailing column here on macOS. I have planted the seeds of a wonderful inspector experience. This AnimalForm is using the grouped style. In case you haven't encountered form styles before, they're applied like this. But because inspector contexts use the grouped style by default, I don't need to style it myself. I can even interact with the inspector in the preview's canvas. Ah, this reminds me, while inspectors can collapse by default, they aren't resizable by default. I can change that using the inspector column width modifier. I'll use some sensible defaults. 200 for min, 300 for ideal, and 400 for max. This ideal parameter will be the size of the column at at first launch, but if the user resizes the inspector, the system will persist that size across launches. Lastly, I'll add a toolbar item to toggle the presented state. I'll use a button that toggles the presented property, and for its label, a Label, using the info.circle system image. The toolbar item will appear in the section of the toolbar above the inspector because it's declared within the inspector's view builder. When I scroll, the toolbar behaves exactly like I'd expect, showing a shadow pocket when enough of the content is underneath the toolbar.
I used the inspector modifier on the AnimalTable intentionally, rather than some other spot in the view hierarchy. Like many SwiftUI APIs, the inspector modifier has different behaviors depending on the context of where it is applied. Specifically, the placement decides whether the full height style is used, where there is no separation between toolbar and content, or the under toolbar style is used, where the inspector is nested under the toolbar. Notice the title separator spans the full width of the window in the under toolbar appearance. Similarly, toolbar content will be placed in the main content's toolbar... or in the inspector's toolbar, depending on where the toolbar modifier is used. There are two points to consider when using the inspector API. First, is the inspector going to be placed inside or outside of a navigation structure like a NavigationStack or NavigationSplitView? Second, should the toolbar content be inside or outside of the inspector's view builder? I'll walk through two of these constructions. First up, placing inspector inside a navigation structure, with toolbar content outside the inspector. When an inspector is contained within a NavigationStack, the inspector is underneath the navigation stack's toolbar. Here, the toolbar content is declared outside of the inspector on the main content and is rendered in the navigation stack's toolbar. In compact horizontal size classes, the inspector presents as a sheet and the toolbar item stays in the main content's toolbar. Back to my table of possibilities. For the second construction, I'll look at placing the inspector outside of a navigation structure, with toolbar content inside the inspector view builder. When the inspector modifier is placed outside of a navigation structure, the inspector is given the full height of the trailing column to lay out. If the inspector has toolbar content, that content will be placed in a toolbar section specifically for the inspector. These two toolbar items are positioned with the principal placement and end up centered in the navigation toolbar above the inspector. This time, however, because the toolbar content is inside the inspector's view builder, when the inspector presents as a sheet, the toolbar content is in the sheet.
These principles extend to macOS, except inspector does not present as a sheet on macOS, and so the table becomes simpler. The only axis to worry about is inside or outside a navigation structure. A final, but important note: If you are using an inspector within a NavigationSplitView, the inspector should be placed in the detail column's view builder, or, just like before, it can also be placed entirely outside the navigation structure. Wow, all of this mystery solving is making me hungry! Oh! I'm getting a Nibble Bulletin in the app! The app shows Nibble Bulletins as resizable sheets with some information about where and when the fruit was nibbled. This is a great time for me to talk about presentation customizations SwiftUI released with iOS 16.4. The fruit nibble bulletin is a sheet, not an inspector. Presentation modifiers allow deep customization of sheets and other presentations like popovers. I'll try a few of them out. The presentation background modifier is aptly named. It will set the background of a presentation. Unlike the existing background modifier, the presentation specific modifier will fill the entire presentation and allow underlying content to show through. So, if I use a thinMaterial, I'll see a hint of the List behind the sheet. I'd like to allow interaction with content behind the sheet, in case I want to scroll the list, and look at suspects while reading the Nibble Bulletin. This is as simple as presentationBackgroundInteraction enabled. The dimming view is removed and I can now interact with the background content. The PresentationBackgroundInteraction type can also have an upThrough parameter accepting a presentation detent. As long as the provided argument matches one of the given presentation's detents, SwiftUI will only provide the dimming view at detents greater than the upThrough argument. I want dimming at any detent above a height of 200, so I will first add a height detent of 200 using the presentationDetents modifier. I'll adjust the sheet from its current detent of medium... down to my custom height of 200. When I only enable background interaction up through 200, the dimming view will return for the medium and large detents. There are more customizations available to you to get the perfect look and feel for presentations. Many of these modifiers have effects on other presentations too, not just sheets. And one more thing about presentation modifiers. Those same modifiers compose with Inspector when Inspector is presenting as a sheet. For my inspector from earlier, to disable background content interaction at the medium detent, I can use the same exact code from before... declaring a height detent, and only enabling background interaction up through the same detent. Now the inspector dims at the medium detent and higher. And that's inspectors in SwiftUI. In this video, I introduced the inspector API and the nuances of using it. I picked some of my favorite sheet presentation modifiers to demonstrate, and showed how they can compose with inspectors. So what are you waiting for? Go out and get inspecting. Place the inspector somewhere in your app. Keep it going by customizing your presentations. And as for the nibbler, I guess our efforts to solve this mystery were fruitless. ♪ ♪