← All release notes
SwiftUIiOS 18Aug 23, 2026 · 5 min read

What's New in SwiftUI — iOS 18

iOS 18 gives SwiftUI two macros that remove real boilerplate, a rebuilt TabView, zoom transitions, and MeshGradient.

iOS 18's SwiftUI release is heavier on developer-experience wins than iOS 26's. Two macros quietly delete real boilerplate. The rest is view-level: a rebuilt TabView, zoom transitions, scroll-phase observation, mesh gradients. All of it fills gaps teams had been working around with custom code for years.

#@Entry: environment, focused, and container values without the boilerplate

One custom EnvironmentValues key used to cost three pieces: a private EnvironmentKey struct, a default value, and a computed property extension. @Entry is one line.

extension EnvironmentValues {
    @Entry var hapticsEnabled: Bool = true
}

The same macro covers FocusedValues and Transferable/ContainerValues entries. Use it anywhere you'd hand-write an EnvironmentKey. Nothing is left that justifies the old three-part version.

#@Previewable: inline state in #Preview

A preview that needed local @State used to need a throwaway container view built for nothing but holding it. @Previewable declares that state right inside the #Preview closure.

#Preview {
    @Previewable @State var isOn = false
    Toggle("Enabled", isOn: $isOn)
}

Toggles, text fields, any preview that exercises interactive state. The preview-only wrapper structs stop cluttering your view files.

#Zoom navigation transitions

Pair .navigationTransition(.zoom) on the destination with .matchedTransitionSource on the source. The destination then grows out of the exact frame of the element that triggered navigation.

@Namespace private var namespace

NavigationLink {
    PhotoDetail(photo: photo)
        .navigationTransition(.zoom(sourceID: photo.id, in: namespace))
} label: {
    Thumbnail(photo: photo)
}
.matchedTransitionSource(id: photo.id, in: namespace)

Make this the default for grid-to-detail navigation. Photo grids and product catalogs. Anywhere the old push transition felt disconnected from what the user tapped.

#Tab and TabSection: a rebuilt TabView

TabView content stops being a plain view builder and becomes explicit Tab values. .sidebarAdaptable turns that same tab set into a sidebar on iPad. One layout to maintain, not two.

TabView {
    Tab("Home", systemImage: "house") {
        HomeView()
    }
    Tab("Search", systemImage: "magnifyingglass") {
        SearchView()
    }
}
.tabViewStyle(.sidebarAdaptable)

If you're carrying a NavigationSplitView on iPad and a TabView on iPhone as two separate code paths, migrate straight to this.

#Scroll phase and geometry observation

.onScrollPhaseChange and .onScrollGeometryChange report scroll state directly. No ScrollViewReader-driven offset tracker to own.

ScrollView {
    content
}
.onScrollPhaseChange { oldPhase, newPhase in
    if newPhase == .decelerating {
        prefetchNextPage()
    }
}

Any time you need "user is actively scrolling" or "user reached this threshold", these replace hand-rolled GeometryReader offset math. Pagination triggers get simpler. So do sticky header state and scroll-linked chrome.

#MeshGradient

MeshGradient spreads color across a grid of control points, each point carrying its own color. LinearGradient and friends stop at two or three stops.

MeshGradient(
    width: 3,
    height: 3,
    points: [
        [0, 0], [0.5, 0], [1, 0],
        [0, 0.5], [0.5, 0.5], [1, 0.5],
        [0, 1], [0.5, 1], [1, 1]
    ],
    colors: [
        .red, .purple, .indigo,
        .orange, .white, .blue,
        .yellow, .green, .mint
    ]
)

That kills the custom Canvas- and shader-based gradient hacks teams built for marketing screens and onboarding backgrounds.

#What to adopt first

  1. Replace every hand-written EnvironmentKey with @Entry. Pure refactor. It works at any deployment target your toolchain supports, and carries zero runtime cost difference from the code it replaces.
  2. Take @Previewable now. It touches preview canvases only, never your shipped deployment target.
  3. If you keep two navigation shells today, move the iPad split-view code onto TabView with .sidebarAdaptable.
  4. Add zoom transitions to your highest-traffic grid-to-detail flow first. Cheap, and users see it.

@Entry and @Previewable are compile-time macros whose generated code doesn't require iOS 18 at runtime, so they're safe even with a lower deployment target. Check that the underlying EnvironmentValues/preview infrastructure you're touching doesn't itself require a newer OS. Zoom transitions, the new Tab API, scroll-phase observation, and MeshGradient are genuine iOS 18 runtime APIs. Those need your deployment target at 18 or an if #available(iOS 18, *) fallback path.