带你手把手撸一个网易云音乐首页(上篇)

本文介绍了使用Swift构建网易云音乐首页的过程,包括调研分析、App框架构建、首页功能需求,以及MVVM模式的应用。通过分析API获取数据,利用UITableView嵌套UICollectionView实现视图效果,文章详细讲解了如何创建Model和ViewModel,为后续的视图构建打下基础。
摘要由CSDN通过智能技术生成

标题前言

Hello,大家好,近期我一直在学习用 Swift 编码,由于之前很多项目我都是用 OC 实现的,所以导致我现在对 Swift 还是处于一个学习的阶段中。为了提高自己的学习效率,每次我都会为自己定下一个短期的目标,就那这次来说吧,为了加快自己上手 Swift, 我为自己定下了的目标就是完成一个 Swift 版本的网易云音乐 App。不知道大家在学习一门新语言的时候,是如何提高学习效率的?不妨在评论区与大家交流一下。
在这里插入图片描述

标题调研分析

先分析一下 iOS 端网易云音乐 App 的首页,如图所示:
在这里插入图片描述
看完后,首先摆在我眼前的第一个困难就是我该如何去获取这些数据!我的第一个想法当然就是去 GitHub 上找有没有开源的 API,不找不知道,一找果然很满意,原来早就有大佬提供了网易云音乐的 API:
在这里插入图片描述
其中就有“首页发现” 和 “首页-发现-圆形图标入口列表” 的 API, 无需我们进行多个接口的调用以及数据源的拼接,就可一获取首页的全部数据啦!在分析返回的 JSON 数据格式的时候,还给大佬提了个issue,大佬也很快的回复了,再次膜拜一下大佬。

