Swipy

Swipy 是一个 SwiftUI 库,用于创建滑动操作。它允许您轻松地将可滑动操作添加到 SwiftUI 视图中,类似于 iOS Mail 和其他应用中的滑动操作。🥳

Swipy, SwiftUI swipe actions library.

轻松实现滑动操作。

Swipy, SwiftUI swipe actions library.

为什么?

SwiftUI 仅为内置 List 视图中的项目提供滑动操作,而且当您想要制作稍微带点可爱且功能丰富的 UI 时,List 变得不可用且极其 Buggy。😞

首先,在发现 List 极其 Buggy 之后,我找到了一个滑动操作库,但它在 ScrollView 中不起作用,然后我决定制作 Swipy。 Swipy 干净简洁,并在最新的 iOS SDK 和 Swift 6 上进行了测试;它支持最低 iOS 15Swift 5。🥳

特性

iOS 支持

Swipy 支持最低 iOS 15

注意

Swipy 的 isSwipingAnItem 绑定与 ScrollView.scrollDisabled(_ disabled: Bool) 修饰符一起使用非常有用,但此修饰符需要最低 iOS 16。但是,您仍然可以使用 isSwipingANItem,因此,如果它为 true,您可以通过某种方式避免滚动。

安装

Swift Package Manager

您可以通过 Swift Package Manager 安装 Swipy。

在 Xcode 上添加 Package Dependency

  1. 在 Xcode 中,File > Add Package Dependencies
  2. 在搜索栏中输入 Swipy GitHub 仓库地址 https://github.com/rohanrhu/Swipy
  3. 将 Swipy 添加到您的项目中。🥳

Package.swift 上添加 Package Dependency

在您的 Package.swift 文件中将 Swipy 添加为依赖项

dependencies: [
    // ...
    .package(url: "https://github.com/rohanrhu/Swipy", from: "1.0.0")
]

或者,Swipy 是一个单独的 Swipy.swift 文件

要在您的项目中使用 Swipy,只需将 Swipy.swift 文件复制到您的项目中。

用法

Swipy 很容易使用。它有两个主要的视图:SwipySwipyAction

使某物可滑动

在大多数情况下,您希望在 ScrollView 中使某物可滑动。首先,您需要定义一个 isSwipingAnItem 状态。

定义 isSwipingAnItem

我们总是需要有一个 isSwipingAnItem 状态,并将其绑定到所有 Swipy 子视图。

重要提示

如果您的 Swipy 视图位于 ScrollView(或任何类型的可滚动对象)内,您的 Swipy 视图将通过绑定设置您的 isSwipingAnItem 状态,以便您能够正确地禁用/启用容器的滚动。

@State private var isSwipingAnItem = false

您将使用它用于您的 ScrollView.scrollDisabled(_ disabled: Bool) 修饰符。

使视图可滑动

要使视图可滑动,您需要将其包装在 Swipy 中,并提供 isSwipingAnItem 绑定。

Swipy(isSwipingAnItem: $isSwipingAnItem) { model in
    Text("Swipe me!")
}

添加操作

您可以通过提供一个闭包来为可滑动视图添加操作,该闭包为每个操作返回一个视图。

Swipy(isSwipingAnItem: $isSwipingAnItem) { model in
    Text("Swipe me!")
} actions: {
    SwipyAction { model in
        Button {
            print("Delete")
            model.unswipe()
        } label: {
            Image(systemName: "trash")
                .foregroundStyle(.white)
        }
    }
}

SwipyModel

SwipyModel 是一个传递给 SwipyAction 的 content 闭包的模型。它提供有关当前滑动状态的信息。

属性

方法

注意

您可以使用 SwipyModel 在执行操作后取消滑动项目。

在命令式地执行操作时取消滑动

您可以通过在 SwipyModel 上调用 unswipe() 方法,在执行操作后取消滑动项目。

SwipyAction { model in
    Button {
        print("Delete")
        model.unswipe()
    } label: {
        Image(systemName: "trash")
            .foregroundStyle(.white)
    }
}

命令式地滑动项目

您可以通过在 SwipyModel 上调用 swipe() 方法,命令式地滑动项目。

Swipy(isSwipingAnItem: $isSwipingAnItem) { model in
    Button {
        model.swipe()
    } label: {
        Text("Swipe me!")
    }
} actions: {
    SwipyAction { model in
        Button {
            model.swipe()
        } label: {
            Image(systemName: "trash")
                .foregroundStyle(.white)
        }
    }
}

定制

