引言
在现代直播应用中,用户小卡已经成为不可或缺的UI组件。它不仅承载展示用户信息的基础功能,还在主播与观众、观众与观众之间的互动扮演重要角色。一个设计合理、布局高效的用户小卡,可以提升直播间用户体验,同时也有助于增强用户的参与感和归属感。
本篇博客将以直播间内用户小卡为例,从结构组成入手,深入分析其布局设计。随后我们将结合具体代码,探讨如何使用UITableView来实现长页面的布局设计,并分享一些优化技巧,帮助开发者在实际项目中高效实现类似的布局和功能。
用户卡片结构组成
从上图可以看到,直播间内的用户小卡包含了非常丰富的元素。我们可以从上到下将其划分为几个部分进行详细说明:
-
1.顶部的操作按钮
位于小卡左上角的操作按钮,例如举报或设置按钮。 -
2.用户基本信息
显示用户头像、昵称和用户ID,同时根据用户佩戴的徽章显示对应的徽章信息。 -
3.用户等级信息
用户基本信息下方展示用户的等级和经验值,直观地反映用户的活跃程度。 -
4.用户年龄、性别、地址与签名信息
这一部分包含用户的年龄、性别、地址以及个性签名等文本信息。 -
5.用户的关注和粉丝数量
进一步往下,显示用户的关注数量、粉丝数量,以及贡献榜前三名粉丝的信息。 -
6.用户的送礼和收礼信息
在粉丝数量下面,用于展示用户累计收到的金币和送出的礼物总数。 -
7.用户的等级、性别及标签徽章
这一部分集中展示用户的等级标签、性别标签、VIP、SVIP、贵族场控等状态标识,以及用户佩戴的所有徽章信息。 -
8.用户达成的成就信息
显示用户已达成的成就数量,并展示部分成就图标作为代表。 -
9.底部操作按钮
小卡底部的操作栏,包含关注、取消关注、@用户以及发送私信等交互按钮。 -
10.小卡背景
除了上述展示信息与操作按钮外,小卡还配有全屏或半屏高度的背景,用于区分不同用户的卡片类型或身份。
如此多的UI元素,如果直接将所有内容堆叠到一个小卡视图中,代码会变得冗杂且难以维护。更重要的是,如果需要在此基础上增减元素,操作起来将更加复杂。因此,设计一个合理且高效的UI布局方案显得尤为重要。
用户小卡布局分析
根据上面我们对小卡结构组成的分析,我们可以将小卡的布局这样进行分割。
关于用户小卡的全屏背景和半屏背景,我们可以直接使用图片按照设计的比例添加到小卡视图的最低层,不需要考虑任何其它元素,只要覆盖在上面的元素本身没有背景颜色,那么我们总可以看见小卡的背景。
背景之外的部分,我们可以注意到,顶部的操作栏和底部的工具栏是固定在小卡视图的这两个位置,并不会滑动,而其它的所有内容则是在这两个之间,并且随着内容高度高于小卡的高度还会上下滑动,因此我们将小卡的布局首先分为三个部分:
- 顶部工具栏:放置设置,举报等按钮,位于小卡视图的最顶端。
- 中间信息视图:用来展示所有的用户信息,内容高度不定,可以上下滑动。
- 底部工具栏:用户放置关注,@和私信等操作按钮。
关于顶部和底部的工具栏,非常简单都是单一的视图。但就中间的信息视图而言,我们还需要选择一个合适的方式来实现它的布局。由于视图可以上下滑动,所以首先会想到UIScrollView,但是如果使用了UIScrollView我们就需要自己来维护上面子组件的显示和隐藏。所以更理想的选择我们可以使用继承自UIScrollView的UITableView,不同的用户信息视图,就创建不同类型的cell,然后使用数据来控制对应信息cell的显示与隐藏。
用户小卡布局代码实现
接下来我们开始结合具体的代码来分析小卡布局的实现。从小卡背景介绍起,再到顶部工具栏视图,底部工具栏视图,以及中间显示所有用户信息的UITableView。
小卡背景
小卡背景有两种形式,一种是半屏的小卡,只占据用户小卡的顶部的一小部分,另外还有一个全屏的小卡,会占据整个小卡背景。由于它们都不受任何其它子组件的影响,因此我们可以直接着手它们的布局。
/// 卡片背景图片
private let bgImageView = UIImageView()
/// 顶部卡片背景图片
private let topBgImageView = UIImageView()
private func setupView() {
self.backgroundColor = .wm_hex("#1E1E2E")
self.layer.masksToBounds = true
self.layer.cornerRadius = 20.0
// 卡片背景图片
self.addSubview(bgImageView)
bgImageView.contentMode = .scaleAspectFill
// 顶部卡片背景图片
self.addSubview(topBgImageView)
topBgImageView.contentMode = .scaleAspectFill
....
}
private func setLayout() {
// 卡片背景图片
bgImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
// 顶部卡片背景图片
topBgImageView.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview()
}
....
}
- 全屏的背景我们使用bgImageView来显示,它直接充满整个小卡视图。
- 而半屏的背景我们使用topBgImageView来显示,它的leading,trailing和top与小卡对其,而高度则会根据内容自动填充。
顶部和底部工具栏
顶部和底部的工具栏,实际上和小卡背景异曲同工,它们都有固定的位置并且不受其它子组件的影响,并且高度固定。
/// 顶部操作视图
private let topToolView = MWUserCardTopToolView()
/// 底部操作视图
private let bottomToolView = MWUserCardBottomToolView()
private func setupView() {
....
// 顶部操作视图
self.addSubview(topToolView)
// 列表
...
// 底部操作视图
self.addSubview(bottomToolView)
bottomToolView.backgroundColor = .wm_hex("353543")
}
private func setLayout() {
...
// 顶部操作视图
topToolView.snp.makeConstraints { make in
make.leading.trailing.top.equalToSuperview()
make.height.equalTo(48.0)
}
// 列表
...
// 底部操作视图
bottomToolView.snp.makeConstraints { make in
make.leading.trailing.bottom.equalToSuperview()
make.height.equalTo(44.0)
}
}
- 顶部工具栏固定在顶部,高度为48。
- 底部工具栏固定在小卡底部,高度固定为44。
用户信息列表
用户信息我们采用UITableView来展示,从用户的头像昵称等信息到用户的成就信息,每一个信息视图对应一个类型的cell。为此我们定义了一个数据模型MWBaseRowItem,里面包含了cell的类以及cell标识,具体实现如下:
import UIKit
open class MWBaseRowItem: NSObject {
/// cell标识
public var cellIdentifier: String = ""
/// cell类名
public var cellClass: AnyClass?
public init(cellIdentifier: String, cellClass: AnyClass? = nil) {
self.cellIdentifier = cellIdentifier
self.cellClass = cellClass
}
}
而每一条用户信息都会创建一个对应的MWBaseRowItem实例,放在dataList里,来维护用户信息的一个列表。列表会根据dataList的内容来显示对应类型cell。
dataList构建实现如下:
/// 构建用户卡片列表数据
class func buildUserCardListData() -> [MWBaseRowItem] {
var list = [MWBaseRowItem]()
//1.头部数据
let headerItem = MWBaseRowItem(cellIdentifier: NSStringFromClass(MWUserCardHeadInfoCell.self), cellClass: MWUserCardHeadInfoCell.self)
list.append(headerItem)
//2.等级
let levelItem = MWBaseRowItem(cellIdentifier: NSStringFromClass(MWUserCardLevelCell.self), cellClass: MWUserCardLevelCell.self)
list.append(levelItem)
//3. 介绍
let introItem = MWBaseRowItem(cellIdentifier: NSStringFromClass(MWUserCardIntroCell.self), cellClass: MWUserCardIntroCell.self)
list.append(introItem)
//4.粉丝/关注数量
let fansItem = MWBaseRowItem(cellIdentifier: NSStringFromClass(MWUserCardFansCell.self), cellClass: MWUserCardFansCell.self)
list.append(fansItem)
//5.金币钻石
let coinItem = MWBaseRowItem(cellIdentifier: NSStringFromClass(MWUserCardCoinCell.self), cellClass: MWUserCardCoinCell.self)
list.append(coinItem)
//6.标签徽章
let badgeItem = MWBaseRowItem(cellIdentifier: NSStringFromClass(MWUserCardBadgeCell.self), cellClass: MWUserCardBadgeCell.self)
list.append(badgeItem)
//7.成就
let achievementItem = MWBaseRowItem(cellIdentifier: NSStringFromClass(MWUserCardAchievementCell.self), cellClass: MWUserCardAchievementCell.self)
list.append(achievementItem)
return list
}
数据里面包含了从用户头像到,用户成就列表的所有数据,以及对应的cell。
下面我们只需要创建一个UITableView并,使用dataList里面的数据来渲染列表即可,实现如下:
/// 列表
private let tableView = UITableView(frame: .zero, style: .plain)
private func setupView() {
....
// 列表
self.addSubview(tableView)
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.delegate = self
tableView.dataSource = self
for item in dataList {
tableView.register(item.cellClass, forCellReuseIdentifier: item.cellIdentifier)
}
....
}
private func setLayout() {
...
// 列表
tableView.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview()
make.bottom.equalToSuperview().offset(-44.0)
make.top.equalTo(topToolView.snp.bottom)
}
...
}
- 我们根据dataList的数据来注册所有需要用的cell。
- 布局时,将顶部工具栏和底部工具栏的位置空出。
接下来在UITableView的代理方法中,我们需要根据dataList中的元素标识,直接返回cell即可:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = dataList[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: item.cellIdentifier, for: indexPath)
cell.selectionStyle = .none
return cell
}
我们不需要关心cell的类型,也不需要关心该显示那些视图,所有的事情都在数据处理阶段完成了。就算新增新的信息视图,我们只需要创建一个新的cell添加到dataList数组中。如果需要因此某个信息视图,只将对应的数据从dataList数组中移除。
结语
直播间内用户小卡的设计不仅仅是简单的信息展示,更是用户交互体验的关键一环。在这篇博客中,我们从小卡的结构组成入手,分析了其布局设计的重点,并通过代码展示了如何高效实现这样一个复杂的UI组件。
合理的布局不仅能够提升代码的可维护性,还能为后续功能扩展打下坚实的基础。通过模块化的设计思路,我们可以轻松地增减元素,而不影响整体的布局和性能。希望本篇内容能为你在直播应用开发中处理类似的复杂UI需求提供帮助与灵感。
UI设计的每一处细节都值得被精雕细琢,而用户小卡的布局也不例外。如果你在阅读后有任何问题或更好的设计思路,欢迎留言与我交流,共同探索更优雅的实现方式!