This month’s code-alongs are dedicated to introduce you to the new liquid glass design of iOS 26. We will create a TabView including 3 different views and a search view. We will also create an easy MiniPlayer which sits above the TabView and is a simple recreation of the Music Player behaviour in Apple Music:

Let’s get started!
Step 1: Download Xcode 26
For this project you’ll need Xcode 26 beta 4. You can download it here:
Xcode 26 beta 4
Please install it before proceeding.
Step 1: Set up your project
- Open Xcode: Launch Xcode and select Create a new Xcode project.
- Choose Template: Select App under the iOS tab and click Next.
- Name Your Project: Enter a name for your project, like
DesignExploration
.- interface: SwiftUI
- language: Swift
Click Next, and then save your project.
When you open your project, you’ll see the already familiar standard code presenting a globe and the Text “Hello, world!” in the ContentView.swift
.
Step 2: Creating the different default views
Please create the following SwiftUI views by clicking command + N
:
HomeView
ListView
ProfileView
SearchView
MiniPlayerView
Step 3: Creating the TabView
In ContentView
we create our TabView as usual:
@State private var selectedTab: String = "home"
var body: some View {
TabView(selection: $selectedTab) {
Tab("Home", systemImage: "house", value: "home") {
HomeView()
}
Tab("Profile", systemImage: "person", value: "profile") {
ProfileView()
}
Tab("List", systemImage: "list.bullet", value: "list") {
ListView()
}
Tab(value: "search", role: .search) {
SearchView()
}
}
}
You’ll see that the usual TabView
already creates the new liquid glass behaviour. Just move from one to the next tab, and you’ll see the wonderful liquid glass effect.
You’ll also see that by adding role: .search
to the last tab, automatically the SearchView
is singled out in a separate container and is represented by the magnifier symbol.
Step 4: SearchView
We want to create a list with the entries “Song Title” and adding the numbers from 1 to 50. At the bottom we want to include a search bar and the list is then filtered accordingly.
Therefore, we need to define a variable that holds the searchString
and also one that holds the filtered items (filteredItems
):
struct SearchView: View {
@State private var searchString = ""
private var filteredItems: [Int] {
let allItems = Array(1...50)
if searchString.isEmpty {
return allItems
} else {
return allItems.filter {
"Song Title \($0)".localizedCaseInsensitiveContains(searchString)
}
}
}
var body: some View {
NavigationStack {
List(filteredItems, id: \.**self**) { idx in
Text("Song Title \(idx)")
}
.navigationTitle("Search")
.searchable(text: $searchString)
}
}
}
By simply adding .searchable(text: $searchString)
, a search bar appears at the bottom and we make the list searchable. You can try it by typing e.g. 1
and all “Song Title” with a 1
are being filtered. That’s all!
Step 5: Adding liquid glass elements to HomeView
Let’s add to HomeView
some liquid glass elements. We define some symbols in symbolSet
we want to show. We set a background image (which has the simple name “1016” and comes from one of Apple’s examples) and present our systemName images as usual – for the moment please just take the namespace
as it is:
struct HomeView: View {
@Namespace private var namespace
let symbolSet: [String] = ["cloud.bolt.rain.fill", "sun.rain.fill", "moon.stars.fill", "moon.fill"]
var body: some View {
ZStack {
Image("1016")
.resizable()
.scaledToFill()
.edgesIgnoringSafeArea(.all)
VStack(spacing: 40.0) {
HStack(spacing: 40.0) {
Image(systemName: "scribble.variable")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
.offset(x: 20.0, y: 0.0)
Image(systemName: "eraser.fill")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
.offset(x: -20.0, y: 0.0)
}
}
}
}
}
By simply adding the modifier .glassEffect()
we create the glass effect around our images. That’s all!
Let’s wrap the HStack
into a
GlassEffectContainer(spacing: 40.0) {
}
The appearance now changes from two separate circular items to an appearance with a slightly overlapping effect since they are now in the same GlassEffectContainer
. You can play with the spacing
and the .offset
To show this effect again, you can add as well:
GlassEffectContainer(spacing: 20.0) {
HStack(spacing: 20.0) {
ForEach(symbolSet.indices, id: \.**self**) { item **in**
Image(systemName: symbolSet[item])
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
.glassEffectUnion(id: item < 2 ? "1" : "2", namespace: namespace)
}
}
}
.glassEffectUnion(id: item < 2 ? "1" : "2", namespace: namespace)
groups glass effects together. Items with the same id will share one glass background. Here, the first two items (item < 2
) get “1”, the others get “2”. namespace
is a @Namespace
variable that ensures SwiftUI knows which items belong to the same group.
Play around!
Step 6: Create a cool toggle effect
We will now create a cool transition effect that showcases the liquid glass effect: Pressing a toggle button to expand two images and revert back. Let’s start by putting two images (for scribble and eraser) on top of each other in the same GlassEffectContainer
:
struct ProfileView: View {
@State private var isExpanded: Bool = false
@Namespace private var namespace
var body: some View {
ZStack {
Image("1016")
.resizable()
.scaledToFill()
.edgesIgnoringSafeArea(.all)
VStack {
GlassEffectContainer(spacing: 80.0) {
HStack(spacing: 80.0) {
Image(systemName: "scribble.variable")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
.glassEffectID("scribble", in: namespace)
if isExpanded {
Image(systemName: "eraser.fill")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
.glassEffectID("eraser", in: namespace)
}
}
}
.padding(.bottom, 40)
}
}
}
}
We wrapped the eraser image in an if
statement because the eraser should only be visible when a button has been toggled. Therefore let’s define the button:
Below the GlassEffectContainer
(after the .padding(.bottom, 40)
), insert the following:
Button("Toggle") {
withAnimation(.easeInOut(duration: 0.25)) {
isExpanded.toggle()
}
}
.buttonStyle(.glass)
Press the toggle button and watch the effect! Doesn’t it look beautiful? You can also use other animations (e.g. .bouncy
instead of .easeInOut
).
Step 7: A toolbar effect
For the last – pretty cool – effect, we will use our ListView
and our MiniPlayerView
. Let’s start with the ListView
:
ListView
is just a simple list of 50 items, nothing special:
struct ListView: View {
var body: some View {
NavigationStack {
VStack {
List(1...50, id: \.**self**) { idx **in**
Text("Item \(idx)")
}
.listStyle(.plain)
}
.navigationTitle("Simple List")
}
}
}
Now let’s create a MiniPlayerView
:
struct MiniPlayerView: View {
var body: some View {
HStack(spacing: 15) {
Image(systemName: "music.note.list")
.font(.title2)
.foregroundStyle(.secondary)
.frame(width: 48, height: 48)
.background(Color(.systemGray5))
.clipShape(RoundedRectangle(cornerRadius: 8))
VStack(alignment: .leading) {
Text("Song Title")
.font(.headline)
.fontWeight(.semibold)
Text("Artist Name")
.font(.subheadline)
.foregroundStyle(.secondary)
}
Spacer()
HStack(spacing: 20) {
Button(action: { }) {
Image(systemName: "play.fill")
.font(.title2)
.foregroundStyle(.primary)
}
Button(action: { }) {
Image(systemName: "forward.fill")
.font(.title2)
.foregroundStyle(.primary)
}
}
}
}
}
For simplicity, the buttons do not include an action but of course you can add an action of your choice.
So far, nothing special. You can go to ContentView
and click on the list tab and you’ll see our list.
Now the cool part comes:
In ContentView
add the following modifiers to TabView
:
.searchToolbarBehavior(.minimize)
.tabBarMinimizeBehavior(.onScrollDown)
.tabViewBottomAccessory {
MiniPlayerView()
.padding()
}
What do they do?
.searchToolbarBehavior(.minimize)
: This modifier controls how the search toolbar behaves when scrolling..minimize
means that when the user scrolls down, the search bar collapses into a small icon in the navigation bar. It saves space and makes the UI feel cleaner. Without this modifier, the search bar would stay expanded, taking up more space..tabBarMinimizeBehavior(.onScrollDown)
: This controls how the tab bar behaves while scrolling..onScrollDown
means that when the user scrolls down, the tab bar hides automatically, giving more space to the content. When the user scrolls back up, the tab bar reappears. This is great for apps with lists or feeds where users scroll through a lot of content..tabViewBottomAccessory
: This lets you add an extra view on top of the tab bar, like a floating accessory panel. Here, it adds aMiniPlayerView()
—think of a small music player, similar to Apple Music. And you’ll see that thisMiniPlayerView
moves to the bottom when you are in ourListView
tab and you scroll down. Nice.
Congratulations!
You’ve successfully created views with new design features of iOS 26! 🎉
What you have learned
In this code-along, you’ve learned how to:
- Create a tab view with multiple tabs, including a dedicated search tab using
role: .search
and featuring the new liquid glass design. - Build a searchable list with real-time filtering using
.searchable(text:)
. - Use the new liquid glass design in iOS 26 with
.glassEffect()
,GlassEffectContainer
, and.glassEffectUnion()
to group elements together. - Apply matched glass effects with
.glassEffectID()
to create smooth transitions when toggling elements. - Enhance navigation with the new iOS 26 modifiers:
.searchToolbarBehavior(.minimize)
to collapse the search bar on scroll..tabBarMinimizeBehavior(.onScrollDown)
to hide the tab bar while scrolling..tabViewBottomAccessory
to attach a custom view above the tab bar.
That’s a wrap!
Keep learning, keep building, and let your curiosity guide you. Happy coding! ✨
You are never too old to set another goal or to dream a new dream. — Les Brown
Download the full project on GitHub: https://github.com/swiftandcurious/DesignExploration