Swipy 具有定制功能,使其适合您应用的设计并提供最佳用户体验。

视图:Swipy

Swipy 接受以下参数并向其子视图提供 SwipyModel

它接受这些参数

选项:SwipyActionsMargin: SwipyHorizontalMargin(leading: Double, trailing: Double)

注意

默认值:SwipyHorizontalMargin(leading: 0, trailing: 0)

选项:swipeThreshold: (SwipyModel) -> Double

计算滑动阈值的函数。该函数接受一个 SwipyModel 并返回一个 Double

您很可能想要根据滑动操作视图的宽度来计算滑动阈值,这是 SwipyModel 中的一个属性。(\.SwipyActionsWidth

注意

默认值:\.SwipyActionsWidth

选项:swipeBehavior: SwipySwipeBehavior

滑动操作的行为。您可以使用预定义的行为或创建自定义行为。

选项:scrollBehavior: SwipyScrollBehavior

滚动操作的行为。您可以使用预定义的行为或创建自定义行为。

视图:SwipyAction

SwipyAction 接受这些参数

SwipyAction 为您的子视图 body SwipyModel 提供一个参数。您很可能想要使用它在执行操作后取消滑动您的项目

行为

Swipy 为滑动和滚动操作提供了可定制的行为。可以将这些行为传递给 Swipy 视图,以控制如何处理滑动和滚动操作。

重要提示

如果您想实现自定义行为,这很简单,但为了避免对它们是什么产生误解,请仔细阅读以下内容。

滑动行为

SwipySwipeBehavior 结构体定义了滑动操作的行为。

重要提示

滑动行为决定函数用于决定是否开始滑动会话。您的滑动行为必须返回 false 以防止开始滑动

您可以使用预定义的行为或创建自定义行为。

重要提示

所有默认行为都会在当前的正在进行的用户拖动会话开始后忽略滑动行为,直到它结束;这意味着 .custom { model, gesture in !model.isSwiped && !model.isSwiping /* ... */ }

预定义的滑动行为

示例

Swipy(
    isSwipingAnItem: $isSwipingAnItem,
    swipeBehavior: .normal
) {
    Text("Swipe me!")
} actions: {
    SwipyAction { model in
        Button {
            print("Delete")
            model.unswipe()
        } label: {
            Image(systemName: "trash")
                .foregroundStyle(.white)
        }
    }
}

组合滑动行为

您可以使用 andornot 方法组合滑动行为。

示例

Swipy(
    isSwipingAnItem: $isSwipingAnItem,
    swipeBehavior: .normal.and(.velocity(400)))
) {
    Text("Swipe me!")
} actions: {
    SwipyAction { model in
        Button {
            print("Delete")
            model.unswipe()
        } label: {
            Image(systemName: "trash")
                .foregroundStyle(.white)
        }
    }
}

自定义滑动行为

您可以通过提供自定义决定函数来创建自定义滑动启动器行为。

自定义示例

Swipy(
    isSwipingAnItem: $isSwipingAnItem,
    swipeBehavior: .custom { model, gesture in
        !model.isSwiped && !model.isSwiping && gesture.translation.width > -100
    }
) {
    Text("Swipe me!")
} actions: {
    SwipyAction { model in
        Button {
            print("Delete")
            model.unswipe()
        } label: {
            Image(systemName: "trash")
                .foregroundStyle(.white)
        }
    }
}

高速度和偏移量示例

Swipy(
    isSwipingAnItem: $isSwipingAnItem,
    swipeBehavior: .custom().offset(200).and(.velocity(2000))
) {
    Text("Swipe me!")
} actions: {
    SwipyAction { model in
        Button {
            print("Delete")

        } label: {
            Image(systemName: "trash")
                .foregroundStyle(.white)
        }
    }
}

滚动行为

SwipyScrollBehavior 结构体定义了当用户垂直滚动时防止滑动的行为。

重要提示

滚动行为是一个保护措施,用于防止容器在滑动时垂直滚动。您的滚动行为必须返回 true,才能开始防止当前正在进行的用户拖动会话滑动。

预定义的滚动行为

示例

Swipy(
    isSwipingAnItem: $isSwipingAnItem,
    scrollBehavior: .normal
) {
    Text("Swipe me!")
} actions: {
    SwipyAction { model in
        Button {
            print("Delete")
            model.unswipe()
        } label: {
            Image(systemName: "trash")
                .foregroundStyle(.white)
        }
    }
}

自定义滚动行为

您可以通过提供自定义决定函数来创建自定义滚动行为。

