IOS 21 发现界面(UITableView)单曲列表(UITableView)实现

发现界面完整效果

本文实现歌单列表效果

文章基于IOS 20 发现界面(UITableView)歌单列表(UICollectionView)实现 继续实现发现界面单曲列表效果

单曲列表Cell实现

实现流程:

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

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

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

1)创建和注册Cell

从效果图上面可以看出,单曲列表Cell由一个title + UITableView来实现的,如果看了前面的文章UITableView应该已经很熟悉了,这里需要注意的是,把UITableView内嵌到UITableView中,且需要显示UITableView的全部Item;那么就需要设置内嵌的UITableView高度 == 行数*Item行高。

下面通过懒加载创建ItemTitleView 和 UITableView,自定义ItemTitleView,就是TGRelativeLayout包含title和右边的icon。

    /// 标题控件lazy var titleView: ItemTitleView = {let r = ItemTitleView()r.titleView.text = R.string.localizable.recommendSong()return r}()lazy var tableView: UITableView = {let result=ViewFactoryUtil.tableView()result.separatorStyle = .singleLine//分割线颜色result.separatorColor = .colorDividerresult.delegate = selfresult.dataSource = self//注册cellresult.register(SongCell.self, forCellReuseIdentifier: Constant.CELL)return result}()
//
//  ItemTitleView.swift
//  首页-发现界面-歌单组/推荐单曲组 标题view
//
//  Created by jin on 2024/8/29.
//import UIKitimport TangramKitclass 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 = 1r.font = UIFont.boldSystemFont(ofSize: TEXT_LARGE2)r.textColor = .colorOnSurfacereturn 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 = .black80r.tg_right.equal(0)r.tg_centerY.equal(0)//图片完全显示到控件里面r.contentMode = .scaleAspectFitreturn r}()
}

重写,添加ItemTitleView 和 UITableView到SongGroupCell

class SongGroupCell:BaseTableViewCell{static let NAME = "SongGroupCell"var datum:Array<Song> = []//发现界面每个单曲cell高度,51:图片高度,10*2:上下两个边距static let HEIGHT_DISCOVERY_SONG:CGFloat = 51+10*2override func initViews() {super.initViews()//分割线container.addSubview(ViewFactoryUtil.smallDivider())//标题container.addSubview(titleView)container.addSubview(tableView)}override func getContainerOrientation() -> TGOrientation {return .vert}
}

绑定列表数据,动态计算内嵌的UITableView的高。

    func bind(_ data:SongData){datum.removeAll()datum = data.datum//高度等于,行数*行高let viewHeight = CGFloat(data.datum.count) * SongGroupCell.HEIGHT_DISCOVERY_SONGtableView.tg_height.equal(viewHeight)tableView.reloadData()}

注册SongGroupCell

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

2)获取data列表数据

定义列表数据模型SongData

//
//  SongData.swift
//  发现界面音乐数据
//
//  Created by jin on 2024/9/2.
//import Foundationclass SongData{var datum:[Song]!init(_ datum: [Song]!) {self.datum = datum}
}

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

class DiscoveryController: BaseLogicController {override func initViews() {super.initViews()setBackgroundColor(.colorBackgroundLight)//初始化TableView结构initTableViewSafeArea()//注册celltableView.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?.loadSongsData()}.disposed(by: rx.disposeBag)}/// 请求音乐数据func loadSongsData() {DefaultRepository.shared.songs().subscribeSuccess { [weak self] data inself?.endRefresh()//添加音乐数据self?.datum.append(SongData(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! ButtonCellcell.bind(data as! ButtonData)return cellcase .sheet://歌单let cell = tableView.dequeueReusableCell(withIdentifier:  SheetGroupCell.NAME, for: indexPath) as! SheetGroupCellcell.bind(data as! SheetData)return cellcase .song://音乐let cell = tableView.dequeueReusableCell(withIdentifier:  SongGroupCell.NAME, for: indexPath) as! SongGroupCellcell.bind(data as! SongData)return celldefault://banner//取出一个Celllet cell = tableView.dequeueReusableCell(withIdentifier:  Constant.CELL, for: indexPath) as! BannerCell//绑定数据cell.bind(data as! BannerData)cell.bannerClick = {[weak self] data inprint("bannerClick \(data)")}return cell}}
}

单曲UITableView Cell实现

实现流程:

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

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

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

1)创建和注册Cell

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

