tableView与collectionView联动功能的实现(左侧大分类,右侧小分类)

protocol TableWithCollectionViewDelegate: NSObjectProtocol {
    func collectionView(didSelectItemAt indexPath: IndexPath)
}

class TableWithCollectionView: UIView {
    weak var delegate: TableWithCollectionViewDelegate?
    var leftModels: [String] = []{
        didSet{
            tableView.reloadData()
            tableView.selectRow(at: IndexPath(row: currentIndex, section: 0), animated: false, scrollPosition: .none)
        }
    }
    var rightModels: [[String]] = []{
        didSet{
            collectionView.reloadData()
        }
    }
    
    
    private var currentIndex: Int = 0 //tableView当前选中的index
    private var sectionInsets: CGFloat = 10~
    private var tableView: BaseTableView!
    private var collectionView: UICollectionView!
    //右侧collectionView当前是否正在向下滚动(即true表示手指向上滑动,查看下面内容)
    private var collectionViewIsScrollDown = true
    //右侧collectionView垂直偏移量
    private var collectionViewLastOffsetY : CGFloat = 0.0
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setUI()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setUI()
    }
}
extension TableWithCollectionView{
    private func setUI(){
        setTableView()
        setCollectionView()
    }
    private func setTableView(){
        tableView = BaseTableView()
        tableView.backgroundColor = .cWhite
        tableView.delegate = self
        tableView.dataSource = self
        tableView.isNoDate = false
        tableView.bounces = false
        tableView.register(LTitleCell.self, forCellReuseIdentifier: LTitleCell.cellID)
        self.addSubview(tableView)
        tableView.snp.makeConstraints { make in
            make.top.leading.bottom.equalToSuperview()
            make.width.equalTo(88~)
        }
    }
    private func setCollectionView(){
        let layout = JJCollectionViewRoundFlowLayout_Swift()
        layout.isCalculateHeader = false
        layout.isCalculateFooter = false
        layout.sectionHeadersPinToVisibleBounds = true
        layout.scrollDirection = .vertical
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.contentInsetAdjustmentBehavior = .never
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.bounces = false
        collectionView.backgroundColor = .bgColor2
        collectionView.showsVerticalScrollIndicator = false
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.register(RCollectionCell.self, forCellWithReuseIdentifier: RCollectionCell.cellID)
        collectionView.register(RCollectionSectionHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: RCollectionSectionHeader.cellID)
        collectionView.register(RCollectionSectionFooter.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: RCollectionSectionFooter.cellID)
        self.addSubview(collectionView)
        collectionView.snp.makeConstraints { make in
            make.top.bottom.equalTo(tableView)
            make.trailing.equalToSuperview().inset(10~)
            make.leading.equalTo(tableView.snp.trailing).offset(10~)
        }
    }
}

//MARK: -UITableView
extension TableWithCollectionView: UITableViewDelegate, UITableViewDataSource{
    //将右侧colletionView的指定分区自动滚动到最顶端
    func collectionViewScrollToTop(section: Int, animated: Bool) {
        let rectY = collectionViewHeaderFrame(section: section)
        let topOfHeader = CGPoint(x: 0, y: rectY - collectionView.contentInset.top)
        collectionView.setContentOffset(topOfHeader, animated: animated)
    }
    //获取colletionView的指定分区头的高度
    func collectionViewHeaderFrame(section: Int) -> CGFloat {
        let indexPath = IndexPath(item: 0, section: section)
        let firstCell = collectionView.collectionViewLayout.layoutAttributesForItem(at: indexPath)
        let attributes = collectionView.collectionViewLayout
            .layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, at: indexPath)
        guard let yForFirstCell = firstCell?.frame.minY else {
            return .zero
        }
        guard let heightForHeader = attributes?.frame.height else {
            return .zero
        }
        return yForFirstCell - heightForHeader - sectionInsets
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        //右侧collection自动滚动到对应的分区
        collectionViewScrollToTop(section: indexPath.row, animated: true)
        currentIndex = indexPath.row
        tableView.reloadData()
        tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        leftModels.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: LTitleCell.cellID, for: indexPath) as!
        LTitleCell
        cell.title = leftModels[indexPath.row]
        return cell
    }
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        let cell = cell as! LTitleCell
        switch indexPath.row {
        case currentIndex - 1:
            cell.bgView.layer.cornerRadius = 5~
            cell.bgView.layer.maskedCorners = CACornerMask(rawValue: CACornerMask.layerMaxXMaxYCorner.rawValue)
        case currentIndex + 1:
            cell.bgView.layer.cornerRadius = 5~
            cell.bgView.layer.maskedCorners = CACornerMask(rawValue: CACornerMask.layerMaxXMinYCorner.rawValue)
        default:
            cell.bgView.layer.cornerRadius = 0
        }
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        50~
    }
    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        UIView()
    }
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        10~
    }
}

