首页 > 其他分享 >IOS 20 发现界面(UITableView)歌单列表(UICollectionView)实现

IOS 20 发现界面(UITableView)歌单列表(UICollectionView)实现

时间:2024-09-05 16:54:25浏览次数:13  
标签:cell 20 collectionView Cell 歌单 let UICollectionView data

发现界面完整效果

本文实现歌单列表效果

文章基于 IOS 19 发现界面(UITableView)快捷按钮实现 继续实现发现界面歌单列表效果

歌单列表Cell实现

实现流程:

1.创建Cell,及在使用UITableView的Controller控制器上注册Cell;

2.获取data列表数据,并调用UITableView的reloadData(),将数据更新到列表;

3.将data的Item数据绑定UITableView的每一个Cell。

1)创建和注册Cell

从效果图上面可以看出,歌单列表Cell由一个title + 宫格列表View来实现的,这里宫格列表使用UICollectionView来实现。需要注意的是,UICollectionView本身也是一个可滑动的控件,把UICollectionView内嵌到UITableView中,且需要显示UICollectionView的全部Item;那么就需要设置UICollectionView不允许滑动,且UICollectionView高度 == Item高度*行数。

通过懒加载创建ItemTitleView 和 UICollectionView,自定义ItemTitleView,就是TGRelativeLayout包含title和右边的icon,并封装UICollectionView到ViewFactoryUtil.collectionView()中。

    /// 标题控件
    lazy var titleView: ItemTitleView = {
        let r = ItemTitleView()
        r.titleView.text = R.string.localizable.recommendSheet()
        return r
    }()
    
    lazy var collectionView: UICollectionView = {
        let r = ViewFactoryUtil.collectionView()
        r.delegate = self
        r.dataSource = self
        r.isScrollEnabled = false
        return r
    }()
//
//  ItemTitleView.swift
//  首页-发现界面-歌单组/推荐单曲组 标题view
//
//  Created by jin on 2024/8/29.
//

import UIKit

import TangramKit

class ItemTitleView : TGRelativeLayout{
    
    init(){
        super.init(frame: CGRect.zero)
        initViews()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        initViews()
    }
    
    func initViews(){
        tg_width.equal(.fill)
        tg_height.equal(.wrap)
        tg_padding = UIEdgeInsets(top: PADDING_MEDDLE, left: PADDING_OUTER, bottom: PADDING_MEDDLE, right: PADDING_OUTER)
        
        addSubview(titleView)
        addSubview(moreIconView)
    }
    
    lazy var titleView: UILabel = {
        let r = UILabel()
        r.tg_width.equal(.wrap)
        r.tg_height.equal(.wrap)
        r.tg_centerY.equal(0)
        r.numberOfLines = 1
        r.font = UIFont.boldSystemFont(ofSize: TEXT_LARGE2)
        r.textColor = .colorOnSurface
        return r
    }()
    
    lazy var moreIconView: UIImageView = {
        let r = UIImageView()
        r.tg_width.equal(15)
        r.tg_height.equal(15)
        r.image = R.image.superChevronRight()?.withTintColor()
        r.tintColor = .black80
        r.tg_right.equal(0)
        r.tg_centerY.equal(0)
        
        //图片完全显示到控件里面
        r.contentMode = .scaleAspectFit
        return r
    }()
}
    /// 创建CollectionView
    static func collectionView() -> UICollectionView {
        let r = UICollectionView(frame: CGRect.zero, collectionViewLayout: collectionViewFlowLayout())
        r.backgroundColor = .clear
        
        //不显示滚动条
        r.showsVerticalScrollIndicator = false
        r.showsHorizontalScrollIndicator = false
        
        //collectionView的内容从collectionView顶部距离开始显示,不要自动偏移状态栏尺寸
        r.contentInsetAdjustmentBehavior = .never
        
        r.tg_width.equal(.fill)
        r.tg_height.equal(.fill)
        
        return r
    }