{
    "code": 200,
    "data": {
        "cursor": null,
        "blocks": [
            {
                "blockCode": "HOMEPAGE_BANNER",
                "showType": "BANNER",
                "extInfo": {
                    "banners": [
                        {
                            "adLocation": null,
                            "monitorImpress": null,
                            "bannerId": "1622653251261138",
                            "extMonitor": null,
                            "pid": null,
                            "pic": "http://p1.music.126.net/gWmqDS3Os7FWFkJ3s8Wotw==/109951166052270907.jpg",
                            "program": null,
                            "video": null,
                            "adurlV2": null,
                            "adDispatchJson": null,
                            "dynamicVideoData": null,
                            "monitorType": null,
                            "adid": null,
                            "titleColor": "red",
                            "requestId": "",
                            "exclusive": false,
                            "scm": "1.music-homepage.homepage_banner_force.banner.2941964.-1777659412.null",
                            "event": null,
                            "alg": null,
                            "song": {

                ......
}

数据源的问题解决了,接下来就是该解决如何将数据可视化了,从网易云音乐首页展示的效果分析来看,整体的视图支持上下滚动,其中单个 Cell 的视图支持横向滚动,所以这里采用 UITableView 嵌套 UICollectionView 的方式应该来说再合适不过了。

剩下的就是需要用到的一些第三方库了,在这里我们用到的第三方库如下:

  • Alamofire
  • Kingfisher
  • SnapKit

标题需要实现的功能

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:710 558 675 ,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)

它的首页内容大致可以分为以下几部分:

  1. 顶部搜索视图
  2. Banner
  3. 圆形菜单按钮
  4. 推荐歌单
  5. 个性推荐
  6. 精选音乐视频
  7. 雷达歌单
  8. 热门播客
  9. 专属场景歌单
  10. 新歌,新碟,数字专辑
  11. 音乐日历
  12. 24小时播客
  13. 视频合辑

支持 light Mode 和 Dark Mode 主题

这里先放上我最终实现好了的效果图:
在这里插入图片描述
具体的实现细节我会通过两篇文章阐述,功能会按照我上述列出来的功能顺序来一一实现的,废话不多说,咱们继续来接着来往下讲。

构建 App 框架

首先打开我们的 Xcode 创建一个基于 Swift 编程语言的 App 工程,并将它命名。

通过观察网易云音乐 App 的样式,从底部的 TabBar 即可看出它整体的 UI 框架是由 UITabbarController 和 UIViewController 组成的, 所以我们可以通过 StoryBoard 将我们的 App 的整体 UI 架构搭建起来;有的人可能会说我不会用 StoryBoard, 我用纯代码可以搭建吗?答案当然是可以的, 因为我的开发习惯就是简单的 UI 用 Storyboard 拖拖拽拽,复杂的 UI 用代码编写,这纯属于个人习惯,怎么适合自己怎么来就行。

使用 Storyboard 搭建的效果图如下:

在这里插入图片描述

构建首页发现视图

我们需要构建的页面是这样的:
在这里插入图片描述
通过上面展示的页面,我们可以发现网易云音乐的首页内容展示的数据非常的丰富,有搜索栏,有定时滚动的 Banner,有横向滚动的卡片视图,自身还支持 上拉刷新和下拉刷新,所以我们的首页可以采用 UITableView 来作为容器,然后在 Cell 上构建相应的子视图,例如 Banner, UICollectionView 等,来实现首页这一表视图。

通常我们在用 UITableView 加载数据的时候,数据的类型都是单一类似的,所以我们在构建 Cell 的时候,都是复用的同一个 Cell,类似手机通讯录一样。但是网易云音乐首页可不是那么回事了,它的每个 Cell 呈现的内容类型都是不同的,这就导致我们无法通过复用 Cell 的方式来呈现数据了, 那怎么样才能构建出正确的视图呢!

首先,我们先来确定问题。

你或许可以经常在别的项目中看到这样的代码,在 UITableView 中根据 index 来配置 UITableViewCell:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

   if indexPath.row == 0 {
        //configure cell type 1
   } else if indexPath.row == 1 {
        //configure cell type 2
   }
   ....
}

同样的在代理方法 didSelectRowAt 中使用同样的逻辑:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

if indexPath.row == 0 {
        //configure action when tap cell 1
   } else if indexPath.row == 1 {
        //configure action when tap cell 1
   }
   ....
}

那这么写有什么问题吗?

如果你的这个表视图是静态的,不存在重新排序或者在表视图里添加或删除 Cell,那么这样写一点问题也没有。直到你想对表视图进行上面所说的这些操作的时候,那么表视图的结构都将被你破坏,这就需要你手动去更新 cellForRowAt 和 didSelectRowAt 方法中所有的 index 了。

那有什么更好的办法吗?

在接下来的内容中,我会尽我所能与大家分享这个问题的解决思路。

MVVM

在这个项目中,我们将使用 MVVM 模式,MVVM 代表 Model-View-ViewModel, 这种模式的好处在于可以让视图与模型独立出来,降低耦合,从而来减轻 Controller 的体积。

Model

在上一篇文章中,我们已经确定了获取数据源的接口,接下来就是如何去请求数据了?

在这里我用到的网路请求库是一个第三方的开源库: Alamofire,简单的将它的请求接口封装一下,代码如下:

import UIKit
import Alamofire

enum MethodType {
    case get
    case post
}

enum NetworkError: Error {
    case invalidResponse
    case nilResponse
}

class NetworkManager<T: Codable> {
    // 网络请求
    static func requestData(_ type: MethodType,
                           URLString: String,
                           parameters: [String : Any]?,
                           completion: @escaping (Result<T, NetworkError>) -> Void) {

        let method = type == .get ? HTTPMethod.get : HTTPMethod.post

        AF.request(URLString, method: method, parameters: parameters, encoding: URLEncoding.httpBody)
            .validate()
            .responseDecodable(of: T.self) { response in
                if let value = response.value {
                    completion(.success(value))
                    return
                }

                if let error = response.error {
                    completion(.failure(.invalidResponse))
                    return
                }

                completion(.failure(.nilResponse))
        }
    }
}

请求返回的 JSON 数据格式如下:

{
    "code": 200,
    "data": {
        "cursor": null,
        "blocks": [
            {
                "blockCode": "HOMEPAGE_BANNER",
                "showType": "BANNER",
                "extInfo": {
                    "banners": [
                        {
                            "adLocation": null,
                            "monitorImpress": null,
                            "bannerId": "1622653251261138",
                            "extMonitor": 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值