我正在参加「启航计划」
作用图
这个是前段时间项目新增的一个功用,刚刚开端组员是用UIScrollView
+ UIView
完结的,但这种完结办法属实是有点low,后续闲暇时笔者用UICollectionView
简略完结了下。
思路
简略理一下思路,首先是把整个页面先布局出来,这儿涉及到一个UICollectionView
的Section
背景色的问题,有需求的能够点这儿,有具体的介绍。移动动画也很简略,先获取起点cell和终点cell,再新建一个动画的AnimationItem
,依据获取的起点和终点cell动画就行,最后再完结拖拽排序作用。
大致总结一下:
- 布局
- 移动动画
- 拖拽排序
下面就依据思路一步步来。我们必定要有一个认识,不论是多么杂乱的动画,只需把它分化开来,按过程一步一步完结就很简略。
完结
我们就跟上面的思路一步一步完结。
布局
首先想到必定是新建一个Model
来办理这个数据,新建Model也要有点技巧。
struct ItemModel {
var section: Int = -1 // cell的section索引
var item: Int = -1 // cell的item索引
var name: String = "" // 名称
var isAdded: Bool = false // 是否增加到首页运用(第0区)
var id: String { // 唯一标识,能够用这个来命名图片的名称,也能够用来作判别
get {
"\(section)_\(item)"
}
}
init(){}
}
看得出来,ItemModel的特点section
+ item
= IndexPath
,能够依据 model 知道当时cell的所在方位了。
笔者这用的是 struct ,感觉用 class 会更好点,由于后续会改变数组中Model的特点值。现已写了就懒得再改了。
存在2个数组数据:
-
var editItems = [ItemModel]()
,由前一页传入的、可修改、拖拽的数据,坐落UICollectionView
的第0个Section。 -
var datas = [[ItemModel]]()
,按照Section的次序,寄存所有的数据。
注意:Section要从1开端,由于第0个Section是能够修改拖拽的区域。
datas中寄存悉数的数据:
for i in 0..<names.count {
let subNames = names[i]
var items = [ItemModel]()
for j in 0..<subNames.count {
var model = ItemModel()
model.section = i+1 // 注意这儿的Section要从1开端
model.item = j
model.name = subNames[j]
model.isAdded = editItems.contains(where: { $0.id == model.id})
items.append(model)
}
datas.append(items)
}
依据数据布局UICollectionView
。
移动动画
移动只需2个操作,增加运用和删去运用。
增加
笔者这儿规定了最多能够增加8个运用。
大致思路:
- 获取当时点击的 cell,为了得到其坐标作为动画开端方位
- 在 collectionView 中刺进一个空白的 cell 占位,此举是为了增加或削减行数的动画过渡更自然;对应也应该在 editItems 中增加一个空白的 model 作为数据源,等移动动画完毕后再给model从头赋值。
- 获取新刺进的空白 cell,为了得到其坐标作为动画的完毕方位
- 生成动画的 cell,开端 -> 完毕 动画。
- 更新数据,改写
删去
思路与增加雷同,且比之更简略
具体的思路和过程,代码中都有一步步的注释,可自行查阅。
拖拽排序
这个拖拽排序,在iOS11之前的比较麻烦,都是靠自己计算,这儿也简略说下思路:
iOS11.0之前的完结思路
- 在UICollectionView上增加一个长按的手势
- 在UICollectionView上面增加一个浮动隐藏的cell,便于拖拽
- 经过长按操作找到需求被拖动的cellA
- 经过拖动cellA找到找到和它交流方位的cellB
- 交流cellA和cellB的方位
- 替换数据源,把开端方位的数据模型删去,然后将开端方位的数据模型刺进到拖拽方位
这种比较杂乱的是结合方位判别需求交流的cell。但是在iOS11之后,UICollectionView
新增了dragDelegate、dropDelegate,用来完结拖拽排序的作用。
dragDelegate、dropDelegate
直接上代码:
collectionView.dragDelegate = self
collectionView.dropDelegate = self
collectionView.dragInteractionEnabled = true
collectionView.reorderingCadence = .immediate
collectionView.isSpringLoaded = true
- dragInteractionEnabled 特点要设置为 true,才能够进行 drag 操作。此特点在 iPad 默许是 true,在 iPhone 默许是 false。
- reorderingCadence 重排序节奏,能够调节集合视图重排序的响应性。
- UICollectionViewReorderingCadenceImmediate 默许值。当开端移动的时分就当即回流集合视图布局,实时的从头排序。
- UICollectionViewReorderingCadenceFast 快速移动,不会当即从头布局,只要在中止移动的时分才会从头布局
- UICollectionViewReorderingCadenceSlow 中止移动再过一会儿才会开端回流,从头布局
- isSpringLoaded 弹性加载作用,也能够运用代理办法:
func collectionView(_ collectionView: UICollectionView, shouldSpringLoadItemAt indexPath: IndexPath, with context: UISpringLoadedInteractionContext) -> Bool
。
需求完结UICollectionViewDropDelegate
和UICollectionViewDragDelegate
协议办法。下面是常用的几个办法,按照调用的先后次序说明一下:
/*
* 识别到拖动,一次拖动一个;若一次拖动多个,则需求选中多个
* 供给一个给定 indexPath 的可进行 drag 操作的 item
* NSItemProvider, 拖放处理时,带着数据的容器,经过方针初始化,该方针需满意 NSItemProviderWriting 协议
*/
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem]
/*
* 运用自定义预览,假如该办法没有完结或者返回nil,那么整个 cell 将用于预览
* UIDragPreviewParameters有2个特点:backgroundColor设置背景色彩;visiblePath设置视图的可见区域
* 笔者运用这个办法除去了拖拽过程中item的阴影
*/
func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt indexPath: IndexPath) -> UIDragPreviewParameters?
/*
* 开端拖拽后,持续增加拖拽的使命,处理雷同`itemsForBeginning`办法
*/
func collectionView(_ collectionView: UICollectionView, itemsForAddingTo session: UIDragSession, at indexPath: IndexPath, point: CGPoint) -> [UIDragItem]
/*
* 拖拽开端,可自行处理
*/
func collectionView(_ collectionView: UICollectionView, dragSessionWillBegin session: UIDragSession)
/*
* 判别对应的 item 能否被执行drop会话,是否能放置
*/
func collectionView(_ collectionView: UICollectionView, canHandle session: UIDropSession) -> Bool
/*
* 处理拖动中放置的战略,此办法会 频繁调用,在此办法中应尽可能削减工作量。
* 四种别离:move移动;copy复制;forbidden禁止,即不能放置;cancel用户撤销。
* 作用一般运用2种:.insertAtDestinationIndexPath 挤压移动;.insertIntoDestinationIndexPath 取代。
* 在某些情况下,方针索引途径可能为空(比如拖到一个没有cell的空白区域)你能够经过 session.locationInView 做你自己的命中测验
*/
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal
/*
* 当drop会话进入到 collectionView 的坐标区域内就会调用
*/
func collectionView(_ collectionView: UICollectionView, dropSessionDidEnter session: UIDropSession)
/*
* 完毕放置时的处理
* 假如该办法不做任何事,将会执行默许的动画
*/
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator)
/*
* 拖拽开端,可自行处理
*/
func collectionView(_ collectionView: UICollectionView, dragSessionDidEnd session: UIDragSession)
/*
* 当dropSession 完结时会被调用,不论结果怎么。一般进行整理或改写操作
*/
func collectionView(_ collectionView: UICollectionView, dropSessionDidEnd session: UIDropSession)
这儿大约的拖拽动画就差不多了,代码中会有更具体的注释。
总结
将一个大功用拆分成一个个小模块,按部就班一步一步完结就不难了。这儿只是中间囊括了各种小动画和改写,设计好思路,不行就多试几次必定能够。
代码自取:RCDragDropAnimation
若存在什么不对的地方,欢迎指正!