重写,添加ItemTitleView 和 UICollectionView到SheetGroupCell

class SheetGroupCell:BaseTableViewCell{
    
    static let NAME = "SheetGroupCell"
    
    var datum:Array<Sheet> = []
    
    override func initViews() {
        super.initViews()
        
        //分割线
        container.addSubview(ViewFactoryUtil.smallDivider())
        
        //标题
        container.addSubview(titleView)
        
        container.addSubview(collectionView)
    }

    override func getContainerOrientation() -> TGOrientation {
        return .vert
    }
}

绑定宫格列表数据,动态计算Item宽高。

    func bind(_ data:SheetData) {
        //计算每个cell宽度
        
        //屏幕宽度-外边距16*2-(self.spanCount-1)*5
        cellWidth = (SCREEN_WIDTH-PADDING_OUTER*CGFloat(2) - (spanCount - CGFloat(1))*PADDING_SMALL)/spanCount
        
        //cell高度,5:图片和标题边距,40:2行文字高度
        cellHeight = cellWidth + PADDING_SMALL + 40
        
        //计算可以显示几行
        let rows = ceil(CGFloat(data.datum.count) / spanCount)
        
        //CollectionView高度等于,行数*行高,10:垂直方向每个cell间距
        let viewHeight = rows * (cellHeight + PADDING_MEDDLE)
        
        collectionView.tg_height.equal(viewHeight)
        
        datum.removeAll()
        datum = data.datum
        collectionView.reloadData()
    }

注册SheetGroupCell

class DiscoveryController: BaseLogicController {

    override func initViews() {
        super.initViews()
        setBackgroundColor(.colorBackgroundLight)
        
        //初始化TableView结构
        initTableViewSafeArea()
        
        //注册cell
        tableView.register(BannerCell.self, forCellReuseIdentifier: Constant.CELL)
        tableView.register(ButtonCell.self, forCellReuseIdentifier: ButtonCell.NAME)
        tableView.register(SheetGroupCell.self, forCellReuseIdentifier: SheetGroupCell.NAME)
        
    }
}

2)获取data列表数据

定义宫格列表数据模型SheetData

//
//  SheetData.swift
//  发现界面歌单数据
//
//  Created by jin on 2024/8/29.
//

import Foundation

class SheetData{
    var datum:[Sheet]!
    
    init(_ datum: [Sheet]!) {
        self.datum = datum
    }
}

请求接口获取歌单列表数据,更新tableView.reloadData()

class DiscoveryController: BaseLogicController {

    override func initViews() {
        super.initViews()
        setBackgroundColor(.colorBackgroundLight)
        
        //初始化TableView结构
        initTableViewSafeArea()
        
        //注册cell
        tableView.register(BannerCell.self, forCellReuseIdentifier: Constant.CELL)
        tableView.register(ButtonCell.self, forCellReuseIdentifier: ButtonCell.NAME)
        tableView.register(SheetGroupCell.self, forCellReuseIdentifier: SheetGroupCell.NAME)
    }
    
    override func initDatum() {
        super.initDatum()
        loadData()
    }
    
    func loadData() {
        DefaultRepository.shared.bannerAds().subscribeSuccess { [weak self] data in
            //清除原来的数据
            self?.datum.removeAll()
            
            //添加轮播图
            self?.datum.append(BannerData(data:data.data!.data!))
            
            //添加快捷按钮
            self?.datum.append(ButtonData())
            
            //请求歌单数据
            self?.loadSheetsData()
        }.disposed(by: rx.disposeBag)
    }
    
    /// 请求歌单数据
    func loadSheetsData() {
        DefaultRepository.shared.sheets(size: VALUE12).subscribeSuccess { [weak self] data in
            //添加歌单数据
            self?.datum.append(SheetData(data.data!.data!))
            
            self?.tableView.reloadData()
        }.disposed(by: rx.disposeBag)
    }
}

3)Item数据绑定Cell

