Swipy 是一个 SwiftUI 库,用于创建滑动操作。它允许您轻松地将可滑动操作添加到 SwiftUI 视图中,类似于 iOS Mail 和其他应用中的滑动操作。🥳
轻松实现滑动操作。
SwiftUI 仅为内置 List 视图中的项目提供滑动操作,而且当您想要制作稍微带点可爱且功能丰富的 UI 时,List 变得不可用且极其 Buggy。😞
首先,在发现 List 极其 Buggy 之后,我找到了一个滑动操作库,但它在 ScrollView 中不起作用,然后我决定制作 Swipy。 Swipy 干净简洁,并在最新的 iOS SDK 和 Swift 6 上进行了测试;它支持最低 iOS 15 和 Swift 5。🥳
ScrollView 中使用Swipy 支持最低 iOS 15。
注意
Swipy 的 isSwipingAnItem 绑定与 ScrollView 的 .scrollDisabled(_ disabled: Bool) 修饰符一起使用非常有用,但此修饰符需要最低 iOS 16。但是,您仍然可以使用 isSwipingANItem,因此,如果它为 true,您可以通过某种方式避免滚动。
您可以通过 Swift Package Manager 安装 Swipy。
https://github.com/rohanrhu/Swipy。在您的 Package.swift 文件中将 Swipy 添加为依赖项
dependencies: [
// ...
.package(url: "https://github.com/rohanrhu/Swipy", from: "1.0.0")
]
要在您的项目中使用 Swipy,只需将 Swipy.swift 文件复制到您的项目中。
Swipy 很容易使用。它有两个主要的视图:Swipy 和 SwipyAction。
在大多数情况下,您希望在 ScrollView 中使某物可滑动。首先,您需要定义一个 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 是一个传递给 SwipyAction 的 content 闭包的模型。它提供有关当前滑动状态的信息。
swipeOffset: 当前滑动偏移量。isSwiping: 一个布尔值,指示是否正在滑动项目。isScrolling: 一个布尔值,指示容器是否正在滚动。isSwiped: 一个布尔值,指示项目是否已滑动。swipeActionsWidth: 滑动操作视图的宽度。contentSize: 内容视图的大小。swipeActionsMargin: 滑动操作的边距。swipeThreshold: 滑动阈值计算函数。swipeBehavior: 滑动操作的行为。scrollBehavior: 滚动操作的行为。swipe(): 滑动项目。unswipe(): 取消滑动项目。注意
您可以使用 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 接受以下参数并向其子视图提供 SwipyModel。
它接受这些参数
isSwipingAnItem:一个绑定,指示是否正在滑动项目。content:一个闭包,返回要滑动的视图。actions:一个闭包,返回每个操作的视图。SwipyActionsMargin:滑动操作的边距,一个 SwipyHorizontalMargin 结构体。swipeThreshold:滑动阈值计算函数。swipeBehavior:滑动操作的行为,一个 SwipySwipeBehavior 结构体。scrollBehavior:滚动操作的行为,一个 SwipyScrollBehavior 结构体。leading:滑动操作左侧的边距。trailing:滑动操作右侧的边距。注意
默认值:SwipyHorizontalMargin(leading: 0, trailing: 0)
计算滑动阈值的函数。该函数接受一个 SwipyModel 并返回一个 Double。
您很可能想要根据滑动操作视图的宽度来计算滑动阈值,这是 SwipyModel 中的一个属性。(\.SwipyActionsWidth)
注意
默认值:\.SwipyActionsWidth
滑动操作的行为。您可以使用预定义的行为或创建自定义行为。
滚动操作的行为。您可以使用预定义的行为或创建自定义行为。
SwipyAction 接受这些参数
content(SwipyModel):一个闭包,返回该操作的视图。SwipyAction 为您的子视图 body SwipyModel 提供一个参数。您很可能想要使用它在执行操作后取消滑动您的项目。
Swipy 为滑动和滚动操作提供了可定制的行为。可以将这些行为传递给 Swipy 视图,以控制如何处理滑动和滚动操作。
重要提示
如果您想实现自定义行为,这很简单,但为了避免对它们是什么产生误解,请仔细阅读以下内容。
SwipySwipeBehavior 结构体定义了滑动操作的行为。
重要提示
滑动行为决定函数用于决定是否开始滑动会话。您的滑动行为必须返回 false 以防止开始滑动。
您可以使用预定义的行为或创建自定义行为。
重要提示
所有默认行为都会在当前的正在进行的用户拖动会话开始后忽略滑动行为,直到它结束;这意味着 .custom { model, gesture in !model.isSwiped && !model.isSwiping /* ... */ }。
SwipySwipeBehavior.normal:默认行为。SwipySwipeBehavior.soft:更柔和的滑动行为。SwipySwipeBehavior.hard:更硬的滑动行为。SwipySwipeBehavior.straight:没有保护行为。SwipySwipeBehavior.disabled:禁用滑动操作。SwipySwipeBehavior.swiping():当一个项目正在被滑动时的行为。SwipySwipeBehavior.swiped():当一个项目被滑动时的行为。SwipySwipeBehavior.offset(_ offset: Double):基于滑动偏移量的行为。SwipySwipeBehavior.velocity(_ velocity: Double):基于滑动速度的行为。示例
Swipy(
isSwipingAnItem: $isSwipingAnItem,
swipeBehavior: .normal
) {
Text("Swipe me!")
} actions: {
SwipyAction { model in
Button {
print("Delete")
model.unswipe()
} label: {
Image(systemName: "trash")
.foregroundStyle(.white)
}
}
}
您可以使用 and、or 和 not 方法组合滑动行为。
示例
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,才能开始防止当前正在进行的用户拖动会话滑动。
SwipyScrollBehavior.normal:默认行为。SwipyScrollBehavior.soft:更柔和的滚动行为。SwipyScrollBehavior.hard:更硬的滚动行为。SwipyScrollBehavior.disabled:禁用滚动操作。示例
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)
}
}
}
您可以使用 and、or 和 not 方法组合滚动行为。
示例
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。