//
//  SongCell.swift
//  发现界面单曲cell
//
//  Created by jin on 2024/9/2.
//import UIKit
import TangramKitclass SongCell:BaseTableViewCell{static let NAME = "SongCell"override func initViews() {super.initViews()container.tg_space = PADDING_MEDDLEcontainer.tg_gravity = TGGravity.vert.centercontainer.addSubview(iconView)container.addSubview(rightContainer)rightContainer.addSubview(titleView)rightContainer.addSubview(infoView)}func bind(_ data:Song) {//TODO Bug用这个框架显示,会导致Item高度不正确,暂时还不知道具体是什么问题
//        iconView.show(data.icon)iconView.sd_setImage(with: URL(string: data.icon!.absoluteUri()), placeholderImage: R.image.placeholder())titleView.text = data.title//专辑和歌单差不多,这里就不在实现了infoView.text = "\(data.singer.nickname!)-这是专辑名称"}lazy var iconView: UIImageView = {let result=UIImageView()result.tg_width.equal(51)result.tg_height.equal(51)result.image = R.image.dayRecommend()result.clipsToBounds=trueresult.contentMode = .scaleAspectFillresult.smallCorner()return result}()lazy var rightContainer: TGLinearLayout = {let result=TGLinearLayout(.vert)result.tg_width.equal(.fill)result.tg_height.equal(.wrap)result.tg_space = PADDING_SMALLreturn result}()/// 标题控件lazy var titleView: UILabel = {let result=UILabel()result.tg_width.equal(.fill)result.tg_height.equal(.wrap)result.numberOfLines = 2result.font = UIFont.systemFont(ofSize: 14)result.textColor = .colorOnSurfacereturn result}()lazy var infoView: UILabel = {let result=UILabel()result.tg_width.equal(.fill)result.tg_height.equal(.wrap)result.font = UIFont.systemFont(ofSize: 12)result.textColor = .black80return result}()
}

 2)获取data列表数据

定义列表Item数据模型Song

//
//  Song.swift
//  音乐对象
//
//  Created by jin on 2024/9/2.
//import Foundation//导入JSON解析框架
import HandyJSONclass Song : BaseCommon{// 标题var title:String!/// 封面var icon:String?/// 音乐地址var uri:String!/// 点击数var clicksCount:Int = 0/// 评论数var commentsCount:Int = 0/// 创建该音乐的人var user:User!/// 歌手var singer:User!override func mapping(mapper: HelpingMapper) {super.mapping(mapper: mapper)mapper <<< self.clicksCount <-- "clicks_count"mapper <<< self.commentsCount <-- "comments_count"}
}

从SongGroupCell bind()中获取单曲列表Item数据,更新tableView.reloadData()

class SongGroupCell:BaseTableViewCell{static let NAME = "SongGroupCell"var datum:Array<Song> = []//发现界面每个单曲cell高度,51:图片高度,10*2:上下两个边距static let HEIGHT_DISCOVERY_SONG:CGFloat = 51+10*2override func initViews() {super.initViews()//分割线container.addSubview(ViewFactoryUtil.smallDivider())//标题container.addSubview(titleView)container.addSubview(tableView)}override func getContainerOrientation() -> TGOrientation {return .vert}func bind(_ data:SongData){datum.removeAll()datum = data.datum//高度等于,行数*行高let viewHeight = CGFloat(data.datum.count) * SongGroupCell.HEIGHT_DISCOVERY_SONGtableView.tg_height.equal(viewHeight)tableView.reloadData()}
}

3)Item数据绑定Cell

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