示例

Swipy(
    isSwipingAnItem: $isSwipingAnItem,
    scrollBehavior: .custom { model, gesture in
        !model.isSwiped && abs(gesture.translation.height) > 10
    }
) {
    Text("Swipe me!")
} actions: {
    SwipyAction { model in
        Button {
            print("Delete")
            model.unswipe()
        } label: {
            Image(systemName: "trash")
                .foregroundStyle(.white)
        }
    }
}

组合滚动行为

您可以使用 andornot 方法组合滚动行为。

示例

Swipy(
    isSwipingAnItem: $isSwipingAnItem,
    scrollBehavior: .normal.or(.soft).and(.not(.hard))
) {
    Text("Swipe me!")
} actions: {
    SwipyAction { model in
        Button {
            print("Delete")
            model.unswipe()
        } label: {
            Image(systemName: "trash")
                .foregroundStyle(.white)
        }
    }
}

另一个例子

Swipy(
    isSwipingAnItem: $isSwipingAnItem,
    scrollBehavior: .custom().offset(20).and(.velocity(100))
) {
    Text("Swipe me!")
} actions: {
    SwipyAction { model in
        Button {
            print("Delete")
            model.unswipe()
        } label: {
            Image(systemName: "trash")
                .foregroundStyle(.white)
        }
    }
}

示例

基本示例

这是一个如何使用 Swipy 的基本示例

import SwiftUI

struct MyView: View {
    @State private var isSwipingAnItem = false
    @State private var items = ["Item 1", "Item 2", "Item 3"]

    var body: some View {
        ScrollView {
            LazyVStack(spacing: 20) {
                ForEach(items, id: \.self) { item in
                    Swipy(isSwipingAnItem: $isSwipingAnItem) { model in
                        Text(item)
                            .frame(maxWidth: .infinity)
                            .padding()
                            .background(
                                RoundedRectangle(cornerRadius: 16)
                                    .fill(Color.white)
                                    .shadow(color: .black.opacity(0.1), radius: 5, x: 1, y: 2)
                            )
                            .padding(.horizontal)
                            .foregroundColor(.black)
                    } actions: {
                        HStack {
                            SwipyAction { model in
                                Button {
                                    withAnimation(.bouncy) {
                                        items.removeAll { $0 == item }
                                        model.unswipe()
                                    }
                                } label: {
                                    Image(systemName: "trash")
                                        .font(.system(size: 20))
                                        .padding(.horizontal)
                                }
                                .frame(maxHeight: .infinity)
                                .background(Color.red)
                                .foregroundColor(.white)
                                .cornerRadius(16)
                            }

                            SwipyAction { model in
                                Button {
                                    withAnimation(.bouncy) {
                                        model.unswipe()
                                    }
                                } label: {
                                    Image(systemName: "pencil")
                                        .font(.system(size: 20))
                                        .padding(.horizontal)
                                }
                                .frame(maxHeight: .infinity)
                                .background(Color.gray)
                                .foregroundColor(.white)
                                .cornerRadius(16)
                            }
                        }
                    }
                }
            }
            .padding(.vertical)
        }
        .scrollDisabled(isSwipingAnItem)
    }
}

捐赠 ❤️

您可以通过捐款来支持开发。您有以下捐赠选项

您还可以通过购买我的 MacsyZones 应用来支持我。🥳

另请参阅 MacsyZones GitHub 仓库,因为它也是开源的。🥳

加密货币捐赠

货币 地址
BTC bc1qhvlc762kwuzeawedl9a8z0duhs8449nwwc35e2
ETH / USDT / USDC 0x1D99B2a2D85C34d478dD8519792e82B18f861974
XMR 88qvS4sfUnLZ7nehFrz3PG1pWovvEgprcUhkmVLaiL8PVAFgfHjspjKPLhWLj3DUcm92rwNQENbJ1ZbvESdukWvh3epBUty

最好是捐赠 USDT 或 USDC,但您可以捐赠以上任何一种货币。🥳

贡献

我们欢迎对 Swipy 的贡献。请参阅 CONTRIBUTING.md 文件以获取更多信息。

行为准则

我们采纳了一份行为准则,希望项目参与者遵守。请阅读CODE_OF_CONDUCT.md,以便您了解哪些行为可以容忍,哪些行为不能容忍。

许可

版权所有 (C) 2024, Oğuzhan Eroğlu rohanrhu2@gmail.com (https://meowingcat.io/)

基于 MIT 许可证授权。

有关更多信息,请参阅LICENSE