Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/c-villain/SwipeActions

Swipe actions, swipe menu based on SwiftUI
https://github.com/c-villain/SwipeActions

ios13 swiftui swiftui-animations swiftui-framework swipeactions swipemenu

Last synced: about 2 months ago
JSON representation

Swipe actions, swipe menu based on SwiftUI

Awesome Lists containing this project

README

        

# SwipeActions

[![Latest release](https://img.shields.io/github/v/release/c-villain/SwipeActions?color=brightgreen&label=version)](https://github.com/c-villain/SwipeActions/releases/latest)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fc-villain%2FSwipeActions%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/c-villain/SwipeActions)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fc-villain%2FSwipeActions%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/c-villain/SwipeActions)
[![](https://img.shields.io/badge/SPM-supported-DE5C43.svg?color=brightgreen)](https://swift.org/package-manager/)
![](https://img.shields.io/github/license/c-villain/SwipeActions)

[![contact: @lexkraev](https://img.shields.io/badge/contact-%40lexkraev-blue.svg?style=flat)](https://t.me/lexkraev)
[![Telegram Group](https://img.shields.io/endpoint?color=neon&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fswiftui_dev)](https://telegram.dog/swiftui_dev)


corner radius

Library for creating swipe actions for any SwiftUI View, similar to Apple's [```swipeActions(edge:allowsFullSwipe:content:)```](https://developer.apple.com/documentation/swiftui/view/swipeactions(edge:allowsfullswipe:content:)) that available from iOS 15 and only in [List](https://developer.apple.com/documentation/swiftui/lists) 🤷🏼‍♂️.
You can use ```SwipeActions``` in project targeting iOS 13 with any view (e.g. ```Text``` or ```VStack```).

👨🏻‍💻 Feel free to subscribe to channel **[SwiftUI dev](https://t.me/swiftui_dev)** in telegram.

## Requirements

- iOS 13.0 or macOS 10.15

## Installation

#### Swift Package Manager

To integrate ```SwipeActions``` into your project using SwiftPM add the following to your `Package.swift`:

```swift
dependencies: [
.package(url: "https://github.com/c-villain/SwipeActions", from: "0.1.0"),
],
```
or via [XcodeGen](https://github.com/yonaskolb/XcodeGen) insert into your `project.yml`:

```yaml
name: YourProjectName
options:
deploymentTarget:
iOS: 13.0
packages:
SwipeActions:
url: https://github.com/c-villain/SwipeActions
from: 0.1.0
targets:
YourTarget:
type: application
...
dependencies:
- package: SwipeActions
```

## Types

Different types of menu:
- .swiped
- .slided


Example for .swiped and .slided menu

Both types can be upgraded with full swiping:


Example of full swipe with non-destructive role

## Quick start

Adding both leading and trailing swipe actions:


Example with leading and trailing swipes

Use ```Leading { ... }``` and ```Trailing { ... }``` closures inside ```.addSwipeAction { ... }``` modifier:

```swift
import SwipeActions

struct YourView: View {

var body: some View {
ScrollView {
LazyVStack {
ForEach(1...100, id: \.self) { cell in
Text("Cell \(cell)")
.frame(height: 50, alignment: .center)
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
.addSwipeAction {
Leading { //<= HERE

Button {
print("edit \(cell)")
} label: {
Image(systemName: "pencil")
.foregroundColor(.white)
}
.frame(width: 60, height: 50, alignment: .center)
.contentShape(Rectangle())
.background(Color.green)

}
Trailing { //<= HERE

Button {
print("remove \(cell)")
} label: {
Image(systemName: "trash")
.foregroundColor(.white)
}
.frame(width: 60, height: 50, alignment: .center)
.contentShape(Rectangle())
.background(Color.red)

Button {
print("Inform \(cell)")
} label: {
Image(systemName: "bell.slash.fill")
.foregroundColor(.white)
}
.frame(width: 60, height: 50, alignment: .center)
.background(Color.blue)
}
}
}
}
}
}
}
```

Adding swipe actions to one side of view:


Example with trailing swipe menu

Use ```.addSwipeAction(edge: ) { ... }``` modifier, ```edge``` - a ```HorizontalAlignment``` value input parameter - with two cases of using ```.leading``` or ```.trailing```

```swift
import SwipeActions

struct YourView: View {

var body: some View {
ScrollView {
LazyVStack {
ForEach(1...100, id: \.self) { cell in
Text("Cell \(cell)")
.frame(height: 50, alignment: .center)
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
.addSwipeAction(edge: .trailing) { // <== HERE! choose .trailing or .leading
Button {
print("remove \(cell)")
} label: {
Image(systemName: "trash")
.foregroundColor(.white)
}
.frame(width: 60, height: 50, alignment: .center)
.contentShape(Rectangle())
.background(Color.red)

Button {
print("Inform \(cell)")
} label: {
Image(systemName: "bell.slash.fill")
.foregroundColor(.white)
}
.frame(width: 60, height: 50, alignment: .center)
.background(Color.blue)

}
}
}
}
}
}
```

For automatically closing other opened actions during sliding:


Example with auto closing swipe actions



Add ```SwipeState``` var to your ```View``` and pass it as a ```binding``` in ```.addSwipeAction(state:)```:

```swift

struct YourView: View {
@State var state: SwipeState = .untouched // <=== HERE

var body: some View {
ScrollView {
VStack(spacing: 2) {
ForEach(1 ... 30, id: \.self) { cell in
Text("Cell \(cell)")
.addSwipeAction(state: $state) { // <=== HERE
....
}
}
}
}
}
}
```



### Full swipe action

For full swipe use modifier ```.addFullSwipeAction(menu:swipeColor:swipeRole:state:content:action:)```

Basically there are two main ```SwipeRole``` for full swipe action: ```.destructive``` (defaults) and other one.


.destructive

This role is used for closing/hiding/removing cell.



Example of full swipe with destructive role




```swift

struct YourView: View {

@State var range: [Int] = [1,2,3,4,5,6,7,8,9,10]

var body: some View {
ScrollView {
VStack(spacing: 2) {
ForEach(range, id: \.self) { cell in
Text("Cell \(cell)")
.addFullSwipeAction(menu: .slided,
swipeColor: .red) { // <=== Color is the same as last button in Trailing for full effect
Leading {
...
}
Trailing {
...

Button {
withAnimation {
if let index = range.firstIndex(of: cell) {
range.remove(at: index)
}
}
} label: {
Image(systemName: "trash")
.foregroundColor(.white)
}
.contentShape(Rectangle())
.frame(width: 60)
.frame(maxHeight: .infinity)
.background(Color.red) // <=== Look here
}
} action: { // <=== action for full swiping
withAnimation {
if let index = range.firstIndex(of: cell) {
range.remove(at: index)
}
}
}
}
}
}
}
}
```




.defaults

This role is used for making some action on cell.


Example of full swipe with non-destructive role




```swift

struct YourView: View { ]

var body: some View {
ScrollView {
VStack(spacing: 2) {
ForEach(1...10, id: \.self) { cell in
Text("Cell \(cell)")
.addFullSwipeAction(menu: .slided,
swipeColor: .green, // <=== Color is the same as last button in Trailing for full effect
swipeRole: .defaults) { // <=== Add this parameter
Leading {
...
}
Trailing {
...

Button {

} label: {
Image(systemName: "trash")
.foregroundColor(.white)
}
.contentShape(Rectangle())
.frame(width: 60)
.frame(maxHeight: .infinity)
.background(Color.green) // <=== Look here
}
} action: { // <=== action for full swiping
...
}
}
}
}
}
}
```


### Recommendations for use

With dynamic height content.


use ```.frame(maxHeight: .infinity)```

```swift
YourView()
.addSwipeAction(menu: .slided, edge: .trailing) {
Button {
...
} label: {
Image("trash")
.font(.system(size: 20.0))
.foregroundColor(.white)
.frame(width: 68, alignment: .center)
.frame(maxHeight: .infinity) // <====== HERE
.background(.red)
}
}
```

With transparent colored views.

There is *no* restrictions or any recommendations for using with ```.slided``` type!

With ```.swiped``` use *non-tranparent* color layer or the same color with ```alfa = 1.0```:

```swift
ForEach(1 ... 30, id: \.self) { cell in
Text("Cell \(cell)")
.padding()
.frame(height: 80)
.frame(maxWidth: .infinity)
//.background(Color.green.opacity(0.2)) // <=== DON'T USE SUCH WAY!
//.background(Color(red: 0.841, green: 0.956, blue: 0.868)) // <== USE THIS WAY!
.background( // <== OR THIS WAY!
ZStack {
Color(UIColor.systemBackground) // non-transparent color layer
Color.green.opacity(0.2)
}
)

.contentShape(Rectangle())
.listStyle(.plain)
.addSwipeAction(menu: .swiped, // <=== SWIPED TYPE
state: $state) {
Leading {
...
}
}
...
}
```

With List.

Basically if you have minimum deployments target for your app is iOS 15 I recommend to use Apple's [swipe actions](https://developer.apple.com/documentation/swiftui/view/swipeactions(edge:allowsfullswipe:content:)) for List. Anyway you may use this.

Due to some features for working with ```List``` you should:

- specify a frame for cell width, e.g. ```.frame(width: UIScreen.main.bounds.size.width - 32, height: 80)``` and a frame for buttons on swipe actions, e.g. ```.frame(width: 60, height: 80)```. Note that height in frames should be the same!

- add modifier ```.onTapGesture { ... }``` for cell to override tapping on swipe action buttons

- add modifier ```.listRowInsets(EdgeInsets())``` for cell

```swift
List(elements) { e in
Text(e.name)
.frame(width: UIScreen.main.bounds.size.width - 32, height: 80) // <=== HERE
.background(Color(UIColor.systemBackground))
.onTapGesture { // <=== HERE
print("on cell tap!")
}
.addSwipeAction(menu: .swiped,
edge: .trailing,
state: $state) {
Button {
print("remove")
} label: {
Image(systemName: "trash")
.foregroundColor(.white)
}
.frame(width: 60, height: 80, alignment: .center) // <=== HERE
.contentShape(Rectangle())
.background(Color.red)
}
.listRowInsets(EdgeInsets()) // <=== HERE
}
.padding(16)
```

Look for code in the example.

With no horizontal padding views.

To avoid effect when content in swipe actions started showing immediately after view with no horizontal padding


Demo without insets

in ```.addSwipeAction { ... }``` add ```Rectangle``` filled with *same* color as root view:


Demo with insets

```swift
YourView()
.frame(height: 80)
.frame(maxWidth: .infinity)
.background(Color.green.opacity(0.8)) // <=== Look here!
.addSwipeAction(edge: .trailing) {
Rectangle() // <=== HERE!
.fill(Color.green.opacity(0.8)) // <=== Don't forget!
.frame(width: 8.0, height: 80)

Button {
} label: {
Image(systemName: "message")
.foregroundColor(.white)
}
.frame(width: 60, height: 80)
.contentShape(Rectangle())
.background(Color.blue)
}
```

With context menu.


Demo without insets

Due to some difficulties for SwiftUI to detect gestures for sliding view and opening context menu I recommend you to use
`.contextMenu` after `.addSwipeAction` (or `addFullSwipeAction`):

```swift
YourView()
.frame(height: 80)
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
.padding()
.background(Color(UIColor.systemBackground))
.addFullSwipeAction(...) { ... } // <=== Look here!
.contextMenu { ... }
```

Actually if you don't use `.contentShape(Rectangle())`, you can also add `.contextMenu` before `.addSwipeAction` (or `addFullSwipeAction`):

```swift
YourView()
.frame(height: 80)
.frame(maxWidth: .infinity)
//.contentShape(Rectangle()) // <=== Look here!
.padding()
.contextMenu { ... } // <=== Look here!
.background(Color(UIColor.systemBackground))
.addFullSwipeAction(...) { ... } // <=== Look here!
```

How to add swpe hint.

Use modifier `.swipeHint`:

```swift
ForEach(range, ...) {
YourCell()
...
.addFullSwipeAction(
menu: .slided,
swipeColor: .red,
state: $state) {
Leading {
...
}
Trailing {
...
}
}
.swipeHint(cell == range.first, hintOffset: 120.0) // for trailing <== LOOK HERE
.swipeHint(cell == range[1] , hintOffset: -120.0) // for leading <== LOOK HERE
...
}
```

## Communication

- If you **found a bug**, open an issue or submit a fix via a pull request.
- If you **have a feature request**, open an issue or submit a implementation via a pull request or hit me up on **[email protected]** or **[telegram](https://t.me/lexkraev)**.
- If you **want to contribute**, submit a pull request onto the master branch.

## License

SwipeActions package is released under an MIT license.

## Special thanks

Thx to [Prafulla Singh](https://prafullkumar77.medium.com/) for inspriration with his [SwiftUI tutorial](https://prafullkumar77.medium.com/swiftui-how-to-make-custom-swipe-able-cell-727a27abdddd).