Implementing the Side Menu for the iOS Mail App with SwiftUI

Published on

Get weekly handpicked updates on Swift and SwiftUI!

With the continuous improvement of SwiftUI 2.0, I feel it’s time to make a major upgrade to my app. I’ve always wanted to implement an elegant side menu effect in my app similar to the iOS mail app, and I searched online for a solution. However, most of the implementations are for UIKit and there are not many project libraries that are well adapted to SwiftUI. I ended up implementing one myself in Xcode 12.

SwipeCell is a SwiftUI library developed with Swift 5.3. Its goal is to achieve the left and right swipe menu functionality similar to the iOS Mail app. SwipeCell requires XCode 12 and iOS 14.

SwipeCell GitHub

Configuring Buttons

let button1 = SwipeCellButton(buttonStyle: .titleAndImage,
                title: "Mark", 
                systemImage: "bookmark",
                titleColor: .white, 
                imageColor: .white, 
                view: nil,   
                backgroundColor: .green,
                action: {bookmark.toggle()},
// You can set buttons to any view to achieve more complex designs and dynamic effects
let button3 = SwipeCellButton(buttonStyle: .view, title:"",systemImage: "", view: {
            if unread {
                Image(systemName: "envelope.badge")
            else {
                Image(systemName: "")
}, backgroundColor: .orange, action: {unread.toggle()}, feedback: false)

Configuring Slots

let slot1 = SwipeCellSlot(slots: [button2,button1])
let slot2 = SwipeCellSlot(slots: [button4], slotStyle: .destructive, buttonWidth: 60) 
let slot3 = SwipeCellSlot(slots: [button2,button1],slotStyle: .destructiveDelay)


    .swipeCell(cellPosition: .left, leftSlot: slot4, rightSlot: nil)

More configuration options

    .swipeCell(cellPosition: .both, 
                leftSlot: slot1, 
                rightSlot: slot1 ,
                swipeCellStyle: SwipeCellStyle(
                            alignment: .leading,
                            dismissWidth: 20,
                            appearWidth: 20,
                            destructiveWidth: 240, 
                            vibrationForButton: .error, 
                            vibrationForDestructive: .heavy, 
                            autoResetTime: 3)

Automatically dismiss when scrolling

For List


For single cell in ScrollView

        Text("Mail Title")
        Text("Mail Content")
    .frame(maxWidth:.infinity,maxHeight: .infinity)
.swipeCell(cellPosition: .both, leftSlot: leftSlot, rightSlot: rightSlot,clip: false)

For LazyVStack in ScrollView

    ForEach(lists,id:\.self){ item in
       Text("Swipe in scrollView:\(item)")
        .swipeCell(cellPosition: .both, leftSlot:slot, rightSlot: slot)
  • dismissSwipeCell supports selection in editmode
  • dismissSwipeCellForScrollView is used for ScrollView, typically for scenarios with only one cell, such as displaying email content in Mail. Refer to the demo for a demonstration
  • dismissSwipeCellForScrollViewForLazyVStack is used for ScrollView with LazyVStack. Sometimes it may interrupt the swipe menu animation. Personally, it is better to use List instead of LazyVStack unless there is a special need.

Due to SwiftUI’s lack of a good solution to get the scrolling status, the above functions are implemented using Introspect.

For the destructiveDelay form of button, you need to add dismissDestructiveDelayButton() in the action to ensure the cell resets after the action is executed.

Current Issues

  • Animation details are still lacking
  • Limitations in EditMode

Welcome Your Valuable Feedback

SwipeCell is available under the MIT license.

Explore weekly Swift highlights with developers worldwide

Buy me a if you found this article helpful

Related Posts