Swift and SwiftUI tutorials for Swift Developers

How to Disable the Swipe Gesture in a TabView in SwiftUI

Introduction

SwiftUI has revolutionized application development for the Apple ecosystem thanks to its declarative approach, clean syntax, and deep integration with Xcode. One of its most widely used components is TabView, a fundamental view for building tab-based interfaces, commonly found in iOS apps such as social networks, media players, banking apps, and educational platforms.

By default, when using TabView with the .page style—or even with the classic tab bar style—SwiftUI enables a swipe gesture that allows users to switch tabs by dragging left or right. While this is convenient in many cases, there are situations where this behavior is not desirable.

In this tutorial, you will learn step by step how to disable the swipe gesture in a TabView using SwiftUI and Xcode, exploring multiple approaches, from simple solutions to more advanced techniques that rely on UIKit when necessary.


Why Would You Want to Disable Swipe in a TabView?

Before diving into the code, let’s look at some real-world scenarios where disabling swipe is a good idea:

  1. Guided flows
    If your app presents a step-by-step process (onboarding, tutorials, registration), you don’t want users skipping steps by swiping freely.
  2. Gesture conflicts
    If one of your tabs contains a ScrollView, a map, or a horizontal carousel, swipe gestures may conflict with tab navigation.
  3. Navigation control
    You may want to allow tab switching only through buttons, validation, or custom logic.
  4. Educational or exam apps
    In quizzes or exams, allowing free navigation between tabs could break the app’s logic.

How TabView Works Internally

In SwiftUI, TabView is an abstraction that relies on UIKit under the hood:

  • The classic tab style uses UITabBarController
  • The .page style uses UIPageViewController or a UIScrollView

This is important because the swipe gesture is handled by these UIKit components, not directly by SwiftUI.

Currently, SwiftUI does not provide a native modifier like:

TabView {
    ...
}.disableSwipe(true) // ❌ This does not exist

So we need alternative approaches.

Base Example: TabView with Swipe Enabled

Let’s start with a simple TabView example:

struct ContentView: View {
    var body: some View {
        TabView {
            Text("Screen 1")
                .font(.largeTitle)
                .tabItem {
                    Label("One", systemImage: "1.circle")
                }

            Text("Screen 2")
                .font(.largeTitle)
                .tabItem {
                    Label("Two", systemImage: "2.circle")
                }

            Text("Screen 3")
                .font(.largeTitle)
                .tabItem {
                    Label("Three", systemImage: "3.circle")
                }
        }
    }
}

By default, users can switch tabs either by tapping the tab bar or by swiping horizontally.

Our goal is to prevent swiping.


Solution 1: Using Controlled Selection

A simple way to prevent unwanted tab changes is to control the selected tab using a @State variable:

struct ContentView: View {
    @State private var selectedTab = 0

    var body: some View {
        TabView(selection: $selectedTab) {
            Text("Screen 1").tag(0)
            Text("Screen 2").tag(1)
            Text("Screen 3").tag(2)
        }
    }
}

This alone does not disable swipe, but it lets you detect when a tab change occurs. You can revert it:

.onChange(of: selectedTab) { _ in
    selectedTab = 0
}

This creates the illusion that swipe does not work, but there is still a brief animation. Not ideal, but useful in simple cases.


Solution 2: Using .page and Disabling the Scroll

When TabView uses .tabViewStyle(.page), it internally uses a UIScrollView:

TabView {
    Color.red
    Color.green
    Color.blue
}
.tabViewStyle(.page)

To truly disable swipe, we must disable the underlying scroll view.

This requires bridging SwiftUI with UIKit using UIViewRepresentable.


Creating a Helper to Disable Scrolling

We create a hidden view that searches for the parent UIScrollView and disables it:

import SwiftUI

struct DisableSwipeTabView: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIView()

        DispatchQueue.main.async {
            if let scrollView = view.findScrollView() {
                scrollView.isScrollEnabled = false
            }
        }

        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {}
}

And a helper extension:

extension UIView {
    func findScrollView() -> UIScrollView? {
        if let scroll = self.superview as? UIScrollView {
            return scroll
        }
        for view in self.subviews {
            if let found = view.findScrollView() {
                return found
            }
        }
        return nil
    }
}

Applying It to the TabView

Now we attach it to the TabView:

struct ContentView: View {
    var body: some View {
        TabView {
            Text("Page 1")
            Text("Page 2")
            Text("Page 3")
        }
        .tabViewStyle(.page)
        .overlay(DisableSwipeTabView())
    }
}

Result:

  • Pages still exist
  • Swipe gesture is completely disabled
  • Navigation can be controlled manually

Adding Button-Based Navigation

Now let’s add manual navigation:

struct ContentView: View {
    @State private var index = 0

    var body: some View {
        VStack {
            TabView(selection: $index) {
                Text("Step 1").tag(0)
                Text("Step 2").tag(1)
                Text("Step 3").tag(2)
            }
            .tabViewStyle(.page)
            .overlay(DisableSwipeTabView())

            HStack {
                Button("Previous") {
                    if index > 0 { index -= 1 }
                }

                Button("Next") {
                    if index < 2 { index += 1 }
                }
            }
        }
    }
}

Now you have a paged UI without swipe.


Advantages of This Approach

  • Full control over navigation
  • No unwanted swipe animations
  • Works well for guided flows
  • Perfect for onboarding, forms, and tutorials

Potential Issues

  1. Internal SwiftUI changes
    Apple may change how TabView is implemented internally.
  2. UIKit dependency
    Some SwiftUI solutions still require UIKit.
  3. Testing
    Always test on multiple devices and iOS versions.

Real-World Use Cases

  • App onboarding
  • Interactive tutorials
  • Course apps
  • Multi-step forms
  • Games with level-based navigation

Conclusion

Disabling the swipe gesture in a TabView in SwiftUI is not officially supported, but by understanding how SwiftUI works under the hood, we can take full control of navigation.

By disabling the internal UIScrollView, we can create precise, professional, and user-friendly interfaces that behave exactly as we want—ideal for guided experiences and complex workflows.

If you have any questions about this article, please contact me and I will be happy to help you 🙂. You can contact me on my X profile or on my Instagram profile.

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

Extensions in Swift - Tutorial with Examples

Next Article

Guide to SwiftUI Components and Libraries

Related Posts