UI界面布局分析(1) - 榜单

前直播项目中,仍然还有很多令人眼花缭乱的布局及功能尚未实现,如果你有兴趣帮忙或者挑战一下的话,可以私信我,我会提供给你完整的设计图链接,接口以及需求的具体方案,我们一起讨论。

​​​​​​​引言

榜单列表是一种广泛应用于现代移动应用的界面设计形式,几乎每种类型的应用都能找到它的身影。在电商应用中,榜单可以展示热销商品,吸引用户关注高人气商品;在社交平台上,榜单用于显示用户互动的活跃度或内容的热度;而咋直播领域,榜单更是必不可少,用户可以通过它实时了解主播或用户的排名,激发更多互动和打赏行为。

尽管榜单列表看似简单,但其设计和实现往往包含了复杂的层级逻辑与布局需求。如果将多层级榜单的数据清晰、有条理地呈现?如何确保界面美观的同时提升性能与用于体验?这些都是我们在实现过程中需要重点考虑的问题。

本篇博客将以上面的榜单列表为例,详细分析其布局接口,包括整体层级划分与子列表的布局实现,帮助你在项目中构建高效且优雅的榜单界面。

需求分析

整个大的榜单页面包含了若干具体的榜单列表,比如Daily,Weekly,Monthly等等,这些列表事实上长的完全相同,只是数据从不同的接口或者相同的接口传递不同的参数来获取的。另外每个列表中都会有一个特殊的表头来展示榜单中排名前三的用户,而下面接下来才是一个普通的列表。还有一点需要注意的是当向上滑动列表时直到表头消失后,普通列表的圆角会停留在页面的最顶部。

总结一下:

  1. 榜单中包含多个不同的榜单。
  2. 每个榜单需要一个特殊的表头用来放置前三用户。
  3. 列表向上滚动时,下半部分的普通列表的顶部会停留在页面的顶部。

布局设计

分析完页面的结构及交互逻辑之后,根据提炼出来的三点,我们就可以着手进行布局设计了。

榜单列表页面

多个子页面的实现有很多方案,也有很多成熟的三方框架可以直接使用,它们甚至已经帮你管理好顶部分页控制器与底部子页面的的切换逻辑,而我们需要做的只是将标题以及子页面对应上,就可以实现多个子页面的切换功能。

不过在本篇博客中我们会以最直观的形式来进行设计和展示。其实在所有的方案中,归根结底用来放置子页面的视图都是UIScrollView,当然了除非你想自己实现滑动或者不滑动。而顶部的分页控制器,我们可以简单的创建几个按钮,每个按钮对应不同的子页面。那么整个结构应该如下:

注释:

  1. 白色的为榜单页面PHListViewController。
  2. 红色的为横向滚动的UIScrollView。
  3. 绿色的就是子页面,用来显示每个榜单PHListSubViewController。
  4. 橙色的按钮用来切换榜单,也就是用来设置UIScrollView的偏移量。

单个榜单页面

这个页面由于底部白色部分列表存在一个顶部圆角,并且当上滑到底部上顶部圆角会停留在页面的最顶端,这为单个列表的实现增加了一点难度。我们可以使用嵌套列表,把底部白色部分单独当做一个列表当滑动到顶部时,才开始允许白色列表滑动,在之前的博客中也有介绍过嵌套列表的实现方案。

但在这榜单列表中我们可以使用更简单的方式,将这个列表分为三部分:

  1. 红色部分作为列表的头部。
  2. 绿色的部分作为表的组标题,组标题滑动到最顶部的时候会自动悬停。
  3. 而黄色的部分就是列表的每个cell,由于布局问题第一个cell可能高度和布局可能略有不同,但我们暂且不考虑它。

代码实现

接下来我们使用代码来实现一个相对简单的案例,案例中会实现榜单中多个子榜单的布局及切换操作,以及每个榜单的上滑下滑操作,但每个元素的详细布局不在这篇博客的讨论范围。

榜单列表页面

榜单列表按照我们之前的布局分析,我们只需要添加顶部的分页控制器,底部的用于承载子列表的横向滚动UIScrollView,以及子列表。

那么具体代码如下:

class MWHotListViewController: UIViewController {
    