DiscoveryController控制器重写父类的扩展 cellForRowAt方法,创建对应的Cell,并将Item数据绑定到Cell。

extension DiscoveryController{
    
    // 返回当前位置cell
    /// - Parameters:
    ///   - tableView: <#tableView description#>
    ///   - indexPath: <#indexPath description#>
    /// - Returns: <#description#>
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let data = datum[indexPath.row]
        
        //获取当前Cell的类型
        let type = typeForItemAtData(data)
        
        switch(type){
        case .button:
            //按钮
            let cell = tableView.dequeueReusableCell(withIdentifier:  ButtonCell.NAME, for: indexPath) as! ButtonCell
            cell.bind(data as! ButtonData)
            return cell
        case .sheet:
            //歌单
            let cell = tableView.dequeueReusableCell(withIdentifier:  SheetGroupCell.NAME, for: indexPath) as! SheetGroupCell
            cell.bind(data as! SheetData)
            return cell
        default:
            //banner
            
            //取出一个Cell
            let cell = tableView.dequeueReusableCell(withIdentifier:  Constant.CELL, for: indexPath) as! BannerCell
            
            //绑定数据
            cell.bind(data as! BannerData)
            
            cell.bannerClick = {[weak self] data in
                    print("bannerClick \(data)")
            }

            return cell
        }
    }
}

歌单UICollectionView Cell实现

由于UICollectionView也是一个列表控件,实现UICollectionView的Cell的流程跟 实现UITableView的Cell的流程基本类似。

实现流程:

1.创建Cell,及在使用UICollectionView的View上注册Cell;

2.获取data列表数据,并调用UICollectionView的reloadData(),将数据更新到列表;

3.将data的Item数据绑定UICollectionView的每一个Cell;

4.重写UICollectionViewDelegateFlowLayout,计算Cell的尺寸。

1)创建和注册Cell

从效果图上面可以看出,歌单列表Item的Cell由一个垂直TGLinearLayout包含UIImageView + UILabel来实现的。布局比较简单,实现代码如下:

//
//  SheetCell.swift
//  歌单cell
//
//  Created by jin on 2024/8/29.
//

import Foundation

class SheetCell : BaseCollectionViewCell{
    
    override func initViews() {
        super.initViews()
        
        container.tg_space = PADDING_SMALL
        
        container.addSubview(iconView)
        container.addSubview(titleView)
    }
    
    func bind(_ data:Sheet) {
        //OC库显示图片
//        if let r = data.icon {
//            let r = ResourceUtil.resourceUri(r)
//            iconView.sd_setImage(with: URL(string: r), placeholderImage: R.image.placeholder())
//        }
        
        iconView.show(data.icon)
        
        titleView.text = data.title
    }
    
    lazy var iconView: UIImageView = {
        let r = UIImageView()
        r.tg_width.equal(.fill)
        r.tg_height.equal(r.tg_width)
        r.image = R.image.placeholder()
        
        //图片从中心等比向外面填充,控件没有黑边,但图片可能被裁剪
        r.contentMode = .scaleAspectFill
        
        //小圆角
        r.smallCorner()
        return r
    }()
    
    /// 标题
    lazy var titleView: UILabel = {
        let r = UILabel()
        r.tg_width.equal(.fill)
        r.tg_height.equal(.wrap)
        r.numberOfLines = 2
        r.font = UIFont.systemFont(ofSize:TEXT_MEDDLE)
        r.textColor = .colorOnSurface
        return r
    }()
}

 2)获取data列表数据

定义宫格列表Item数据模型Sheet

//
//  Sheet.swift
//  歌单对象
//
//  Created by jin on 2024/8/23.
//

import Foundation

//导入JSON解析框架
import HandyJSON

class Sheet : HandyJSON{
    
    /// 歌单标题
    var title:String!

    /// 歌单封面
    var icon:String?

    /// 点击数
    var clicksCount:Int=0

    /// 收藏数
    var collectsCount:Int=0

