美文网首页收藏swiftSwift学习
Swift 仿头条频道管理(UICollectionView,拖

Swift 仿头条频道管理(UICollectionView,拖

作者: Crazy_ba22 | 来源:发表于2018-05-10 14:25 被阅读0次

    前言

    初步实现了一个仿今日头条的频道管理,能够进行拖拽排序,效果图如下

    image

    分析

    主要使用UICollectionView实现,利用其原生的API实现拖拽效果。

    核心分为以下步骤:

    • 得到获取焦点的Cell

    • 处理移动中的事件

    • 移动结束后,处理放下Cell问题

    创建UICollectionView之前先创建个UICollectionViewFlowLayout,我们定义一行最多显示4个Cell,单个Cell之前的间距是10,高度为40

    
    let width: CGFloat = (self.view.frame.width -  5 * 10) / 4
    
    let height: CGFloat = 40
    
    

    定义UICollectionViewFlowLayout

    
    let flowLayout = UICollectionViewFlowLayout()
    
    //滚动方向
    
    flowLayout.scrollDirection = .vertical
    
    //网格中各行项目之间使用的最小间距
    
    flowLayout.minimumLineSpacing = 10
    
    //在同一行中的项目之间使用的最小间距
    
    flowLayout.minimumInteritemSpacing = 10
    
    //用于单元格的默认大小
    
    flowLayout.itemSize = CGSize.init(width: width, height: height)
    
    //用于标题的默认大小
    
    flowLayout.headerReferenceSize = CGSize.init(width: self.view.frame.width, height: 50)
    
    

    headerReferenceSize 用于定义头部的大小(IndexPath.section对应的部分)

    创建UICollectionView,UICollectionView需要register两个,一个是普通视图的Cell,一个是头部对应的Cell

    
    myCollectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
    
    myCollectionView.register(UINib.init(nibName: "EditCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: EditCollectionViewCell.forCellReuseIdentifier)
    
    myCollectionView.register(UINib(nibName: "HeaderCollectionReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HeaderCollectionReusableView.forCellReuseIdentifier)
    
    

    EditCollectionViewCell 为频道的Cell(如:“关注、特产、健康、房产等”),HeaderCollectionReusableView 为头部的Cell (如:“我的频道、频道推荐”)

    需要实现的代理

    
    myCollectionView.delegate = self
    
    myCollectionView.dataSource = self
    
    myCollectionView.dragDelegate = self
    
    myCollectionView.dropDelegate = self
    
    

    delegate 管理集合视图中项目显示和选择

    dataSource 提供数据源

    dragDelegate 管理拖曳交互

    dropDelegate 管理丢弃交互

    重点就是dragDelegate和dropDelegate

    提供的数据源如下

    
    func initData() {
    
            itemHeaders = ["我的频道","频道推荐"]
    
            itemNames = [0: [String](["关注","推荐","视频","热点","北京","新时代","图片","头条号","娱乐","问答","体育","科技","懂车帝","财经","军事","国际"]),1: [String](["健康","冬奥","特产","房产","小说","时尚","历史","育儿","直播","搞笑","数码","美食","养生","电影","手机","旅游","宠物","情感"])]
    
        }
    
    

    开启UICollectionView响应拖拽操作

    
    myCollectionView.dragInteractionEnabled = true
    
    

    以上是基本的操作流程,下面重点讲下,上边提到的三个步骤


    得到获取焦点的Cell

    需要实现UICollectionViewDragDelegate代理,并提供UIDragItem

    
    func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
    
            guard indexPath.section != 1 else {
    
                return []
    
            }
    
            let item = self.itemNames[indexPath.section]![indexPath.row]
    
            let itemProvider = NSItemProvider(object: item as NSString)
    
            let dragItem = UIDragItem(itemProvider: itemProvider)
    
            dragItem.localObject = item
    
            dragingIndexPath = indexPath
    
            return [dragItem]
    
        }
    
    

    由于我们规定,“我的频道”可以拖拽,“频道推荐”不可以,所以 indexPath.section 为1 的部分,返回一个空数组,表示不响应事件。

    通过section和row 获取到对象的值,并创建NSItemProvider返回。

    NSItemProvider:拖放活动期间在进程之间共享的数据或文件,初始化的object要是NSObject, NSCopying的子类,如:NSItemProvider(object: item as NSString),是把String作为共享数据了。

    处理移动中的事件

    这个我们需要实现UICollectionViewDropDelegate代理,并提供UICollectionViewDropProposal

    
    func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
    
            guard dragingIndexPath?.section == destinationIndexPath?.section else {
    
                return UICollectionViewDropProposal(operation: .forbidden)
    
            }
    
            if session.localDragSession != nil {
    
                if collectionView.hasActiveDrag {
    
                    return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
    
                } else {
    
                    return UICollectionViewDropProposal(operation: .copy, intent: .insertAtDestinationIndexPath)
    
                }
    
            } else {
    
                return UICollectionViewDropProposal(operation: .forbidden)
    
            }
    
        }
    
    

    由于“我的频道”和“频道推荐”是禁止互相滑动的,所以,拖拽的起始dragingIndexPath和目标destinationIndexPath的section不一样,就表示跨区了,设置其为forbidden。

    移动结束后,处理放下Cell问题

    这是最后一步,需要实现UICollectionViewDropDelegate代理,通过coordinator我们可以获取到操作类型,是move还是copy。

    
    func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
    
            guard let destinationIndexPath = coordinator.destinationIndexPath else {
    
                return
    
            }
    
            switch coordinator.proposal.operation {
    
            case .move:
    
                break
    
            case .copy:
    
                break
    
            default:
    
                return
    
            }
    
        }
    
    

    move 操作后,需要把之前的位置删除掉,在新的位置进行插入

    
    self.itemNames[destinationIndexPath.section]!.remove(at: sourceIndexPath.row)
    
    self.itemNames[destinationIndexPath.section]!.insert(item.dragItem.localObject as! String, at: destinationIndexPath.row)
    
    collectionView.deleteItems(at: [sourceIndexPath])
    
    collectionView.insertItems(at: [destinationIndexPath])
    
    

    要先对数据源进行移除和添加操作,然后在视图进行更新,否则会崩溃

    copy 操作后,需要在新的位置进行插入

    
    let indexPath = IndexPath(row: destinationIndexPath.row, section: destinationIndexPath.section)
    
    self.itemNames[destinationIndexPath.section]!.insert(item.dragItem.localObject as! String, at: indexPath.row)
    
    collectionView.insertItems(at: indexPath)
    
    

    以上就是全部的分析流程,还有一种实现思路是:在collectionView上添加一个自定义的View,覆盖在Cell之上,获取手势事件后,根据手势滑动,动态更改自定义的View的位置,同样可以实现以上效果。

    Git: https://github.com/LSnumber1/CustomUICollection

    相关文章

      网友评论

        本文标题:Swift 仿头条频道管理(UICollectionView,拖

        本文链接:https://www.haomeiwen.com/subject/yfizrftx.html