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

news2024/11/16 1:47:01

发现界面完整效果

本文实现歌单列表效果

文章基于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 = .colorDivider
        result.delegate = self
        result.dataSource = self
        
        //注册cell
        result.register(SongCell.self, forCellReuseIdentifier: Constant.CELL)
        
        return result
    }()
//
//  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
    }()
}

重写,添加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*2
    
    override 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_SONG
        tableView.tg_height.equal(viewHeight)
        
        tableView.reloadData()
    }

注册SongGroupCell

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)
        tableView.register(SongGroupCell.self, forCellReuseIdentifier: SongGroupCell.NAME)
    }
}

2)获取data列表数据

定义列表数据模型SongData

//
//  SongData.swift
//  发现界面音乐数据
//
//  Created by jin on 2024/9/2.
//

import Foundation

class SongData{
    var datum:[Song]!
    
    init(_ datum: [Song]!) {
        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?.loadSongsData()
        }.disposed(by: rx.disposeBag)
    }
    
    /// 请求音乐数据
    func loadSongsData() {
        DefaultRepository.shared.songs().subscribeSuccess { [weak self] data in
            self?.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! 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
        case .song:
            //音乐
            let cell = tableView.dequeueReusableCell(withIdentifier:  SongGroupCell.NAME, for: indexPath) as! SongGroupCell
            cell.bind(data as! SongData)
            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
        }
    }
}

单曲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 TangramKit

class SongCell:BaseTableViewCell{
    
    static let NAME = "SongCell"
    
    override func initViews() {
        super.initViews()
        
        container.tg_space = PADDING_MEDDLE
        container.tg_gravity = TGGravity.vert.center
        
        container.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=true
        
        result.contentMode = .scaleAspectFill
        
        result.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_SMALL
        return result
    }()
    
    /// 标题控件
    lazy var titleView: UILabel = {
        let result=UILabel()
        result.tg_width.equal(.fill)
        result.tg_height.equal(.wrap)
        result.numberOfLines = 2
        result.font = UIFont.systemFont(ofSize: 14)
        result.textColor = .colorOnSurface
        return 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 = .black80
        return result
    }()
}

 2)获取data列表数据

定义列表Item数据模型Song

//
//  Song.swift
//  音乐对象
//
//  Created by jin on 2024/9/2.
//

import Foundation

//导入JSON解析框架
import HandyJSON

class 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*2
    
    override 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_SONG
        tableView.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
    }
    
    /// 返回cell
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let data = datum[indexPath.row]
        
        let cell = tableView.dequeueReusableCell(withIdentifier: Constant.CELL, for: indexPath) as! SongCell
        
        if 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
    }
    
    /// 点击了cell
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        SwiftEventBus.post(Constant.EVENT_SONG_CLICK, sender: datum[indexPath.row])
    }
}

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

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

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

相关文章

datagrip链接sql server2005报错

错误信息 第一次报 DBMS: Microsoft SQL Server (no ver.) Case sensitivity: plainmixed, delimitedexact [08S01] 驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误:“The server selected protocol version TLS10 is not accepted by client pr…

汽车电子行业知识:关于车载中控屏

文章目录 1. 车载中控屏的功能2. 最新技术3. 最新产品4. 未来趋势5. 车载中控屏的供应商5.1. 电子元件制造商5.2. 显示技术公司5.3. 软件和系统集成商5.4. 汽车制造商5.5. 新兴科技公司 车载中控屏是现代汽车中不可或缺的组成部分&#xff0c;它不仅提供了车辆信息的显示&#…

爬楼梯[简单]

优质博文&#xff1a;IT-BLOG-CN 题目 假设你正在爬楼梯。需要n阶你才能到达楼顶。 每次你可以爬1或2个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;2 解释&#xff1a;有两种方法可以爬到楼顶。 1阶…

前端模拟面试:如何检查JavaScript对象属性是否存在?

你正在参加一场关键的前端开发面试&#xff0c;面试官提出了一个经典的JavaScript问题&#xff1a;“在JavaScript中&#xff0c;如何检查对象是否包含某个属性&#xff1f;请你详细介绍几种不同的方法&#xff0c;并解释它们的区别。” 这个问题不仅考验你对JavaScript的基础掌…

注册表分析

目录 介绍步骤regsports 介绍 RegRipper 是一个开源工具&#xff0c;用 Perl 编写&#xff0c;用于从注册表中提取/解析信息&#xff08;键、值、数据&#xff09;并将其呈现以供分析。 RegRipper 由两个基本工具组成&#xff0c;它们都提供类似的功能。 RegRipper GUI 允许分…

Mac工程动态库配置和加载探究

缘起 最近在做Mac程序的打包&#xff0c;其中涉及到Mac程序引用了Hoops的第三方动态库。在之前的工程配置中&#xff0c;Project的Run Script是这么来处理动态库的&#xff1a; FRAMEWORKS_DIR${TARGET_BUILD_DIR}/${EXECUTABLE_NAME}.app/Contents/Frameworks/ mkdir -p ${F…

FastDFS分布式存储:概念、集群案例

FastDFS FastDFS&#xff1a;Fast DistributedFileSystem&#xff0c;快速分布式文件系统 FastDFS是一个开源的轻量级分布式文件系统&#xff0c;它对文件进行管理&#xff0c;功能包括&#xff1a;文件存储、文件同步、文件访问&#xff08;文件上传、文件下载&#xff09;等&…

在x86上拉取ARM架构的镜像

添加–platform 参数 docker pull --platform linux/arm64 nginx:1.27.1查看镜像架构 docker inspect nginx:1.27.1 | grep Architecture

智慧社区管理系统平台:架构全新升级,Java商业版OEM开源定制开发

智慧社区综合管理平台&#xff0c;致力于打造以党建为引领&#xff0c;精细化治理 个性化服务于一体的智慧社区平台。 平台整体包含智慧社区综合管理云平台数字孪生大屏可视化APP微信小程序&#xff0c;满足智慧街道、智慧社区标准化功能建设。数字孪生倾斜摄影&#xff0c;支…

云计算之网络

目录 一、VPC&#xff1a;云网络的基石 1.1 VPC产品介绍 1.2 vswitch交换机 1.3 vrouter路由器 1.4 产品架构 1.5 常见问题解答及处理 1.5.1 VPC内如何查询某个IP归属? 1.5.2 网络ACL阻断导致ECS访问CLB不通 1.5.3 EIP秒级突发/分布式限速丢包 1.5.4 NAT网关的流量监…

C# 窗体小实验 点击确定按钮返回文本框显示

1.1创建c项目 1.2配置新项目 1.3打开工具箱 1.4 创建按钮和文本框 拖至到窗体中 右键确定按钮 点击属性 设置Text(确定)文本显示 &#xff0c;buttton2同理 设置退出 设置完成效果 双击确定按钮 进入编辑代码窗口 编写代码如下&#xff1a; 然后设置退出的 代码&#xff1a; 单…

btrace 开源!基于 Systrace 高性能 Trace 工具

android.os.Trace#beginSection 会调用 nativeTraceBegin 方法&#xff0c;该方法实现参考 frameworks/base/core/jni/android_os_Trace.cpp。 static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass, jlong tag, jstring nameStr) { withString(env, nameStr…

实验报告: lookie-lookie 项目测试与分析

目录 一、实验目的 二、实验环境 三、实验步骤 1. 下载与准备项目 1.1 从 GitHub 获取项目 1.2 查看项目文件结构 2. 运行项目 2.1 启动项目 2.2 浏览器设置 3. 项目体验 3.1 功能测试 3.2 运行截图 4. 文件结构分析 4.1 总体结构 4.2 主要文件和目录说明 5. 数…

ElasticSearch分布式搜索引擎入门

一、ElasticSearch Elasticsearch是一个基于 Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java语言开发的&#xff0c;并作为Apache许可条款下的开放源码发布&#xff0c; 是一种流行的企业级搜索引擎。…

图像白平衡

目录 效果 背景 什么是白平衡&#xff1f; 实现原理 将指定图色调调整为参考图色调主要流程 示例代码 效果 将图一效果转换为图二效果色调&#xff1a; 调整后&#xff0c;可实现色调对换 背景 现有两张图像&#xff0c;色调不一致&#xff0c;对于模型重建会有影响。因…

海洋运输船5G智能工厂物联数字孪生平台,推进制造业数字化转型

海洋运输船5G智能工厂物联数字孪生平台&#xff0c;推进制造业数字化转型。在当今全球制造业的浪潮中&#xff0c;数字化转型已成为不可逆转的趋势&#xff0c;它不仅重塑了生产流程&#xff0c;更深刻影响着企业的竞争力与可持续发展能力。其中&#xff0c;海洋运输船5G智能工…

基于python的Selenium webdriver环境搭建(笔记)

一、PyCharm安装配置Selenium环境 本文使用环境&#xff1a;windows11、Python 3.8.1、PyCharm 2019.3.3、Selenium 3.141.0 测试开发环境搭建综述 安装python和pycharm安装浏览器安装selenium安装浏览器驱动测试环境是否正确 这里我们直接从第三步开始 1.1 Seleium安装…

QT实现文本的读写

使用QT读写文件 来&#xff0c;在程序中文件的读写是非常重要的&#xff0c;毕竟我们在大多数时候都是要访问文本文件的&#xff0c;那么今天就来学习一下怎么使用QT来读写文件。 1.写界面 直接在ui界面中编辑即可 布局小技巧我们先选择两个按钮&#xff0c;然后水平布局&am…

iOS18 beta版本怎么回退至iOS17正式版本?

截止目前&#xff0c;苹果最近的iOS18的beta测试版本已经发了8版了&#xff0c;有许多朋友们都已升级提前尝鲜了&#xff0c;升级体验后许多果粉朋友们觉得有许多功能还是不够稳定&#xff0c;有些许bug&#xff0c;就想要降级&#xff0c;回退到iOS17的正式版&#xff0c;但又…

deep-live-cam实时换中文整合包下载,双击exe直接运行

windows环境整合包下载地址&#xff1a; 点击下载 直接解压&#xff0c;双击启动.exe即可使用 硬件要求&#xff1a;有英伟达显卡&#xff0c;且要支持CUDA 硬件不符合要求也不用急&#xff0c;软件也有对应mac版本和windows非N卡版本&#xff0c;我还没做成整合包&#xff0c;…