Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2025 DesignSwiftUI & UI Frameworks

WWDC25 · 26 min · Design / SwiftUI & UI Frameworks

Build a UIKit app with the new design

Update your UIKit app to take full advantage of the new design system. We’ll dive into key changes to tab views, split views, bars, presentations, search, and controls, and show you how to use Liquid Glass in your custom UI. To get the most out of this video, we recommend first watching “Get to know the new design system” for general design guidance.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 38 snippets

Minimize tab bar on scroll swift · at 2:31 ↗
// Minimize tab bar on scroll

tabBarController.tabBarMinimizeBehavior = .onScrollDown
Add a bottom accessory swift · at 3:08 ↗
// Add a bottom accessory

let nowPlayingView = NowPlayingView()
let accessory = UITabAccessory(contentView: nowPlayingView)
tabBarController.bottomAccessory = accessory
Update the accessory with the tabAccessoryEnvironment trait swift · at 3:35 ↗
// Update the accessory with the trait

registerForTraitChanges([UITraitTabAccessoryEnvironment.self]) { (view: MiniPlayerView, _) in
    let isInline = view.traitCollection.tabAccessoryEnvironment == .inline
    view.updatePlayerAppearance(inline: isInline)
}

// Automatic trait tracking with updateProperties()
override func updateProperties() {
    super.updateProperties()
    let isInline = traitCollection.tabAccessoryEnvironment == .inline
    updatePlayerAppearance(inline: isInline)
}
Extend content under the sidebar swift · at 5:51 ↗
// Extend content underneath the sidebar

let posterImageView = UIImageView(image: ...)

let extensionView = UIBackgroundExtensionView()
extensionView.contentView = posterImageView
view.addSubview(extensionView)

let detailsView = ShowDetailsView()
view.addSubview(detailsView)
Adjust the effect layout swift · at 6:51 ↗
// Adjust the effect layout

let posterImageView = UIImageView(image: ...)

let extensionView = UIBackgroundExtensionView()
extensionView.contentView = posterImageView
extensionView.automaticallyPlacesContentView = false
view.addSubview(extensionView)

posterImageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    posterImageView.topAnchor.constraint(equalTo: extensionView.topAnchor),
    posterImageView.leadingAnchor.constraint(equalTo: extensionView.safeAreaLayoutGuide.leadingAnchor),
    posterImageView.trailingAnchor.constraint(equalTo: extensionView.safeAreaLayoutGuide.trailingAnchor),
    posterImageView.bottomAnchor.constraint(equalTo: extensionView.safeAreaLayoutGuide.bottomAnchor),
])
Custom grouping swift · at 8:38 ↗
// Custom grouping

navigationItem.rightBarButtonItems = [
    doneButton,
    flagButton,
    folderButton,
    infoButton,
    .fixedSpace(0),
    shareButton,
    selectButton
]
UIBarButtonItem tint color and style swift · at 8:53 ↗
// Tint color and style

let flagButton = UIBarButtonItem(image: UIImage(systemName: "flag.fill"))
flagButton.tintColor = .systemOrange
flagButton.style = .prominent
Toolbar with evenly distributed items in a single background swift · at 9:10 ↗
// Toolbar with evenly distributed items, grouped in a single background.

let flexibleSpace = UIBarButtonItem.flexibleSpace()
flexibleSpace.hidesSharedBackground = false

toolbarItems = [
   .init(image: UIImage(systemName: "location")),
   flexibleSpace,
   .init(image: UIImage(systemName: "number")),
   flexibleSpace,
   .init(image: UIImage(systemName: "camera")),
   flexibleSpace,
   .init(image: UIImage(systemName: "trash")),
]
Titles and subtitles swift · at 10:15 ↗
// Titles and subtitles

navigationItem.title = "Inbox"
navigationItem.subtitle = "49 Unread"
Large subtitle view swift · at 10:27 ↗
// Titles and subtitles

navigationItem.title = "Inbox"
navigationItem.largeSubtitleView = filterButton
Edge effect for a custom container swift · at 11:20 ↗
// Edge effect’s custom container

let interaction = UIScrollEdgeElementContainerInteraction()
interaction.scrollView = contentScrollView
interaction.edge = .bottom

buttonsContainerView.addInteraction(interaction)
Hard edge effect style swift · at 11:48 ↗
// Hard edge effect style

scrollView.topEdgeEffect.style = .hard
Morph popover from its source button swift · at 13:55 ↗
// Morph popover from its source button

viewController.popoverPresentationController?.sourceItem = barButtonItem
Morph sheet from bar button swift · at 14:07 ↗
// Morph sheet from bar button

viewController.preferredTransition = .zoom { _ in 
     folderBarButtonItem
}
Source item for action sheets swift · at 14:46 ↗
// Setting source item for action sheets

alertController.popoverPresentationController?.sourceItem = barButtonItem
Placing search in the toolbar swift · at 15:36 ↗
// Place search bar in a toolbar