//MARK: -UICollectionView
extension TableWithCollectionView: UICollectionViewDelegate, UICollectionViewDataSource, JJCollectionViewDelegateRoundFlowLayout_Swift{
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        delegate?.collectionView(didSelectItemAt: indexPath)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, configModelForSectionAtIndex section: Int) -> JJCollectionViewRoundConfigModel_Swift {
        let model = JJCollectionViewRoundConfigModel_Swift.init()
        model.backgroundColor = UIColor.cWhite
        model.cornerRadius = 10~
        return model
    }
    
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        rightModels.count
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        rightModels[section].count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let item = collectionView.dequeueReusableCell(withReuseIdentifier: RCollectionCell.cellID, for: indexPath) as! RCollectionCell
        item.model = rightModels[indexPath.section][indexPath.item]
        return item
    }
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        if kind == UICollectionView.elementKindSectionHeader {
            let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: RCollectionSectionHeader.cellID, for: indexPath) as! RCollectionSectionHeader
            //设置分组标题
            header.titleLabel.text = leftModels[indexPath.section]
            return header
        }else {
            return collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: RCollectionSectionFooter.cellID, for: indexPath) as! RCollectionSectionFooter
        }
    }
    
    //返回分组头大小
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSize(width: collectionView.bounds.width, height: 40~)
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
        return CGSize(width: collectionView.bounds.width, height: section == collectionView.numberOfSections - 1 ? 10~ : 0.01)
    }
    
    //返回单元格大小
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {
        let itemWidth = (collectionView.bounds.width - 20~) / 3
        let itemHeight = itemWidth / 3 * 4
        return CGSize(width: itemWidth, height: itemHeight)
    }
    
    //每个分组的内边距
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets.init(top: sectionInsets, left: sectionInsets, bottom: sectionInsets, right: sectionInsets)
    }
    
    //单元格的行间距
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 1.0
    }
    
    //单元格横向的最小间距
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0.0
    }
    //分区头即将要显示时调用
    func collectionView(_ collectionView: UICollectionView,
                        willDisplaySupplementaryView view: UICollectionReusableView,
                        forElementKind elementKind: String, at indexPath: IndexPath) {
        //如果是由用户手动滑动屏幕造成的向上滚动,那么左侧表格自动选中该分区对应的分类
        if !collectionViewIsScrollDown
            && (collectionView.isDragging || collectionView.isDecelerating) {
            currentIndex = indexPath.section
            tableView.reloadData()
            tableView.selectRow(at: IndexPath(row: currentIndex, section: 0),
                                animated: true, scrollPosition: .top)
        }
    }
    //分区头即将要消失时调用
    func collectionView(_ collectionView: UICollectionView,
                        didEndDisplayingSupplementaryView view: UICollectionReusableView,
                        forElementOfKind elementKind: String, at indexPath: IndexPath) {
        //如果是由用户手动滑动屏幕造成的向下滚动,那么左侧表格自动选中该分区对应的下一个分区的分类
        if collectionViewIsScrollDown && (collectionView.isDragging || collectionView.isDecelerating) {
            currentIndex = indexPath.section + 1
            tableView.reloadData()
            tableView.selectRow(at: IndexPath(row: currentIndex, section: 0), animated: true, scrollPosition: .top)
        }
    }
    
    //视图滚动时触发(主要用于记录当前collectionView是向上还是向下滚动)
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if collectionView == scrollView {
            collectionViewIsScrollDown = collectionViewLastOffsetY < scrollView.contentOffset.y
            collectionViewLastOffsetY = scrollView.contentOffset.y
        }
    }
}

其中JJCollectionViewDelegateRoundFlowLayout_Swift是用于设置UIcollectionView的section底色,圆角

IMG_3992.PNG
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容