UI页面布局分析(5)- 评分弹窗的实现
引言
评分功能时许多应用中不可或缺的一部分,无论是购物平台的商品评价,还是内容平台的用户反馈,评分弹窗都能帮助我们快速收集用户的意见。而评分弹窗中,点击星星点亮对应数量的交互,是实现良好用户体验的关键之一。该功能虽然十分简单,但偶尔也会让一些刚入门的开发一时间摸不到头脑。
在这篇博客中,我们将聚焦用评分UI的核心交互,展示如何通过点击星星实现实时点亮效果。我们将分别使用UIKit来实现这个功能,提供该技术栈下的完整代码示例。
页面布局分析
该页面可以大致分为上下两部分:下半部分为取消按钮,而上半部分为评分操作视图。评分操作视图中除了标题、图标和提示信息等辅助元素外,其核心部分是居中的五个星星评分视图。
评分视图的实现方式其实非常灵活,无论是星星、小红心,还是小灯泡,只需要选择适合的图标即可。我们可以使用UIButton或者是UIImageView来构建评分视图的每个小图标。以UIButton为例,可以通过设置按钮的normal状态为非高亮图片,而selected状态为高亮图片来实现状态切换。用户点击按钮时,更新其selected状态,并同步点亮该按钮之前的所有按钮,从而实现评分效果。
具体代码实现
根据前面的布局分析,我们将实现分为两个部分:整体页面的实现和评分核心组件的实现。
整体页面的实现
整体页面包括评分操作视图和取消按钮。评分操作视图包含标题、图标、提示信息,以及核心的评分星星视图。这部分代码将着重展示页面的基本结构和布局逻辑,确保 UI 清晰美观。
具体实现如下:
首先是上半部分的背景视图topBackgroundView,和取消按钮cancelButton:
class PHScoreView: UIView {
/// 上半部分背景
private let topBackgroundView = UIView()
...
/// 取消按钮
private let cancelButton = UIButton()
}
private func setupView() {
// 上半部分背景
addSubview(topBackgroundView)
topBackgroundView.backgroundColor = UIColor(red: 0.118, green: 0.118, blue: 0.18, alpha: 1)
topBackgroundView.layer.masksToBounds = true
topBackgroundView.layer.cornerRadius = 32.0
...
// 取消按钮
addSubview(cancelButton)
cancelButton.setTitle("Cancel", for: .normal)
cancelButton.setTitleColor(.white, for: .normal)
cancelButton.backgroundColor = UIColor(red: 0.118, green: 0.118, blue: 0.18, alpha: 1)
cancelButton.layer.masksToBounds = true
cancelButton.layer.cornerRadius = 18.0
cancelButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
}
private func setLayout() {
// 上半部分背景
topBackgroundView.snp.makeConstraints { (make) in
make.leading.equalToSuperview().offset(30.0)
make.trailing.equalToSuperview().offset(-30.0)
make.top.equalToSuperview()
}
....
// 取消按钮
cancelButton.snp.makeConstraints { (make) in
make.top.equalTo(topBackgroundView.snp.bottom).offset(16.0)
make.leading.equalTo(topBackgroundView.snp.leading)
make.trailing.equalTo(topBackgroundView.snp.trailing)
make.height.equalTo(48.0)
make.bottom.equalToSuperview().offset(-45.0)
}
}
- topBackgroundView视图将会作为标题,图片以及评分星星视图的父视图,它的高度会有里面的子组件来决定,因此我们只设置了top和leading以及trailing约束。
- cancelButton取消按钮比较独立,位于视图的最低端,为了将整个视图的高度撑起,我们需要设置它的top约束以及bottom约束。
接下来是标题,图片星级和提示部分,所有视图将会放置在topBackgroundView上面:
/// 标题
private let titleLabel = UILabel()
/// 标题图片
private let titleImageView = UIImageView()
/// 星星评分视图
private let starView = PHStarView()
/// 提示
private let tipLabel = UILabel()
// 标题
topBackgroundView.addSubview(titleLabel)
titleLabel.text = "Like this app?"
titleLabel.textColor = .white
titleLabel.font = UIFont.boldSystemFont(ofSize: 20)
titleLabel.textAlignment = .center
// 标题图片
topBackgroundView.addSubview(titleImageView)
titleImageView.image = UIImage(named: "logo")
// 星星评分视图
topBackgroundView.addSubview(starView)
// 提示
topBackgroundView.addSubview(tipLabel)
tipLabel.text = "Tap a star to rate"
tipLabel.font = UIFont.systemFont(ofSize: 14)
tipLabel.textColor = UIColor(red: 0.737, green: 0.753, blue: 0.8, alpha: 1)
tipLabel.textAlignment = .center
// 标题
titleLabel.snp.makeConstraints { (make) in
make.top.equalToSuperview().offset(32.0)
make.leading.trailing.equalToSuperview()
make.height.equalTo(27.0)
}
// 标题图片
titleImageView.snp.makeConstraints { (make) in
make.top.equalTo(titleLabel.snp.bottom).offset(16.0)
make.centerX.equalToSuperview()
make.size.equalTo(CGSize(width: 206.0, height: 54.0))
}
// 星星评分视图
starView.snp.makeConstraints { (make) in
make.top.equalTo(titleImageView.snp.bottom).offset(16)
make.centerX.equalToSuperview()
make.height.equalTo(44.0)
}
// 提示
tipLabel.snp.makeConstraints { (make) in
make.top.equalTo(starView.snp.bottom).offset(16)
make.leading.trailing.equalToSuperview()
make.height.equalTo(20)
make.bottom.equalToSuperview().offset(-32.0)
}
- 这里面最需要注意的就是由于我们没有给父视图设置高度,因此需要确保各个子视图之间的约束连贯,确保可以将父视图内容撑起。
- 而startView我们当前只设置了高度,内容还没有实现嗷。
运行效果如下:
星级评价实现
接下来我们将重点放到PHStarView的实现上,我们采用UIStackView+UIButton的方式来实现。
具体布局如下:
class PHStarView:UIView {
/// stackView
private let stackView = UIStackView()
override init(frame: CGRect) {
super.init(frame: frame)
addStackView()
addSubViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func addStackView() {
addSubview(stackView)
stackView.axis = .horizontal
stackView.spacing = 4.0
stackView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
private func addSubViews() {
for i in 0..<5 {
let button = UIButton()
button.setImage(UIImage(named: "star"), for: .normal)
button.setImage(UIImage(named: "star_fill"), for: .selected)
button.tag = i + 100
button.addTarget(self, action: #selector(starClick), for: .touchUpInside)
stackView.addArrangedSubview(button)
button.snp.makeConstraints { make in
make.height.width.equalTo(44.0)
}
}
}
...
}
而关于点击评分的实现,我们只需要将点击事件之前的所有按钮设置为selected状态即可:
@objc private func starClick(sender:UIButton) {
let index = sender.tag - 100
for i in 0..<5 {
let button = stackView.viewWithTag(i + 100) as! UIButton
button.isSelected = i <= index
}
}
运行效果如下:
结语
通过本篇文章,我们介绍了如何使用 UIKit 实现评分弹窗的页面布局和核心交互。我们从整体布局分析入手,逐步构建了一个简单而高效的评分视图,其中包括按钮的点击事件和星星状态的动态更新。
尽管本篇文章仅使用 UIKit 来展示实现过程,但相同的布局思路和交互逻辑也可以很容易地迁移到 SwiftUI 或其他框架中。评分弹窗的这种交互设计无论是在移动应用中,还是在其他平台的开发中,都能为用户提供直观、流畅的体验。
希望本文能够为你提供一些启发,帮助你在自己的项目中实现类似的功能。未来,我们也将继续探讨如何优化评分弹窗的交互效果,提升用户体验。
源码:https://download.csdn.net/download/weixin_39339407/90181770