条件回忆

在上一章节中,咱们学习了Model-View-ViewModel架构模式,并完成了增加、删去办法。其中删去办法运用的是最简单的,利用SwiftUI自带的contextMenu上下文菜单按钮控件,完成长按引发删去操作,点击删去操作调用ViewModel视图模型中的删去办法删去指定ID的数据项。

而在卡片式列表运用中,常用的删去办法是横向滑动引发删去的办法,网上也有许多运用SwiftUI自带的EditButton引发横向删去的办法,但总有这样那样的原因导致自带的滑动删去并不好用。

在本章中,咱们将学习一种运用gesture手势修饰符完成滑动删去的交互

交互动作:向左滑动引发删去操作

在SwiftUI供给的手势操作中,有onTapGesture点击手势、LongPressGesture长按手势、DragGesture拖拽手势三种主要操作。而要运用这三种手势,需求运用到gesture手势修饰符,如下代码所示:

// 拖拽手势
.gesture(
    DragGesture()
        .onChanged { value in
            // 拖动时的操作
        }
        .onEnded { value in
            // 拖动结束时操作
        }
)

运用DragGesture拖拽手势前,需求运用@GestureState属性包装器界说一个拖拽方位参数viewState,用来记录咱们的拖拽前的初始方位CGSize.zero,也用来监听和更新UI,如下代码所示:

@State var viewState = CGSize.zero

由于咱们要拖动的是单张身份卡片,因此需求给身份卡片构件CardView增加拖动方位的偏移量修饰符,如下代码所示:

//设置只能从右往左拖动
.offset(x: self.viewState.width < 0 ? self.viewState.width : 0)

上述代码中,咱们给CardView的视图内容增加了offset偏移量修饰符,设置只能沿X轴拖动,并增加条件假如拖拽前的初始方位viewState小于0,则可被沿X轴横向拖拽,假如大于等于0,即向右边拖拽时,则维持方位为0,保证CardView的视图内容只能从右往左拖动。

紧接着,咱们给DragGesture手势增加更新办法,如下代码所示:

// 拖拽手势
.gesture(
    DragGesture()
        .onChanged { value in
            // 拖动时的操作
            self.viewState = value.translation
        }
        .onEnded { value in
            // 拖动结束时操作
            self.viewState = .zero
        }
)

上述代码中,咱们在onChanged拖动时,让身份卡的方位viewState等于拖动的方位translation,如此便完成了卡片拖动动作。而在onEnded拖动结束时,咱们让身份卡片回到初始的方位zero。

完成之后,咱们能够尝试拖动下卡片,如下图所示:

为了突出当时正在进行拖拽删去的操作,咱们能够给身份卡片CardView在拖拽时增加样式,比如在向左拖拽到左边一定方位的时分,让卡片填充一个布景色彩。

那么首要咱们先声明拖动到某个方位是操作删去的方位,并还需再声明一个变量奉告体系当时是否在履行删去操作,如下代码所示:

@State var valueToBeDeleted: CGFloat = -75
@State var readyToBeDeleted: Bool = false

下一步咱们能够在拖动时增加判别当时拖动方位是否到达预备删去的方位,如下代码所示:

self.readyToBeDeleted = self.viewState.width < self.valueToBeDeleted ? true : false

并且在删去时,给身份卡片修改布景色,如下代码所示:

.background(self.readyToBeDeleted ? Color(.systemRed) : .white)

当然,拖动结束后,还需求更新readyToBeDeleted是否操作更新的状态为false,如下代码所示:

self.readyToBeDeleted = false

样式完成后,咱们来完成删去逻辑,咱们在ViewModel视图模型中创立了删去办法deleteItem,deleteItem办法需求基于卡片ID进行指定删去,而在CardView卡片构件中,并没有ID,而数据集和其ID是在ContentView视图中存在。

要想取得UUID,咱们还需求在ViewModel视图模型中创立一个取得数据UUID的办法,如下代码所示:

// 取得数据项的UUID
func getItemById(itemId: UUID) -> Model? {
	return models.first(where: { $0.id == itemId }) ?? nil
}

上述代码中,咱们创立了一个取得数据项的ID的办法,经过传入点击项的UUID,然后回来对应数据项在数据会集的UUID,奉告体系当时操作的数据项是models数组中的哪一个数据。

然后咱们再回到ContentView视图中,在CardView身份卡片视图中声明相关的变量,如下代码所示:

var viewModel: ViewModel
var itemId: UUID
var item: Model? {
	return viewModel.getItemById(itemId: itemId)
}

上述代码中,咱们运用全局变量引证ViewModel模型视图,并且声明晰两个参数itemId和item。itemId为UUID格局,作为数据项的唯一标识符,item为契合Model模型的数据项,用于经过ID找到Model模型的数据。

由于CardView声明晰变量,因此在引证CardView的当地需求绑定相关的参数,如下代码所示:

// 卡片视图
CardView(platformIcon: item.platformIcon, title: item.title, platformName: item.platformName, indexURL: item.indexURL, viewModel: viewModel, itemId: item.id)

下一步,咱们就能够拖动结束时,判别身份卡是否拖动到删去的方位,假如是,则调用ViewModel视图模型的deleteItem删去数据项办法,删去指定ID的数据项,如下代码所示:

if self.viewState.width < self.valueToBeDeleted {
    self.viewModel.deleteItem(itemId: itemId)
}

咱们尝试拖拽卡片操作下删去操作的交互,如下图所示:

373fdd30-20b0-4106-828a-7ba3683850a3.gif

