歌单详情完整效果
歌单详情歌单列表头部+图片背景效果
本节是在文章 IOS 24 实现歌单详情(UITableView)列表 的基础上,实现歌单详情里面的歌单列表头部Cell和图片背景效果。
歌单列表头部Cell实现
实现流程:
1.创建Cell,及在使用UITableView的Controller控制器上注册Cell;
2.获取data列表数据,并调用UITableView的reloadData(),将数据更新到列表;
3.将data的Item数据绑定UITableView的每一个Cell。
1)创建和注册Cell
从效果图上面可以看出,歌单列表头部Cell由一个垂直方向布局包含歌单信息和快捷按钮两部分来实现。
自定义SheetInfoCell,继承自BaseTableViewCell,设置TGLinearLayout为垂直方向。
Swift
class SheetInfoCell: BaseTableViewCell{
static let NAME = "SheetInfoCell"
override func initViews() {
super.initViews()
container.tg_padding = UIEdgeInsets(top: PADDING_OUTER, left: PADDING_OUTER, bottom: PADDING_LARGE2, right: PADDING_OUTER)
container.tg_space = PADDING_LARGE2
}
override func getContainerOrientation() -> TGOrientation {
return .vert
}
}
添加歌单信息和快捷按钮布局,并绑定布局数据
Swift
class SheetInfoCell: BaseTableViewCell{
static let NAME = "SheetInfoCell"
override func initViews() {
super.initViews()
container.tg_padding = UIEdgeInsets(top: PADDING_OUTER, left: PADDING_OUTER, bottom: PADDING_LARGE2, right: PADDING_OUTER)
container.tg_space = PADDING_LARGE2
//水平容器
let orientationContainer = ViewFactoryUtil.orientationContainer()
orientationContainer.tg_space = PADDING_OUTER
orientationContainer.tg_gravity = TGGravity.vert.center
orientationContainer.tg_padding = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: PADDING_SMALL)
container.addSubview(orientationContainer)
//图标
orientationContainer.addSubview(iconView)
//右侧容器
let rightContainer = TGLinearLayout(.vert)
rightContainer.tg_width.equal(.fill)
rightContainer.tg_height.equal(.wrap)
rightContainer.tg_space = PADDING_SMALL
orientationContainer.addSubview(rightContainer)
//标题
rightContainer.addSubview(titleView)
//用户容器
let userContainer = ViewFactoryUtil.orientationContainer()
userContainer.tg_space = PADDING_SMALL
userContainer.tg_gravity = TGGravity.vert.center
rightContainer.addSubview(userContainer)
userContainer.addSubview(avatarView)
userContainer.addSubview(nicknameView)
//详情容器
let detailContainer = ViewFactoryUtil.orientationContainer()
detailContainer.tg_top.equal(PADDING_MEDDLE)
detailContainer.tg_space = PADDING_SMALL
userContainer.tg_gravity = TGGravity.vert.center
rightContainer.addSubview(detailContainer)
detailContainer.addSubview(detailView)
detailContainer.addSubview(ViewFactoryUtil.moreIconView())
//快捷按钮容器
let buttonContainer = ViewFactoryUtil.orientationContainer()
buttonContainer.corner(23)
buttonContainer.tg_horzMargin(PADDING_LARGE2)
buttonContainer.tg_height.equal(46)
container.addSubview(buttonContainer)
buttonContainer.addSubview(collectCountView)
buttonContainer.addSubview(ViewFactoryUtil.smallVerticalDivider())
buttonContainer.addSubview(commentCountView)
buttonContainer.addSubview(ViewFactoryUtil.smallVerticalDivider())
buttonContainer.addSubview(shareCountView)
}
func bind(_ data:Sheet) {
iconView.show2(data.icon)
titleView.text = data.title
avatarView.show2(data.user.icon)
nicknameView.text = data.user.nickname
detailView.text = data.detail
collectCountView.setTitle("\(data.collectsCount)", for: .normal)
commentCountView.setTitle("\(data.commentsCount)", for: .normal)
}
override func getContainerOrientation() -> TGOrientation {
return .vert
}
lazy var iconView: UIImageView = {
let r = UIImageView()
r.tg_width.equal(120)
r.tg_height.equal(120)
r.image = R.image.placeholder()
r.smallCorner()
r.contentMode = .scaleAspectFill
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_LARGE2)
r.textColor = .colorLightWhite
return r
}()
lazy var avatarView: UIImageView = {
let r = UIImageView()
r.tg_width.equal(30)
r.tg_height.equal(30)
r.contentMode = .scaleAspectFill
r.smallCorner()
return r
}()
lazy var nicknameView: UILabel = {
let r = UILabel()
r.tg_width.equal(.wrap)
r.tg_height.equal(.wrap)
r.numberOfLines = 1
r.font = UIFont.systemFont(ofSize: TEXT_MEDDLE)
r.textColor = .colorLightWhite
return r
}()
lazy var detailView: UILabel = {
let r = UILabel()
r.tg_width.equal(160)
r.tg_height.equal(.wrap)
r.numberOfLines = 1
r.font = UIFont.systemFont(ofSize: TEXT_MEDDLE)
r.textColor = .colorLightWhite
return r
}()
lazy var collectCountView: QMUIButton = {
let r = ViewFactoryUtil.secoundButton(icon: R.image.search()!.withTintColor(), title: "0")
r.backgroundColor = .colorLightWhite
return r
}()
lazy var commentCountView: QMUIButton = {
let r = ViewFactoryUtil.secoundButton(icon: R.image.search()!.withTintColor(), title: "0")
r.backgroundColor = .colorLightWhite
return r
}()
lazy var shareCountView: QMUIButton = {
let r = ViewFactoryUtil.secoundButton(icon: R.image.search()!.withTintColor(), title: "0")
r.backgroundColor = .colorLightWhite
return r
}()
}
在SheetDetailController控制器,注册SheetInfoCell
Swift
class SheetDetailController: BaseTitleController {
override func initViews() {
super.initViews()
title = R.string.localizable.sheet()
// 注册单曲
tableView.register(SongItemCell.self, forCellReuseIdentifier: Constant.CELL)
tableView.register(SheetInfoCell.self, forCellReuseIdentifier: SheetInfoCell.NAME)
tableView.bounces = false
}
}
2)获取data列表数据
定义列表数据模型Sheet
Swift
//
// Sheet.swift
// 歌单对象
//
// Created by jin on 2024/8/23.
//
import Foundation
//导入JSON解析框架
import HandyJSON
class Sheet:BaseCommon {
/// 歌单标题
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?
override func mapping(mapper: HelpingMapper) {
super.mapping(mapper: mapper)
mapper <<< self.clicksCount <-- "clicks_count"
mapper <<< self.collectsCount <-- "collects_count"
mapper <<< self.commentsCount <-- "comments_ount"
mapper <<< self.songsCount <-- "songs_count"
}
}
请求歌单详情接口获取歌单详情里的歌曲列表数据,更新tableView.reloadData()
Swift
class SheetDetailController: BaseTitleController {
/// 数据id
var id: String!
var data: Sheet!
override func initViews() {
super.initViews()
// 注册单曲
tableView.register(SongItemCell.self, forCellReuseIdentifier: Constant.CELL)
tableView.register(SheetInfoCell.self, forCellReuseIdentifier: SheetInfoCell.NAME)
tableView.bounces = false
}
override func initDatum() {
super.initDatum()
loadData()
}
func loadData() {
DefaultRepository.shared
.sheetDetail(id)
.subscribeSuccess { [weak self] data in
self?.show(data.data!)
}.disposed(by: rx.disposeBag)
}
func show(_ data: Sheet) {
self.data = data
//第一组
datum = [data]
//第二组
if let r = data.songs {
if !r.isEmpty {
//有音乐才设置
//设置数据
datum += data.songs ?? []
superFooterContainer.backgroundColor = .colorLightWhite
}
}
tableView.reloadData()
}
/// 获取列表类型
func typeForItemAtData(_ data:Any) -> MyStyle {
if data is Sheet {
return .sheet
}
return .song
}
}
3)Item数据绑定Cell
SheetDetailController控制器重写父类的扩展 cellForRowAt方法,创建对应的Cell,并将Item数据绑定到Cell。
Swift
extension SheetDetailController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = datum[indexPath.row]
let type = typeForItemAtData(data)
switch type {
case .sheet:
let cell = tableView.dequeueReusableCell(withIdentifier: SheetInfoCell.NAME, for: indexPath) as! SheetInfoCell
cell.bind(data as! Sheet)
return cell
default:
let cell = tableView.dequeueReusableCell(withIdentifier: Constant.CELL, for: indexPath) as! SongItemCell
cell.bind(data as! Song)
cell.indexView.text = "\(indexPath.row)"
return cell
}
}
}
歌单详情图片背景效果
为整个界面添加图片背景和模糊效果
Swift
class SheetDetailController: BaseTitleController {
//背景
var backgroundImageView: UIImageView!
//背景模糊
var backgroundVisual: UIVisualEffectView!
override func initViews() {
super.initViews()
//添加背景图片控件
backgroundImageView = UIImageView()
backgroundImageView.clipsToBounds = true
backgroundImageView.alpha = 0
backgroundImageView.contentMode = .scaleAspectFill
view.addSubview(backgroundImageView)
//背景模糊效果
let blur = UIBlurEffect(style: .dark)
backgroundVisual = UIVisualEffectView(effect: blur)
backgroundImageView.addSubview(backgroundVisual)
// 初始化TableView结构
initTableViewSafeArea()
title = R.string.localizable.sheet()
// 注册单曲
tableView.register(SongItemCell.self, forCellReuseIdentifier: Constant.CELL)
tableView.register(SheetInfoCell.self, forCellReuseIdentifier: SheetInfoCell.NAME)
tableView.bounces = false
}
}
从歌单详情接口中获取歌单封面图片作为界面的背景图片
Swift
class SheetDetailController: BaseTitleController {
/// 数据id
var id: String!
var data: Sheet!
//背景
var backgroundImageView: UIImageView!
//背景模糊
var backgroundVisual: UIVisualEffectView!
override func initViews() {
super.initViews()
//添加背景图片控件
backgroundImageView = UIImageView()
backgroundImageView.clipsToBounds = true
backgroundImageView.alpha = 0
backgroundImageView.contentMode = .scaleAspectFill
view.addSubview(backgroundImageView)
//背景模糊效果
let blur = UIBlurEffect(style: .dark)
backgroundVisual = UIVisualEffectView(effect: blur)
backgroundImageView.addSubview(backgroundVisual)
}
override func initDatum() {
super.initDatum()
loadData()
}
func loadData() {
DefaultRepository.shared
.sheetDetail(id)
.subscribeSuccess { [weak self] data in
self?.show(data.data!)
}.disposed(by: rx.disposeBag)
}
func show(_ data: Sheet) {
self.data = data
backgroundImageView.show(data.icon)
//使用动画显示背景图片
UIView.animate(withDuration: 0.3) {
//透明度设置为1
self.backgroundImageView.alpha = 1
}
}
}
在viewDidLayoutSubviews()方法里面设置背景图片和模糊效果的宽高和坐标
Swift
class SheetDetailController: BaseTitleController {
/// 数据id
var id: String!
var data: Sheet!
//背景
var backgroundImageView: UIImageView!
//背景模糊
var backgroundVisual: UIVisualEffectView!
override func initViews() {
super.initViews()
//添加背景图片控件
backgroundImageView = UIImageView()
backgroundImageView.clipsToBounds = true
backgroundImageView.alpha = 0
backgroundImageView.contentMode = .scaleAspectFill
view.addSubview(backgroundImageView)
//背景模糊效果
let blur = UIBlurEffect(style: .dark)
backgroundVisual = UIVisualEffectView(effect: blur)
backgroundImageView.addSubview(backgroundVisual)
}
override func initDatum() {
super.initDatum()
loadData()
}
func loadData() {
DefaultRepository.shared
.sheetDetail(id)
.subscribeSuccess { [weak self] data in
self?.show(data.data!)
}.disposed(by: rx.disposeBag)
}
func show(_ data: Sheet) {
self.data = data
backgroundImageView.show(data.icon)
//使用动画显示背景图片
UIView.animate(withDuration: 0.3) {
//透明度设置为1
self.backgroundImageView.alpha = 1
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
//设置背景宽高+坐标
backgroundImageView.frame = view.bounds
backgroundVisual.frame = backgroundImageView.bounds
}
}
至此,实现了歌单详情里面的歌单列表头部+图片背景效果。