Swipe Cell SwiftUI is a simple swipe-to-reveal menu view written in Swift. It supports iOS / iPadOs 14 or later. The purpose is to replace the default SwiftUI List .onDelete(perform: ...) modifier since its functionality is limited to deletion only and only works on List rows. Moreover, rows in an iOS 14 LazyVStack do not support swipe to reveal out of the box. Swipe Cell SwiftUI allows adding multiple menu buttons on the left and right side and also features a swipe out functionality (e.g. swipe to delete).
This repo only contains the Swift package, no example XCode project. Please download the example project here. Simply add the swift package through the Swift Package mMnager (see below - Installation).
Add multiple leading and/or trailing menu buttons
Fully customize each button view
Add swipe out behavior to one button on each side
Add a haptic feedback to indicate that the swipe out will be triggered
from version 2.0: auto closes an open cell if the user swipes another cell
from version 2.1: make an open cell close on tap on another cell (example below)
Check out the example for details.
Installation through the Swift Package Manager (SPM) is recommended.
SPM: Select your project (not the target) and then select the Swift Packages tab. Click + and type SwipeCellSUI should find the package on github. Otherwise copy and paste the whole URL of this repo.
Check out the version history below for the current version.
Make sure to import SwipeCellSUI in every file where you use SwipeCellSUI.
import SwipeCellSUI
Before version 2.1.2, the swipe cell modifier seems to block List scrolling. This was fixed in 2.1.2. However, tapping the revealed buttons still does not seem to work properly with List views. The touch event works only for one button even if another button was tapped.
Check out the following example. This repo only contains the Swift package, no example code. Please download the example project here.
struct RowView: View {
var availableWidth: CGFloat
var item: String
@State private var isPinned: Bool = false
var deletionCallback: (String)->()
@Binding var currentUserInteractionCellID: String?
var body: some View {
Text(item).frame(width: availableWidth, height:100)
.background(RoundedRectangle(cornerRadius: 5).foregroundColor(.green))
.swipeCell(id: self.item, cellWidth: availableWidth, leadingSideGroup: leftGroup(), trailingSideGroup: rightGroup(), currentUserInteractionCellID: $currentUserInteractionCellID)
.onTapGesture {
// this will make another cell close, in case its side panel is open:
self.currentUserInteractionCellID = item
// implement other functionality associated to tapping the row if required.
}
// you can customize settings by adding the settings parameter at the end
}
func leftGroup()->[SwipeCellActionItem] {
return [ SwipeCellActionItem(buttonView: {
self.pinView(swipeOut: false)
}, swipeOutButtonView: {
self.pinView(swipeOut: true)
}, buttonWidth: 80, backgroundColor: .yellow, swipeOutAction: true, swipeOutHapticFeedbackType: .success, swipeOutIsDestructive: false)
{
print("pin action!")
self.isPinned.toggle()
}]
}
func pinView(swipeOut: Bool)->AnyView {
Group {
Spacer()
VStack(spacing: 2) {
Image(systemName: self.isPinned ? "pin.slash": "pin").font(.system(size: 24)).foregroundColor(.white)
Text(self.isPinned ? "Unpin": "Pin").fixedSize().font(.system(size: 14)).foregroundColor(.white)
}.frame(maxHeight: 80).padding(.horizontal, swipeOut ? 20 : 5)
if swipeOut == false {
Spacer()
}
}.animation(.default).castToAnyView()
}
func rightGroup()->[SwipeCellActionItem] {
let items = [
SwipeCellActionItem(buttonView: {
VStack(spacing: 2) {
Image(systemName: "person.crop.circle.badge.plus").font(.system(size: 22)).foregroundColor(.white)
Text("Share").fixedSize().font(.system(size: 12)).foregroundColor(.white)
}.frame(maxHeight: 80).castToAnyView()
}, backgroundColor: .blue)
{
print("share action!")
},
SwipeCellActionItem(buttonView: {
VStack(spacing: 2) {
Image(systemName: "folder.fill").font(.system(size: 22)).foregroundColor(.white)
Text("Move").fixedSize().font(.system(size: 12)).foregroundColor(.white)
}.frame(maxHeight: 80).castToAnyView()
}, backgroundColor: .purple, actionCallback: {
print("folder action")
}),
SwipeCellActionItem(buttonView: {
self.trashView(swipeOut: false)
}, swipeOutButtonView: {
self.trashView(swipeOut: true)
}, backgroundColor: .red, swipeOutAction: true, swipeOutHapticFeedbackType: .warning, swipeOutIsDestructive: true) {
print("delete action")
self.deletionCallback(item)
}
]
return items
}
func trashView(swipeOut: Bool)->AnyView {
VStack(spacing: 3) {
Image(systemName: "trash").font(.system(size: swipeOut ? 28 : 22)).foregroundColor(.white)
Text("Delete").fixedSize().font(.system(size: swipeOut ? 16 : 12)).foregroundColor(.white)
}.frame(maxHeight: 80).animation(.default).castToAnyView()
}
}
mihapp's fix for usage with List - buttons are triggered separately now.
Fixed scrolling issue for List views. Caution: Tapping the revealed menu buttons still doesn't work properly if you attach the swipe cell modifier to the rows of a List (better use ScrollView + LazyVStack).
minor change: updated documentation.
Breaking changes:
Swipe Cell menu opening less easily now.
Settings properties can now be changed as expected.
Initial public release.
SwipeCellSUI is available under the MIT license. See the LICENSE file for more info.
link |
Stars: 57 |
Last commit: 1 year ago |
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics