引言
进度条作为用户页面中一种直观的视觉元素,被光放应用于任务完成度、目标进展等状态。在iOS开发中尽管系统提供了基本的UIProgressView,但在更复杂的场景下,比如与用户身份挂钩的贵族系统页面中,往往需要更具特色的自定义进度条,以匹配整体设计风格并带来更好的交互体验。
以贵族页面的贵族进度条为例,这种进度条不仅需要展示进度数值,还可能包含动态效果、分段提示、渐变填充等视觉样式。通过定制化设计,我们能够让进度条在功能性和美观度上达到新的高度。
本篇博客将会从贵族进度条的布局分析入手,到具体的代码实现。逐步讲解如何实现一款自定义的贵族进度条的实现过程,希望你能够在本篇博客中找到实用的开发技巧和思路。
布局分析
我们可以把进度条设计为四个部分:
- 进度条轨道:首先绿色框圈起来的部分为进度条的整体进度背景,也就是进度条的轨道。
- 进度条进度:其次是红色框圈起来的部分,用来显示进度条的进度。
- 进度当前值:然后是红色箭头指向的部分用来显示进度当前数值及标记的一个小视图。
- 进度条最大值:最后是绿色箭头指向的部分用来显示进度的最大值及标记。
将进度条视图分割成这四大部分之后,我们再来实现它的效果就清晰了许多。
进度条的基本实现
接下下来我们就来实现进度条每个部分的具体布局及实现细节。
进度条轨道
我们最先创建的一个是进度条的轨道,放到所有视图的最下面,保证其它视图在显示时可以覆盖到轨道之上。由于它没有什么特殊的样式,那么只需要创建一个带圆角的UIView即可:
/// 轨道
private let truckView = UIView()
private func setupView() {
// 轨道
self.addSubview(truckView)
truckView.layer.masksToBounds = true
truckView.layer.cornerRadius = 5.0
truckView.backgroundColor = .wm_hex("#2E2E37")
....
}
private func setLayout() {
// 轨道背景
truckView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(8.0)
make.leading.equalToSuperview()
make.height.equalTo(8.0)
....
}
注意在进行布局时,我们只设置了轨道距离父图层左侧的约束,而右侧我们需要等设置最大进度值的时候再进行设置,保证它的最右侧与最大值的最左侧保持一个距离。
进度条
进度条仔细观察的话会有一个上下的渐变效果,关于渐变视图的实现在另外的博客中我们已经介绍过了哈。将进度条添加到轨道之上,并设置约束,左侧与轨道对其,居中显示,而宽度与轨道设置一定比例,初始比例一定是0意味着没有任何进度。
/// 轨道
private let truckView = UIView()
private func setupView() {
...
// 进度
truckView.addSubview(progressView)
progressView.layer.masksToBounds = true
progressView.layer.cornerRadius = 2.0
...
}
private func setLayout() {
...
// 进度
progressView.snp.makeConstraints { make in
make.leading.equalToSuperview()
make.centerY.equalToSuperview()
make.height.equalTo(4.0)
make.width.equalTo(truckView).multipliedBy(0.0)
}
...
}
进度标记
进度标记主要由两部分组成,一个是标记的图标我们可以直接使用UIImageView来显示这个图标,另外一个是进度值,我们可以创建一个UILabel用来显示进度值。首先需要注意的是图标的约束,我们需要使图标的中心X值与进度条的trailing值相同。而进度值的centerX只需要与进度图标的centerX相同,就可以实现页面上要求的进度图标始终在进度条的最右端。
/// 进度标记图片
private let progressMarkIcon = UIImageView()
/// 进度值值
private let progressLabel = UILabel()
private func setupView() {
...
// 进度标记
self.addSubview(progressMarkIcon)
progressMarkIcon.image = UIImage(named: "noble_progress_first_normal")
// progressMarkIcon.image = UIImage(named: "noble_progress_first_selected")
// 进度值
self.addSubview(progressLabel)
progressLabel.font = MWFontHelper.font(name: .nunito, size: 14)
progressLabel.textColor = .wm_hex("#FBF5E4").withAlphaComponent(0.5)
progressLabel.textAlignment = .center
...
}
private func setLayout() {
....
// 进度标记
progressMarkIcon.snp.makeConstraints { make in
make.centerX.equalTo(progressView.snp.trailing)
make.centerY.equalTo(truckView)
make.width.height.equalTo(24.0)
}
// 进度值
progressLabel.snp.makeConstraints { make in
make.centerX.equalTo(progressMarkIcon)
make.top.equalTo(progressMarkIcon.snp.bottom)
}
...
}
进度条最大值
由于最大值也需要特殊标记出来,因此我们也需要为它来创建一个UIImageView和UILable用来显示图标和最大值数值,并放到视图的最右端。由于之前我们并没有设置轨道的右侧约束,因此这里需要记得添加的约束需要和轨道的trailing相关,否则将看不见轨道嗷。
/// 最大值标记图标
private let totalMarkIcon = UIImageView()
/// 最大值
private let totalLabel = UILabel()
private func setupView() {
...
// 最大值
self.addSubview(totalMarkIcon)
totalMarkIcon.image = UIImage(named: "noble_progress_last_normal")
// totalMarkIcon.image = UIImage(named: "noble_progress_last_selected")
// 最大值
self.addSubview(totalLabel)
totalLabel.font = MWFontHelper.font(name: .nunito, size: 14)
totalLabel.textColor = .wm_hex("#FBF5E4").withAlphaComponent(0.5)
}
private func setLayout() {
...
// 最大值图片
totalMarkIcon.snp.makeConstraints { make in
make.centerX.equalTo(truckView.snp.trailing)
make.centerY.equalTo(truckView)
make.width.height.equalTo(24.0)
}
// 最大值
totalLabel.snp.makeConstraints { make in
make.leading.equalTo(truckView.snp.trailing).offset(20.0)
make.width.equalTo(40.0)
make.centerY.equalTo(totalMarkIcon)
make.trailing.equalToSuperview()
}
}
进度更新
进度条应该有三个最基本的属性,最小值,最大值以及当前进度值,在我们创建的进度条当中最小值默认为0。因此需要创建两个属性,最大值以及当前值。
设置最大值
设置最大值的目的之一是为了让右侧的最大值显示数值,而目的二则是为了当我们设置进度值时,计算当前进度的比例。
/// 最大值
var maxValue = 0 {
didSet {
setMaxValue()
}
}
/// 设置最大值
private func setMaxValue() {
totalLabel.text = maxValue.formatter_kmSymbol
}
设置进度值
设置进度值需要进行的操作有很多:
- 更新进度值的数值。
- 更新进度条的约束。
- 设置进度图标的高亮。
- 判断是否达到最大值,如果是则隐藏进度图标和数值,最大值图标高亮。
/// 设置进度值
var progressValue = 0 {
didSet {
setProgressValue()
}
}
/// 设置进度值
private func setProgressValue() {
if maxValue == 0 {
return
}
let progress = CGFloat(progressValue) / CGFloat(maxValue)
progressView.snp.remakeConstraints { make in
make.leading.equalToSuperview()
make.centerY.equalToSuperview()
make.height.equalTo(4.0)
make.width.equalTo(truckView).multipliedBy(progress)
}
self.progressLabel.text = progressValue.formatter_kmSymbol
if progressValue >= 0 {
progressMarkIcon.image = UIImage(named: "noble_progress_first_selected")
progressLabel.textColor = .wm_hex("#FBF5E4")
} else {
progressMarkIcon.image = UIImage(named: "noble_progress_first_normal")
progressLabel.textColor = .wm_hex("#FBF5E4").withAlphaComponent(0.5)
}
if progressValue >= maxValue {
progressLabel.text = ""
progressMarkIcon.image = nil
totalMarkIcon.image = UIImage(named: "noble_progress_last_selected")
totalLabel.textColor = .wm_hex("#FBF5E4")
} else {
totalMarkIcon.image = UIImage(named: "noble_progress_last_normal")
totalLabel.textColor = .wm_hex("#FBF5E4").withAlphaComponent(0.5)
}
}
进度使用
当我们想要使用上述创建的进度条时,只需要和其它视图一样,创建进度条并按照设计图设置约束。然后根据数据设置进度条的最大值及当前值。
/// 进度
private let progressView = MWNobleInProgressView()
// 进度
self.addSubview(progressView)
progressView.maxValue = 90
progressView.progressValue = 40
// 进度
progressView.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview().inset(20.0)
make.bottom.equalToSuperview().offset(-20.0)
make.height.equalTo(43.0)
}
最终效果如下:
结语
本篇博客主要讨论了一个高度自定义的进度视图的布局分析以及具体实现细节。虽然文中的代码示例可能无法直接复制粘贴到项目中使用,但它们更侧重于传递思路和方法,希望在页面布局分析和实现方案上能为你提供启发。
自定义进度视图的设计,不仅体现了页面的整体风格,也提升了用户的交互体验。通过灵活运用绘图、动画以及动态数据绑定技术,我们可以根据不同的业务需求,打造更加个性化的组件。在未来的开发中,这样的实现思路可以延展到更多场景,比如任务进度、用户成就展示,甚至是游戏中的动态UI效果。
希望本文的内容能够为你的开发实践提供帮助。如果你有其他有趣的需求或更具创意的实现方式,欢迎留言分享!