/// 数据源和代理
extension SongGroupCell:QMUITableViewDelegate,QMUITableViewDataSource{/// 有多少个func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return datum.count}/// 返回cellfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {let data = datum[indexPath.row]let cell = tableView.dequeueReusableCell(withIdentifier: Constant.CELL, for: indexPath) as! SongCellif indexPath.row == 0 {cell.container.tg_padding = UIEdgeInsets(top: 0, left: PADDING_OUTER, bottom: PADDING_MEDDLE, right: PADDING_OUTER)} else {cell.container.tg_padding = UIEdgeInsets(top: PADDING_MEDDLE, left: PADDING_OUTER, bottom: PADDING_MEDDLE, right: PADDING_OUTER)}cell.bind(data)return cell}/// 点击了cellfunc tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {SwiftEventBus.post(Constant.EVENT_SONG_CLICK, sender: datum[indexPath.row])}
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1523393.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

如何使用 Mistral 和 Llama2 构建 AI 聊天机器人

开始使用 Mistral 让我们从 Mistral 7B Instruct 的 GGUF 量化版本开始&#xff0c;并使用 AutoClasses ‘AutoModelForCausalLM’ 之一来加载模型。AutoClasses 可以帮助我们自动检索给定模型路径的模型。AudoModelForCausalLM 是具有因果语言建模的模型类之一&#xff0c;这…

【STM32+HAL库】---- 驱动DHT11温湿度传感器

硬件开发板&#xff1a;STM32F407VET6 软件平台&#xff1a;cubemaxkeilVScode1 DHT11工作原理 1.1 简介 DHT11温湿度传感器是一种数字式温湿度传感器&#xff0c;其工作原理基于集成了湿度感测元件和NTC温度感测元件的传感器模块。以下是DHT11温湿度传感器的工作原理&#x…

SQL - SQL优化

在sql查询中为了提高查询效率&#xff0c;我们常常会采取一些措施对查询语句进行sql优化&#xff0c;下面总结的一些方法&#xff0c;有需要的可以参考参考 一、查询SQL尽量不要使用select *&#xff0c;而是具体字段 // 建议 SELECT id,user_name,age,tel FROM user// 不建议…

同城便民信息生活小程序源码系统 求职招聘+房产出租+相亲交友 带完整的安装代码包以及搭建部署教程

系统概述 同城便民信息生活小程序源码系统是一款专为满足城市居民多元化需求而设计的综合性服务平台。该系统通过整合求职招聘、房产出租、相亲交友等核心功能模块&#xff0c;旨在打造一个集信息发布、查询、交流于一体的闭环生态系统。用户可以在小程序内轻松发布或浏览各类…

【STM32+HAL库】---- 驱动MAX30102心率血氧传感器

硬件开发板&#xff1a;STM32F407VET6 软件平台&#xff1a;cubemaxkeilVScode1 MAX30102心率血氧传感器工作原理 MAX30102传感器是一种集成了红外光源、光电检测器和信号处理电路的高度集成传感器&#xff0c;主要用于心率和血氧饱和度的测量。以下是MAX30102传感器的主要特点…

使用光敏电阻设计照度计

照度计是一种使用 SI 单位勒克斯测量照度和光发射度的设备。它有效地测量落在给定面积单位上的光的功率量&#xff0c;不同之处在于功率测量被加权以反映人眼对不同波长的光的敏感度。描述照度计的一种更简单的方法是&#xff0c;它测量落在传感器上的光的亮度。市售照度计的价…

使用PyTorch从零构建Llama 3

我们上次发了用PyTorch从零开始编写DeepSeek-V2的文章后&#xff0c;有小伙伴留言说希望介绍一下Llama 3。那么今天他就来了&#xff0c;本文将详细指导如何从零开始构建完整的Llama 3模型架构&#xff0c;并在自定义数据集上执行训练和推理。 [图1]&#xff1a;Llama 3架构展示…

Linux/Ubuntu服务器 screen 安装与使用

一、screen简单介绍 在Linux系统中&#xff0c;screen是一个非常强大的终端仿真器&#xff0c;它允许用户在一个终端窗口中创建多个子窗口&#xff0c;每个子窗口都可以运行一个独立的会话。screen的主要特点包括&#xff1a; 会话分离&#xff1a;screen允许用户在终端会话中运…

宝宝护眼灯哪个牌子好?2024年热门宝宝护眼灯款式推荐

宝宝护眼灯哪个牌子好&#xff1f;在日常生活的点点滴滴中&#xff0c;适宜的灯光扮演着至关重要的角色&#xff0c;无论是学习还是办公等环境&#xff0c;皆需要恰当的照明。为此&#xff0c;人们通常会备上一款台灯&#xff0c;特别是对于长期与电脑为伴的设计师、影像绘图专…

爆改YOLOv8|利用yolov10的C2fCIB改进yolov8-高效涨点

1&#xff0c;本文介绍 本文介绍了一种改进机制&#xff0c;通过引入 YOLOv10 的 C2fCIB 模块来提升 YOLOv8 的性能。C2fCIB 模块中的 CIB&#xff08;Compact Inverted Bottleneck&#xff09;结构采用了高效的深度卷积进行空间特征混合&#xff0c;并使用点卷积进行通道特征…

【unity知识】Animator动画状态的基本属性介绍

文章目录 动画状态的基本属性1、标签Tag2、Motion 该状态所管理的动画片段3、speed 动画的播放速度4、Motion Time 播放动画片段定在一个特定时间点5、Mirror镜像动画6、CycleOffset动画偏移7、FootIK8、Write Defaults 参考完结 动画状态的基本属性 1、标签Tag 通过打标签我们…

AI大模型时代,产品经理需要了解什么?

在移动互联网高速发展的时代&#xff0c;产品经理一度成为最火爆的职业&#xff0c;人人都想当产品经理&#xff0c;有很多人说&#xff1a;产品经理的上限极高&#xff0c;它应该是CEO式的岗位。事实上&#xff0c;我们看到新型互联网科技公司的CEO也确实都是产品出身。但是这…

数据库审计是什么?主要用在哪些场景呢?

数据库审计是什么&#xff1f;主要用在哪些场景呢&#xff1f; 数据库审计 数据库审计是指对数据库系统中的操作进行记录、监控和分析的过程&#xff0c;用于检查和评估数据库的安全性、合规性和完整性。数据库审计可以为组织提供重要的安全保障和合规性需求的满足。本文将介…

重置vCenter Server的root密码

文章目录 重置vCenter Server的root密码一、vCenter Server 6.7之前的版本步骤&#xff1a; 二、vCenter Server 7.0及之后版本步骤&#xff1a; 注意事项 重置vCenter Server的root密码 在虚拟化环境中&#xff0c;VMware vCenter Server扮演着核心管理角色的重任。然而&…

前端请求的路径baseURL怎么来的 ?nodejs解决cors问题的一种方法

背景&#xff1a;后端使用node.js搭建&#xff0c;用的是express 前端请求的路径baseURL怎么来的 &#xff1f; 前后端都在同一台电脑上运行&#xff0c;后端的域名就是localhost&#xff0c;如果使用的是http协议&#xff0c;后端监听的端口号为3000&#xff0c;那么前端请求…

视频合并在线工具哪个好?好用的视频合并工具推荐

当我们手握一堆零散却各有千秋的视频片段时&#xff0c;是否曾幻想过它们能像魔法般合并成一部完整、流畅的故事&#xff1f; 别担心&#xff0c;今天咱们就来一场“视频合并大冒险”&#xff0c;揭秘几款视频合并软件手机免费工具&#xff0c;帮助你在指尖上实现创意无限的视…

每日一题 背包,dp,兵营力量训练

首先&#xff0c;读完这题我一开始有点懵&#xff0c;分析了条件后还是不知道怎么分配比较完美&#xff0c;一开始想一直给最小的那个分配呗&#xff0c;但这不知道分配的力量是多少&#xff0c;没有一个界线&#xff0c;所以要找一个界线&#xff0c;最后还是看了别人的参考答…

数据首发!高阶ADAS摄像头搭载量同比增超80%,11V占据主流

高工智能汽车研究院:高阶ADAS摄像头搭载量同比增长超80%&#xff0c;11V占据主流 随着高阶新车智驾的加速落地&#xff0c;也带动核心ADAS摄像头搭载量爆发式增长 高工智能汽车研究院监测数据显示&#xff0c;今年1-6月中国市场(不含进出口)乘用车前装标配NOA(含硬件标配)搭载…

【C++】vector类:模拟实现(适合新手手撕vector)

在实现本文的vector模拟前&#xff0c;建议先了解关于vector的必要知识&#xff1a;【C】容器vector常用接口详解-CSDN博客https://blog.csdn.net/2301_80555259/article/details/141529230?spm1001.2014.3001.5501 目录 一.基本结构 二.构造函数&#xff08;constructor&…

elementUI根据列表id进行列合并@莫成尘

本文章提供了elementUI根据列表id进行列合并的demo&#xff0c;效果如图&#xff08;可直接复制代码粘贴&#xff09; <template><div id"app"><el-table border :data"tableList" style"width: 100%" :span-method"objectS…