    /// 顶部分页控制器
    private let segmentControl = MWHotSegmentControl()
    /// 横向滚动的scrollView,用来展示不同的子控制器
    private let scrollView = UIScrollView()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .blue
        addSegmentControl()
        addScrollView()
        addSubListViewController()
    }
    
    /// 添加分页控制器
    private func addSegmentControl() {
        view.addSubview(segmentControl)
        segmentControl.snp.makeConstraints { (make) in
            make.top.equalToSuperview().offset(MW_NAVIGATIONBAR_HEIGHT)
            make.leading.trailing.equalToSuperview().inset(16.0)
            make.height.equalTo(44.0)
        }
    }
    
    /// 添加scrollView
    private func addScrollView() {
        view.addSubview(scrollView)
        scrollView.isPagingEnabled = true
        scrollView.snp.makeConstraints { (make) in
            make.top.equalTo(segmentControl.snp.bottom)
            make.leading.bottom.equalToSuperview()
            make.width.equalTo(MW_SCREEN_WIDTH)
        }
    }
    
    /// 添加所有榜单
    private func addSubListViewController() {
        let colors = [UIColor.red,UIColor.green,UIColor.blue]
        let subListViewControllers = [MWHotSubListViewController(),MWHotSubListViewController(),MWHotSubListViewController()]
        for (index,subListViewController) in subListViewControllers.enumerated() {
            addChild(subListViewController)
            subListViewController.view.backgroundColor = colors[index]
            scrollView.addSubview(subListViewController.view)
            subListViewController.view.snp.makeConstraints { (make) in
                make.top.equalToSuperview()
                make.height.equalToSuperview()
                make.leading.equalToSuperview().offset(MW_SCREEN_WIDTH*CGFloat(index))
                make.width.equalTo(MW_SCREEN_WIDTH)
                if index == subListViewControllers.count - 1 {
                    make.trailing.equalToSuperview()
                }
            }
        }
    }

    
  
}

运行代码,可以看见我们目前就已经实现了大榜单的页面结构。

单个列表页面

那么对于单个列表,我们则需要按照表头,表的组标题,以及列表Cell的方式来创建。

表头

关于表头上面内容的UI布局,我们就不在这篇博客进行分析了。表头保证透明可以让背景颜色投过来即可,为此我们为列表添加一个高度为255的透明UIView作为列表的头视图。

   /// 列表
    private let tableView = UITableView(frame: .zero, style: .plain)
    

    override func viewDidLoad() {
        super.viewDidLoad()
        addTableView()
        addTableHeaderView()
    }
    
    private func addTableView() {
        self.view.addSubview(tableView)
        tableView.backgroundColor = .clear
        tableView.delegate = self
        tableView.sectionHeaderTopPadding = 0.0
        tableView.dataSource = self
        tableView.snp.makeConstraints { (make) in
            make.edges.equalToSuperview()
        }
        tableView.register(MWSectionHeaderView.self, forHeaderFooterViewReuseIdentifier: "sectionHeader")
    }
    
    private func addTableHeaderView() {
        let header = UIView(frame: CGRect(x: 0, y: 0, width: MW_SCREEN_WIDTH, height: 255.0))
        header.backgroundColor = .clear
        tableView.tableHeaderView = header
    }
 

组头

而组头需要我们重点来考虑一下,因为实现上滑后白色列表顶部悬停在视图顶部的效果都需要在这里来实现,为此我们继承自UITableViewHeaderFooterView自定义了一个组头视图,并为它设置了顶部圆角,具体代码如下:

class MWSectionHeaderView: UITableViewHeaderFooterView {

   /// 背景
    private let backView = UIView()
    /// 底部视图
    private let bottomView = UIView()
    
    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)
        addBackView()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func addBackView() {
        contentView.addSubview(backView)
        backView.backgroundColor = .blue
        backView.frame = CGRect(x: 0, y: 0, width: MW_SCREEN_WIDTH, height: 24.0)
        contentView.addSubview(bottomView)
        bottomView.backgroundColor = .white
        bottomView.frame = CGRect(x: 0, y: 0, width: MW_SCREEN_WIDTH, height: 24.0)
    }
    
    override func layoutSubviews() {
         //添加顶部圆角
        let maskPath = UIBezierPath(roundedRect: bottomView.bounds, byRoundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 12, height: 12))
        let maskLayer = CAShapeLayer()
        maskLayer.frame = bottomView.bounds
        maskLayer.path = maskPath.cgPath
        bottomView.layer.mask = maskLayer
    }

}

列表

而其它的内容视图,则不需要过多考虑,我们只创建系统为我们提供的cell即可,列表实现的代码如下:

    // MARK: - UITableViewDelegate,UITableViewDataSource
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 24.0
    }
    
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "sectionHeader") as! MWSectionHeaderView
        header.backgroundColor = .blue
        return header
    }
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
        if cell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
            cell?.backgroundColor = .white
        }
        cell?.textLabel?.text = "第\(indexPath.row)行"
        return cell!
    }

此时运行的效果如下:

结语

本篇博客通过对榜单列表的需求分析,以及布局设计分析,采用最直接简洁的方式实现了整个榜单列表的布局,博客中只介绍了榜单列表实现的两个关键步骤,详细实现步骤并没有列出,但Demo实例代码中包含了完整代码,比如点击按钮切换榜单,以及左右滑动切换榜单顶部分页控制器实现同步切换等等。

关于页面布局的分析并不复杂,相同的界面也会有很多种不同的实现方式,只要页面结构层次清晰,那么都算是一个好的布局。大家有什么其它的布局设计方案也可以私信交流。

另外当前直播项目中,仍然还有很多令人眼花缭乱的布局及功能尚未实现,如果你有兴趣帮忙或者挑战一下的话,可以私信我,我会提供给你完整的设计图链接,接口以及需求的具体方案,我们一起讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值