toolbarItems = [
    navigationItem.searchBarPlacementBarButtonItem,
    .flexibleSpace(),
    addButton
]
Universally accessible search on iPad swift · at 16:01 ↗
// Place search at the trailing edge of the navigation bar

navigationItem.searchBarPlacementAllowsExternalIntegration = true
Activate the search field when search bar is tapped swift · at 16:47 ↗
// Activate the search field when search bar is tapped

searchTab.automaticallyActivatesSearch = true
Search as a dedicated view swift · at 17:03 ↗
// Search as a dedicated view

navigationItem.preferredSearchBarPlacement = .integratedCentered
Buttons swift · at 17:52 ↗
// Standard glass
button.configuration = .glass()

// Prominent glass
tintedButton.configuration = .prominentGlass()
Neutral slider with 5 ticks and a neutral value swift · at 18:16 ↗
// Neutral slider with 5 ticks and a neutral value
slider.trackConfiguration = .init(allowsTickValuesOnly: true,
                                  neutralValue: 0.2,
                                  numberOfTicks: 5)
Thumbless slider swift · at 18:59 ↗
// Thumbless slider
slider.sliderStyle = .thumbless
Glass for custom views swift · at 20:28 ↗
// Adopting glass for custom views

let effectView = UIVisualEffectView()
addSubview(effectView)

let glassEffect = UIGlassEffect()
// Animating setting the effect results in a materialize animation
UIView.animate {
    effectView.effect = glassEffect
}
Custom corner configuration swift · at 20:49 ↗
// Custom corner configuration

UIView.animate {
    effectView.cornerConfiguration = .fixed(8)
}
Dark mode swift · at 20:54 ↗
// Adapting to dark mode

UIView.animate {
    view.overrideUserInterfaceStyle = .dark
}
Adding glass to an existing glass container swift · at 21:02 ↗
// Adding glass to an existing glass container

let container = UIVisualEffectView()
container.effect = UIGlassEffect()

container.contentView.addSubview(effectView)
Container relative corners swift · at 21:08 ↗
// Container relative corners

UIView.animate {
    effectView.cornerConfiguration = .containerRelative()
    effectView.frame.origin = CGPoint(x: 10, y: 10)
}
Container relative corners, animated swift · at 21:23 ↗
// Container relative corners

UIView.animate {
    effectView.frame.origin = CGPoint(x: 30, y: 30)
}
Glass adapts based on its size swift · at 21:30 ↗
// Glass adapts based on its size

UIView.animate {
    view.overrideUserInterfaceStyle = .light
    effectView.bounds.size = CGSize(width: 250, height: 88)
}

UIView.animate {
    effectView.bounds.size = CGSize(width: 150, height: 44)
}
Adding content to glass views swift · at 21:49 ↗
// Adding content to glass views

let label = UILabel()
label.text = "WWDC25"
label.textColor = .secondaryLabel

effectView.contentView.addSubview(label)
Applying tint color to glass swift · at 22:15 ↗
// Applying tint color to glass

let glassEffect = UIGlassEffect()
glassEffect.tintColor = .systemBlue

UIView.animate {
    effectView.effect = glassEffect
    label.textColor = .label
}
Using custom colors with glass swift · at 22:33 ↗
// Using custom colors with glass

let glassEffect = UIGlassEffect()
glassEffect.tintColor = UIColor(displayP3Red: r,
                                green: g,
                                blue: b,
                                alpha: 1)

UIView.animate {
    effectView.effect = glassEffect
    // Animate out the label
    label.alpha = 0
}
Enabling interactive glass behavior swift · at 23:03 ↗
// Enabling interactive glass behavior

let glassEffect = UIGlassEffect()
glassEffect.isInteractive = true

effectView.effect = glassEffect
Animating glass out using dematerialize animation swift · at 23:20 ↗
// Animating glass out using dematerialize animation

UIView.animate {
    effectView.effect = nil
}
Adding glass elements to a container swift · at 23:52 ↗
// Adding glass elements to a container

let container = UIGlassContainerEffect()
let containerView = UIVisualEffectView(effect: container)

let glassEffect = UIGlassEffect()
let view1 = UIVisualEffectView(effect: glassEffect)
let view2 = UIVisualEffectView(effect: glassEffect)

containerEffectView.contentView.addSubview(view1)
containerEffectView.contentView.addSubview(view2)
Adjusting the container spacing swift · at 24:12 ↗
// Adjusting the container spacing

let containerEffect = UIGlassContainerEffect()
containerEffect.spacing = 20
containerEffectView.effect = containerEffect
Merging two glass views swift · at 24:27 ↗
// Merging two glass views

UIView.animate {
    view1.frame = finalFrame
    view2.frame = finalFrame
}
Dividing glass into multiple views swift · at 24:33 ↗
// Dividing glass into multiple views

UIView.performWithoutAnimation {
    for view in finalViews {
        containerEffectView.contentView.addSubview(view)
        view.frame = startFrame
    }
}

UIView.animate {
    for view in finalViews {
        view.frame = finalFrame(for: view)
    }
}

Resources