本文主要分享下楼主在学习Swift编程过程中,对GitHub上的一个开源App Swift Music的研究心得。
项目地址:https://github.com/xujiyao123/SwiftMusic
一、项目简介
本项目主要实现了歌曲关键字查询歌曲,通过使用
http://v3.kaolafm.com/api/play/(musicId)
这两个API获取歌曲详细信息并进行播放。并通过内置URL访问并播放指定视频内容。
二、项目结构分析
楼主之所以选择分析研究这个项目是因为在学习过程中发现这个项目是一个比较典型的MVC模型,并且所有View部分都是通过代码实现的而非storyboard,具有一定的学习价值。接下来,本文将按照Model、View、Control,三个部分详细分析这个项目是如何通过MVC模型来实现查询歌曲、获取歌曲、播放歌曲及视频功能的。
1、 Model(模型)
在Model部分中,作者一共提供了4个文件。
videos.plist是一个存有所能播放的视频数据的plist文件。
在WebImageCategory.swift中,作者拓展了UIIMageView这个class,加入了一个通过URL获取图片并加载的方法 setWebImageWithUrlStr(urlStr:String?) -> Void。
在MusicModel.swift中,作者定义了两个class,分别是:MusicModel和MusicPlayModel。
MusicModel类是用于建立关键字搜索到的歌曲的对象。其中包含了歌曲的5个属性,并定义了一个类方法loadMusicListDataWithClouse(searchStr:String ,page:Int , clouse:(data:NSMutableArray , haveNext:Int) -> Void) -> Void 通过传入歌曲关键字及搜索页数来获取歌曲的详细信息分别创建MusicModel对象并将其装入一个NSMutableArray中,将所得NSMutableArray传给方法参数clouse以便进行下一步操作。通过引入方法参数clouse(clousure),作者很巧妙的将数据读取并进行封装与对数据之后的操作进行了分离。
MusicPlayModel类则是用于建立通过musicID获取到的歌曲的对象。其中包含了title,pic,mp3Url三个属性,并定义类方法loadMusicDataWithClouse(musicID:String ,clouse:(data:NSMutableArray) -> Void) ->Void 通过传入的musicID,获取歌曲信息并建立MusicPlayModel对象并将去装入NSMutableArray传递给方法参数clouse(clousure)以便进行下一步操作。
在VideoModel.swift中,作者定义了一个VideoModel类,也是运用了痛MusicModel类似的手段对视频信息数据进行处理并传递给clouse。与MusicModel不同的是,在loadVideoDataWithClouse(clouse:(dataArr:NSMutableArray) -> Void) -> Void 中作者通过dispatch_async添加一个异步任务来处理videos.plist里的视频数据。
通过对Model中的4个文件的分析,不难发现,作者在Model部分定义了数据的模型并通过闭包(clousure)的方式接受后续对数据的操作,以达到将Model与其余部分分离的目的。由此可见,使用方法参数(闭包clousure)是一个实现MVC很好的办法。
2、View(视图)
View部分一共包含了2个文件。
在XUTableViewCell.swift文件中作者创建了UITableViewCell并定义了其显示内容及将VideoModel与MusicModel对象填充入Cell的方法。
在MusicPlayerManager.swift中首先定义了协议MusicPlayDelegate,其中包含了4个方法。在MusicPlayManager类中,定义了协议代理delegate,player: AVPlayer! 及var timer:NSTimer!。通过类变量shared获得一个MusicPlayerManager()。方法playWithModel(model:MusicPlayModel) 用于播放音乐并使用timer每隔1s出发playing方法。playing() 方法用于判断播放是否结束,并使用代理调用协议方法playManager(manager:MusicPlayerManager ,BeginToPlayProgress progress:Double) 或 playManagerplayIsEnd(manager:MusicPlayerManager) 并将自身作为参数传递给协议方法。pause() 与 goon() 分别是控制player暂停与播放,并将自身作为参数用代理调用相关协议方法。changeValueWithProgress(value:Float64) ->Void 通过参数的传入来控制player的播放进度。
作者主要在View部分定义了UITableViewCell及MusicPlayerManager并通过使用代理协议的方法,将player本身与其余部分程序进行了分离。在MVC模型中,使用代理来传递值及调用协议方法也是一个很好的办法。
3、 Controller
在Controller中,作者给出了4个文件分别对应了本项目中的4个界面。
RootViewController.swift对应的是项目的主菜单。RootViewController.swift中,设定了页面的背景颜色,定义并实例了UINavigationController()及两个UIButton(),并为两个UIButton定义了触发方法用于跳转页面至按钮分别对应的音乐和电影界面。
RootViewController.swift对应的是视频界面。这之中定义并实例了UITableViewController,通过调用VideoModel.loadVideoDataWithClouse(clouse:(dataArr:NSMutableArray) -> Void) -> Void方法获取视频数据并通过方法drawCellWithModel(model:VideoModel) ->Void将数据载入cell并填充如UITableViewController。同时定义了tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) 用于实现播放视频的功能。
MusicListViewController.swift对应着音乐搜索界面,同视频界面相似的定义了UITableViewController并调用cell.drawMusicWithModel(model:MusicModel) ->Void进行填充。与视频界面不同的是,在音乐搜索界面中作者定义并实例了textFild() 用于输入搜索关键字,并定义loadData(page:Int?) ->Void 方法调用MusicModel.loadMusicListDataWithClouse(searchStr:String ,page:Int , clouse:(data:NSMutableArray , haveNext:Int) -> Void) -> Void 获取音乐列表。同时作者还定义了select cell方法用于跳转至音乐播放界面。调用ZLSwifthRefresh 中方法实现列表下拉刷新的功能。
在MusicViewController.swift中作者实例了两个UIButton用于播放和暂停,一个UIImage用于显示歌曲图片并对UIImage进行了圆角和旋转处理(旋转动画+NSTimer触发器),一个UISlider()用于显示及控制播放进度。通过使用MusicPlayModel.loadMusicDataWithClouse(musicID:String ,clouse:(data:NSMutableArray) -> Void) ->Void 方法获取播放歌曲的数据及信息。使用MusicPlayerManager.shared属性获得MusicPlayerManager(),调用MusicPlayerManager()中的方法达到控制播放器播放暂停的效果,并获得协议代理,同时实现代理中的4个方法。
作者在Controller的4个文件中分别将4个页面中所包含的各个控制器进行了定义及实例。通过不同的指令调用Model中的方法获得数据,并将数据传递给View中的Cell和Player进行显示及播放。
三、项目音乐部分运行流程图
四、项目可改进部分
1、项目在URL通信部分中还在使用NSURLConnection而并未使用功能更加完善的NSURLSession。
2、项目UI及功能较为简单。
3、虽然定义了MusicPlayerDelegate协议,但在实际实现过程中并未赋予其实用功能。View与Controller间交互也是通过传递MusicPlayerManager()来完成的。
五、总结
本项目虽然在功能上较为简单,但是在程序设计的结构上很好的遵循MVC模型,并通过clousure,代理协议,对象传递等方式来达成Model、View和Controller三者之间的交互。这是值得学习的。作者完全使用代码对界面进行设计,也是值得借鉴的,毕竟有的时候用代码设计界面比storyboard会来的更为方便。