二次提示:运用正告弹窗提示用户

上面咱们完成了滑动删去的操作,但操作太直接了,或许存在用户误操作的情况。为了防止用户误操作,咱们能够增加一层判别机制。当用户引发删去操作时提示用户当时正在履行删去操作,请求用户的二次承认,用户承认后方可履行删去。

这种强提示的用户场景下,咱们能够运用正告弹窗奉告用户。正告弹窗和模态弹窗的运用办法类型,需求提早声明一个是否翻开正告弹窗的变量,如下代码所示:

@State var showDeleteAlert: Bool = false

关于删去运用的正告弹窗,咱们能够独自构建正告弹窗视图,然后再调用,如下代码所示:

// 删去弹窗
    private var deleteAlert: Alert {
        let alert = Alert(title: Text(""), message: Text("确定要删去吗?"), primaryButton: .destructive(Text("承认")) {
        }, secondaryButton: .cancel(Text("撤销")))
        return alert
    }

上述代码中,咱们创立了一个正告弹窗deleteAlert,视图类型为Alert弹窗。在deleteAlert弹窗中,咱们设置了Alert的标题、副标题、主要按钮、撤销按钮,终究回来这个Alert样式给到deleteAlert。

要运用Alert弹窗的办法也比较简单,能够运用alert弹窗修饰符,如下代码所示:

//翻开删去承认弹窗
.alert(isPresented: $showDeleteAlert, content: { deleteAlert })

上述代码中,咱们给整个卡片视图增加了alert正告弹窗修饰符,并绑定翻开弹窗的参数showDeleteAlert,正告弹窗的内容为咱们独自构建的deleteAlert删去弹窗视图。

然后咱们能够再拖动判别删去的时分触发翻开删去弹窗,在删去弹窗中点击确定时,调用删去办法,如下代码所示:

self.showDeleteAlert.toggle()

完成之后,咱们再在模拟器中预览下操作后的作用,如下图所示:

2c5347a5-10cc-4809-a8e4-e2a0df6e6103.gif

体会升级:文字说明和轰动反应

完成滑动删去动作后,总感觉如同少了点东西。操作几回后发现,当用户拖动卡片向左滑动时,的确能够经过色彩表示当时用户正在履行操作,并且的确有正告弹窗进行二次提示,但关于小白用户来说,也的确在正告弹窗出来之前是不知道正在操作删去的。

这存在学习本钱,咱们能够加一点点小细节,当用户向左滑动卡片时,在卡片背面呈现提示文字,如下代码所示:

// 提示文字
HStack {
	Spacer()
	Text("左滑删去")
	.padding()
	.foregroundColor(Color(.systemGray))
}

上述代码中,咱们在List列表中遍历数据项时,在ZStack仓库视图包裹中的NavigationLink导航链接、CardView身份卡片中增加了一个“提示文字”视图,运用Spacer空间垫片将Text文字撑到右边。

由于ZStack仓库视图的层级关系和代码的前后顺序有关,Text文字在CardView身份卡片前,那么常规情况下文字会被遮挡。而当CardView身份卡片向左滑动时,就呈现了Text文字了。

除了文字外,咱们还能够再触发删去操作的时分增加轰动反应,进一步提升用户体会。
SwiftUI供给了反应生成器 UIFeedbackGenerator供开发者调用iOS体系的线性马达形成轰动作用。咱们能够创立一个新的Swift文件专门管理轰动反应内容。

创立一个新的文件夹,命名为SupportFile,并创立一个新的Swift文件,命名为Haptics,如下图所示:

然后咱们引进SwiftUI,并创立一个类来管理轰动反应的内容,如下代码所示:

import Foundation
import SwiftUI
struct Haptics {
    static func hapticSuccess() {
        let generator = UINotificationFeedbackGenerator()
        generator.notificationOccurred(.success)
    }
    static func hapticWarning() {
        let generator = UINotificationFeedbackGenerator()
        generator.notificationOccurred(.warning)
    }
}

上述代码中,咱们创立了一个结构体Haptics,声明晰2个办法hapticSuccess成功时的轰动动效、hapticWarning正告时的轰动动效,并调用UINotificationFeedbackGenerator轰动反应生成器组件,赋予不同的轰动反应作用:success或者warning。

紧接着咱们回到ContentView视图中,在CardView视图中拖动身份卡片操作时,咱们调用Haptics中的轰动反应办法,如下代码所示:

Haptics.hapticWarning()

如此,在用户向左滑动身份卡履行删去操作时,体系就会基于用户一个轰动反应,奉告用户这是一个“值得谨慎的操作”,进一步地供给用户的体会。

许多时分,加一点点小细节,整个运用会上一个台阶。

项目小结

在本章中,咱们完成了自界说滑动删去的交互操作,这比起直接运用List自带的滑动删去,或者运用简单的控件完成愈加“高级一些”,当然这也增加了一些学习本钱。

关于许多时分SwiftUI自带控件无法满意开发需求时,咱们要具有各种自界说完成控件或者操作能力,这是区别只会用框架的“搬砖员”和真正的程序员的重要特征。

别的咱们还在该项目中增加了“一点点细节”,让运用的交互性和用户体会更好一些,这也是作为一个创作者的寻求。其实也想表达一个观念,假如一个程序员只会运用框架而加入自己的思考和理解,那么25岁和30岁也仅仅打字快慢的区别罢了。

接下来的章节,咱们想要做的还有许多,我也会把每一步的完成细节和操作流程都分享出来,请保持期待吧~

版权声明

本文为稀土技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!