    /// 评论数
    var commentsCount:Int=0

    /// 音乐数量
    var songsCount:Int=0

    /// 歌单创建者
//    var user:User!
//
//    /// 歌曲列表
//    var songs:Array<Song>?
    
    var detail:String?
    
    required init() {}
}

从SheetGroupCell bind()中获取歌单宫格列表Item数据,更新collectionView.reloadData()

class SheetGroupCell:BaseTableViewCell{
    
    static let NAME = "SheetGroupCell"
    
    var datum:Array<Sheet> = []
    
    var cellWidth:CGFloat!
    var cellHeight:CGFloat!
    var spanCount:CGFloat = 3
    
    override func initViews() {
        super.initViews()
        
        //分割线
        container.addSubview(ViewFactoryUtil.smallDivider())
        
        //标题
        container.addSubview(titleView)
        
        container.addSubview(collectionView)
        
        collectionView.register(SheetCell.self, forCellWithReuseIdentifier: Constant.CELL)
    }
    
    override func getContainerOrientation() -> TGOrientation {
        return .vert
    }
    
    func bind(_ data:SheetData) {
        //计算每个cell宽度
        
        //屏幕宽度-外边距16*2-(self.spanCount-1)*5
        cellWidth = (SCREEN_WIDTH-PADDING_OUTER*CGFloat(2) - (spanCount - CGFloat(1))*PADDING_SMALL)/spanCount
        
        //cell高度,5:图片和标题边距,40:2行文字高度
        cellHeight = cellWidth + PADDING_SMALL + 40
        
        //计算可以显示几行
        let rows = ceil(CGFloat(data.datum.count) / spanCount)
        
        //CollectionView高度等于,行数*行高,10:垂直方向每个cell间距
        let viewHeight = rows * (cellHeight + PADDING_MEDDLE)
        
        collectionView.tg_height.equal(viewHeight)
        
        datum.removeAll()
        datum = data.datum
        collectionView.reloadData()
    }
}

3)Item数据绑定Cell

SheetGroupCell 重写父类的扩展 cellForItemAt方法,创建对应的Cell,并将Item数据绑定到Cell。

/// CollectionView数据源和代理
extension SheetGroupCell:UICollectionViewDataSource,UICollectionViewDelegate{
    
    /// 有多少个
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return datum.count
    }
    
    /// 返回cell
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let data = datum[indexPath.row]
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Constant.CELL, for: indexPath) as! SheetCell
        cell.bind(data)
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if let d = delegate {
            d.sheetCLick(data: datum[indexPath.row])
        }
    }
}

4)计算Cell尺寸

重写UICollectionViewDelegateFlowLayout,返回Cell的尺寸

/// UICollectionViewDelegateFlowLayout
extension SheetGroupCell:UICollectionViewDelegateFlowLayout{
    /// 返回CollectionView里面的Cell到CollectionView的间距
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 0, left: PADDING_OUTER, bottom: PADDING_OUTER, right: PADDING_OUTER)
    }
    
    /// 返回每个Cell的行间距
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return PADDING_MEDDLE
    }
    
    /// 返回每个Cell的列间距
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return PADDING_SMALL
    }
    
    /// cell尺寸
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: cellWidth, height: cellHeight)
    }
}

至此完成歌单列表的实现。

标签:cell,20,collectionView,Cell,歌单,let,UICollectionView,data
From: https://blog.csdn.net/sziitjin/article/details/141862282

