SwipeMenuViewController

5.0.0

Swipe-based paging UI for iOS
yysskk/SwipeMenuViewController

What's New

5.0.0

2026-07-06T03:56:16Z

SwipeMenuViewController 5.0.0 is a major modernization release: it moves to Swift 6.2 / Xcode 26 with main-actor isolation, raises the minimum deployment target to iOS 16, ships exclusively through Swift Package Manager, adds a unit test suite and DocC documentation, and fixes a batch of long-standing correctness bugs.

⚠️ This release contains breaking changes — please review them before upgrading. Projects on older toolchains can stay on the 4.x line.

Breaking

  • Distribution is Swift Package Manager only. CocoaPods and Carthage support has been removed (the podspec and the framework Xcode project were deleted).
  • The minimum deployment target was raised from iOS 11 to iOS 16.
  • Requires the Swift 6.2 toolchain (Xcode 26+); the package now builds in Swift 6 language mode with main-actor default isolation.
  • The public delegate and data source protocols are now @MainActor-isolated and use AnyObject instead of class.
  • SwipeMenuViewOptions and its nested types are now Sendable.
  • Removed the deprecated SwipeMenuViewOptions.TabView.AdditionView.margin property; use padding instead.
  • ContentScrollViewDataSource now requires AnyObject and ContentScrollView.dataSource is now a weak reference (fixes a retain cycle that leaked SwipeMenuView).

Added

  • DocC documentation catalog with Getting Started and Customizing Appearance articles, plus documentation comments across the public API.
  • A unit test suite (Swift Testing) that runs on the iOS simulator in CI.

Changed

  • Sources were reorganized into the standard Swift package layout (Sources/SwipeMenuViewController).
  • The example app was rebuilt as a standalone Xcode project (Example/Example.xcodeproj) that consumes the library as a local package dependency.
  • CI now builds the Swift package and the example app and runs the tests on the latest Xcode.

Fixed

  • Fixed a crash and broken tab text-color interpolation when tab colors are defined in a non-RGB color space (for example .white/.black).
  • Fixed duplicate Auto Layout constraints being added on every layout pass in SwipeMenuViewController.
  • Fixed a potential crash in the default data source when numberOfPages(in:) returns more pages than there are child view controllers.
  • Fixed tab underline/text-color artifacts when swiping past the first or last tab.
  • Fixed SwipeMenuView.jump(to:animated:) leaving currentIndex and the delegate change callbacks out of sync with the content when jumping across more than one page, and made it ignore out-of-range indices (a negative index previously crashed).
  • Fixed ContentScrollView requesting nonexistent pages from its data source when built with an out-of-range initial index (for example reloadData(default:) past the last page).
  • Fixed the views rebuilding themselves when removed from their superview (emitting spurious delegate setup callbacks) and duplicating their tab and content subviews when a SwipeMenuView was removed and re-added to a view hierarchy.
  • Fixed the tab bar applying safe-area insets even when isSafeAreaEnabled is false, so a safe-area change (rotation, notch) no longer shifts a tab bar that opted out of safe-area layout.

Full Changelog: 4.1.0...5.0.0

SwipeMenuViewController

Platform Swift SPM compatible CI Documentation License

Overview

SwipeMenuViewController provides SwipeMenuView and SwipeMenuViewController, which make it easy to build swipe-based paging UI. A scrollable tab bar sits above a horizontally paging content area, and swiping the content keeps the tab selection in sync. The interface is modeled on UIKit's own data source and delegate patterns, so it should feel familiar.

Demo

Here are some of the styles you can build with SwipeMenuView.

Segmented Tab & Underline Flexible Tab & Underline Flexible Tab & Circle

Requirements

  • iOS 16.0+
  • Xcode 26.0+ / Swift 6.2+

Need an older toolchain or deployment target? Use the 4.x releases.

Installation

Swift Package Manager

SwipeMenuViewController is distributed exclusively through Swift Package Manager.

In Xcode, choose File ▸ Add Package Dependencies… and enter:

https://github.com/yysskk/SwipeMenuViewController.git

Or add it to a Package.swift manifest:

dependencies: [
    .package(url: "https://github.com/yysskk/SwipeMenuViewController.git", .upToNextMajor(from: "5.0.0"))
]

Usage

The quickest way to get started is to subclass SwipeMenuViewController and add your pages as child view controllers:

import SwipeMenuViewController

final class MenuViewController: SwipeMenuViewController {

    override func viewDidLoad() {
        pages.forEach { addChild($0) }
        super.viewDidLoad()
    }

    private let pages: [UIViewController] = {
        let first = UIViewController()
        first.title = "First"
        let second = UIViewController()
        second.title = "Second"
        return [first, second]
    }()
}

By default each page is backed by one of the controller's children: the page count is children.count, each tab title is the child's title, and each page shows the child's view. Override the SwipeMenuViewDataSource methods for fully custom paging.

To place the paging UI inside a view hierarchy you already have, add a SwipeMenuView directly, set its dataSource (and optional delegate), and customize it with SwipeMenuViewOptions.

SwipeMenuView is a plain UIView, so it only reads each page's view — it cannot establish view-controller containment for you. When your pages are view controllers, add them as children yourself so they receive the usual appearance and trait-collection callbacks:

import SwipeMenuViewController

final class CatalogViewController: UIViewController {

    private let swipeMenuView = SwipeMenuView(frame: .zero)

    private let pages: [UIViewController] = ["Sports", "News", "Weather"].map { title in
        let page = UIViewController()
        page.title = title
        return page
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Establish containment: the host view controller owns the pages.
        pages.forEach { addChild($0) }

        swipeMenuView.frame = view.bounds
        swipeMenuView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        swipeMenuView.dataSource = self
        view.addSubview(swipeMenuView)

        var options = SwipeMenuViewOptions()
        options.tabView.style = .segmented
        swipeMenuView.reloadData(options: options)

        // reloadData added each page's view, so finish containment.
        pages.forEach { $0.didMove(toParent: self) }
    }
}

extension CatalogViewController: SwipeMenuViewDataSource {

    func numberOfPages(in swipeMenuView: SwipeMenuView) -> Int { pages.count }

    func swipeMenuView(_ swipeMenuView: SwipeMenuView, titleForPageAt index: Int) -> String {
        pages[index].title ?? ""
    }

    func swipeMenuView(_ swipeMenuView: SwipeMenuView, viewControllerForPageAt index: Int) -> UIViewController {
        pages[index]
    }
}

The delegate and data source callbacks are main-actor isolated, so implement them from your (main-actor) view controllers as usual. See the documentation for every SwipeMenuViewOptions field.

Documentation

The full API reference and articles are published online with DocC:

Read the documentation →

Start with the Getting Started and Customizing Appearance articles for the full setup walkthrough and every available option. You can also build the documentation locally in Xcode with Product ▸ Build Documentation.

Contributing

Bug reports and pull requests are welcome. Please open an issue using one of the templates, and make sure the test suite passes (xcodebuild test -scheme SwipeMenuViewController -destination 'platform=iOS Simulator,name=iPhone 16').

Changelog

See CHANGELOG.md for the release history.

License

SwipeMenuViewController is available under the MIT license. See the LICENSE file for details.

Description

  • Swift Tools 6.2.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sun Jul 12 2026 01:05:00 GMT-0900 (Hawaii-Aleutian Daylight Time)