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

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

Configuring Slots

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

Assembly

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

More configuration options

Swift
cellView()
    .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

Swift
List{
    ...
    }
    .dismissSwipeCell()

For single cell in ScrollView

Swift
ScrollView{
    VStack{
        Text("Mail Title")
            .dismissSwipeCellForScrollView() 
        Text("Mail Content")
        ....
    }
    .frame(maxWidth:.infinity,maxHeight: .infinity)
}
.swipeCell(cellPosition: .both, leftSlot: leftSlot, rightSlot: rightSlot,clip: false)

For LazyVStack in ScrollView

Swift
ScrollView{
    LazyVStack{
    ForEach(lists,id:\.self){ item in
       Text("Swipe in scrollView:\(item)")
        .frame(height:80)
        .swipeCell(cellPosition: .both, leftSlot:slot, rightSlot: slot)
        .dismissSwipeCellForScrollViewForLazyVStack()
    }
}
  • 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.

Weekly Swift & SwiftUI insights, delivered every Monday night. Join developers worldwide.
Easy unsubscribe, zero spam guaranteed