相关文章

  • 发现一个 grafana 的神奇bug (版本:Grafana v7.5.3 (42f2079))
    某个api的延迟始终显示不出来,研究了很久的promql表达式的计算细节,也没找到原因。面板展示如下:但是当我选择另一个api的延迟曲线的时候,又对了:如果我选择展示所有曲线,仍然显示不出来:可见:是某条曲线的数据可能有某种问题,导致所有曲线都无法展示。应该是个纯前端ui的问......
  • Overloud Gem Bundle Complete 2024 是一套出色的音频插件
    OverloudGemBundleComplete2024是一套出色的音频插件,专为专业音频工程师、音乐制作人和爱好者设计。它精确模拟了古老的模拟电路,提供了丰富、温暖和逼真的声音,适合高质量音频创作。该套件还加入了Dopamine、ECHOSON、Mod、Modula、OTD-2和Sculptube等增强功能,扩展了音乐......
  • 裁员的风吹到了2024,软件测试行业还有前景吗?
    前一段时间IBM中国"灭霸式"裁员引爆互联网,前后仅花费了三分钟时间,就裁撤员工超过1000人。这次被裁的主要是研发及测试岗位,涉及部门是IBM中国开发中心(CDL)和IBM中国系统中心(CSL)。而这位现任IBM印度裔CEO此前已经在全球范围内进行了多轮裁员。IBM中国研发部门的关闭并非孤立事件......
  • 月老网相亲:2024年下半年结婚领证黄道吉日
    不知不觉间,2024年过了一半过身了,各位单身的小伙伴要加油脱单呀,找到对象的速度求婚结婚,在余下的2024年下半年,有哪些结婚领证的黄道吉日呢?‌2024年下半年适合领结婚证的黄道吉日有很多,具体如下‌: ‌7月‌:共有14天适合领证,其中6天为周末或节假日;‌8月‌:共有13天适合领证,其中......
  • 2024.08.03科大讯飞飞凡计划(简单)
    1.交换树节点给定一棵树,每个节点有一个权值。现在每次可以交换任意两个节点的权值,请问最少多少次交换可以使得每个节点的权值等于它的编号?保证给出的权值是一个排列,也就是说保证一定有解。不用考虑树的关系,因为不是相邻交换```intmain(){intn;cin>>n;v......
  • 2024 年 5 亿大模型新应用井喷即将到来,算力问题该如何解决?2024 年 5 亿大模型新应用井
    在科技飞速发展的2024年,我们正站在大模型应用爆发的前沿。预计将有5亿大模型新应用如雨后春笋般涌现,为各行各业带来前所未有的变革。然而,在这令人兴奋的前景背后,算力问题却成为了制约其发展的关键瓶颈。大模型的训练和运行需要巨大的计算资源,其对算力的需求呈指数级增长。传统......
  • 2024.09 别急记录
    1.ARC070F-HonestOrUnkind发现\(a\leqb\)时\(b\)内部可以构造出一个好人集合,一定无解;否则有两种情况:\(x\)认为\(y\)是坏人,二者一好一坏,全部删去即可;\(x\)认为\(y\)是好人,那么\(y\)一定比\(x\)更好。维护一个栈表示目前越来越好的人,每次取出栈顶询问新人......
  • 2024年最强网络安全学习路线,详细到直接上清华的教材!
        关键词:网络安全入门、渗透测试学习、零基础学安全、网络安全学习路线首先咱们聊聊,学习网络安全方向通常会有哪些问题前排提示:文末有CSDN官方认证Python入门资料包!1、打基础时间太长学基础花费很长时间,光语言都有几门,有些人会倒在学习linux系统及命令的路上,更多......
  • 2024届毕设(智能纸巾盒控制系统)(Linux)
    设计背景        其实我大二的时候就想做一个这样的产品了,因为在食堂吃饭的时候老是忘记带纸巾,主要是懒得带,而且感觉塞那么一小包在裤兜里老是有点不自在。老是蹭蹭舍友的,笑死!        就在不久后,食堂里面有些摊位就开始配纸巾了,但是我靠,我爱吃的鸡排饭那家没......
  • 20240904_182638 mysql 填空题 变量
    查看所有的系统变量名称showvariables查看所有系统变量中以auto开头的名称showvariableslike'auto%'查看系统变量autocommit的值select@@autocommit设置系统变量autocommit的值为1setautocommit=1设置自定义变量name的值为'tom'set@name='tom'查看自定义变量nam......