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

devtools/2024/11/15 0:48:31/

发现界面完整效果

本文实现歌单列表效果

文章基于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.ppmy.cn/devtools/108969.html

相关文章

STM32 外部中断(EXTI)

STM32 外部中断(EXTI) 实验&#xff1a;配置一个引脚的下降沿作为外部中断。 参考&#xff1a;江协科技 相关缩写 RCC(Reset and Clock Control) 复位和时钟控制 GPIO(General Purpose Input/Output) 通用输入/输出 AFIO(Alternate Function Input Output) 复用功能输入输…

ArcGIS Pro SDK (十二)布局 9 布局元数据与图框

ArcGIS Pro SDK (十二)布局 9 布局元数据与图框 文章目录 ArcGIS Pro SDK (十二)布局 9 布局元数据与图框1 布局元数据1.1 布局元数据2 布局地图框2.1 更改与地图框关联的地图2.2 更改地图框照相机设置2.3 将地图框缩放到单个图层的范围2.4 将地图框范围更改为多个图层中的…

JS中正则表达式捕获组与反向引用详解

正则表达式&#xff08;Regular Expression&#xff0c;简称 Regex&#xff09;是一种强大的字符串匹配工具&#xff0c;它能够让我们通过模式来查找、匹配、替换字符串中的内容。而在正则表达式中&#xff0c;捕获组是一个非常重要的概念&#xff0c;常常用于将匹配到的内容保…

基于yolov8的红绿灯目标检测训练与Streamlit部署(代码+教程)

项目背景 随着智能交通系统的快速发展&#xff0c;自动驾驶技术逐渐成为研究的热点。在自动驾驶领域中&#xff0c;准确识别道路上的交通信号灯是确保车辆安全行驶的关键技术之一。近年来&#xff0c;深度学习技术的发展为交通信号灯的识别提供了强大的支持。YOLO&#xff08;…

Why I‘m getting 404 Resource Not Found to my newly Azure OpenAI deployment?

题意&#xff1a;为什么我新部署的Azure OpenAI服务会出现404资源未找到的错误&#xff1f; 问题背景&#xff1a; Ive gone through this quickstart and I created my Azure OpenAI resource created a model deployment which is in state succeedded. I also playaround …

Win32绕过UAC弹窗获取管理员权限

在早些年写一些桌面软件时&#xff0c;需要管理员权限&#xff0c;但是又不想UAC弹窗&#xff0c;所以一般是直接将UAC的级别拉到最低&#xff0c;或者直接禁用UAC的相关功能。 什么是UAC(User Account Control) 用户帐户控制 (UAC) 是一项 Windows 安全功能&#xff0c;旨在保…

C#发送正文带图片带附件的邮件

1&#xff0c;开启服务&#xff0c;获取授权码。以QQ邮箱为例&#xff1a; 点击管理服务&#xff0c;进入账号与安全页面 2&#xff0c;相关设置参数&#xff0c;以QQ邮箱为例&#xff1a; 登录时&#xff0c;请在第三方客户端的密码输入框里面填入授权码进行验证。&#xff0…

Java 面试题:从源码理解 ThreadLocal 如何解决内存泄漏 ConcurrentHashMap 如何保证并发安全 --xunznux

文章目录 ThreadLocalThreadLocal 的基本原理ThreadLocal 的实现细节内存泄漏源码使用场景 ConcurrentHashMap 怎么实现线程安全的CAS初始化源码添加元素putVal方法 ThreadLocal ThreadLocal 是 Java 中的一种用于在多线程环境下存储线程局部变量的机制&#xff0c;它可以为每…