swift版QQ音乐播放器(二)

一 完善部分的QQ音乐效果图

二 需要完善点

1 歌曲的切换和暂停播放

2 歌曲当前播放时间和歌曲总时间的更新

3 进度条的处理

4 歌手头像处理

5 头像动画效果

6 歌词的进度显示

8 完善细节

三 添加歌曲展示页面中的动画效果

1 代码书写位置 : 由于展示歌词的控制器的UITableViewController,那么我们可以使用代理方法.当用户拖动tableView的时候,会调用一个方法,在该方法中实现动画效果

2 思路 : 通过拿到第一个cell和最后一个cell来计算中间cell的索引值,然后遍历所有的索引,取出cell在分别计算各个cell的x值.就可以达到目的.

3 直接奉上代码 :

    //** 实现一种动画效果 */
    override func scrollViewDidScroll(scrollView: UIScrollView) {
        //获取指定的cell
        guard let indexRows = tableView.indexPathsForVisibleRows else {
            return
        }
        //** 获取中间的cell */
        let firstCell = indexRows.first?.row ?? 0
        let lastCell = indexRows.last?.row ?? 0

        //** 计算中间的cell */
        let middleCell = Int(Float(firstCell + lastCell) * 0.5)
        //** 遍历指定的所有的cell */
        for indexRow in indexRows {
            //取出cell
            let cell = tableView.cellForRowAtIndexPath(indexRow)
            cell?.x = CGFloat(abs(indexRow.row - middleCell) * 40)
            print(indexRow.row - middleCell)
        }
    }

四 处理音乐切换和暂停播放

1 通过在storyboard中自动布局,达到下面图中的效果(这里我就不一 一细说了,都是基本)

2 通过从storyboard拖线的方式我们可以拿到需要设置的东西

—-> 2.1 注意摆放位置 : 这里我们按照经常需要改变的属性和只改变一次属性的模式来摆放代码.

3 代码部分 :

—-> 3.1 只需要改变一次的属性(里面包含了上一篇中的部分属性)
    //** 歌曲名称 */
    @IBOutlet weak var nameLabel: UILabel!
    //** 歌手名称 */
    @IBOutlet weak var singerLabel: UILabel!
    //** 总音乐时间 */
    @IBOutlet weak var totalLabel: UILabel!
    //** 歌手背景图片 */
    @IBOutlet weak var singerImageView: UIImageView!
    //** 能让现实歌词的view拖动的scrollView */
    @IBOutlet weak var lrcSImageView: UIScrollView!
    //** 歌手专辑图片 */
    @IBOutlet weak var backImageView: UIImageView!
    //** 暂停或者播放 */
    @IBOutlet weak var playOrPause: UIButton!
—-> 3.2 经常需要改变的属性(里面包含了上一篇中的部分属性)
    //** 当前时间 */
    @IBOutlet weak var currentLabel: UILabel!
    //** 显示歌词的label */
    @IBOutlet weak var lrcLabel: UILabel!
    //** 滑块 */
    @IBOutlet weak var silderProgress: UISlider!

4 封装工具类(封装调用思想: 三层调用)

—-> 4.1 工具类一 : 对单首音乐的播放状态工具类(往里面添加继续播放;暂停播放的方法)
———-> 4.1.1 该部分代码 :
    //** 继续播放音乐 */
    func playMusic() {
        player?.play()
    }

    //** 暂停播放 */
    func pauseMusic() {
        player?.pause()
    }
—-> 4.2 工具类二 : 在负责音乐的业务逻辑工具类(获取最新音乐数据;重新播放音乐;暂停播放音乐;上一首和下一首)
———-> 4.2.1 记录播放歌曲的索引
    //** 创建音乐播放列表 (XFJQQMusicListItem: 音乐列表的模型)*/
    var musicList : [XFJQQMusicListItem]?
    //** 提供一个记录歌曲的索引 */
    var index = 0 {
        didSet {
            //判断
            if index < 0 {
                index = (musicList?.count ?? 0) - 1
            }
            if index > ((musicList?.count ?? 0) - 1){
                index = 0
            }
        }
    }
———-> 4.2.2 创建另外一个模型

———-> 4.2.3 问题 : 怎么才能当前创建的模型具有第一个模型中的属性呢?
———-> 4.2.4 解决 : 才用拥有的方法(让第一个模型作为第二个模型中的属性)
———-> 4.2.5 新创建的模型需要考虑到的属性 : 当前播放时间;歌曲总时间;播放状态
———-> 4.2.6 设计属性采用的方法 : 对当前时间和总歌曲时间提供get方法
———-> 4.2.7 具体代码如下 :

class XFJQQMusicNewMessageModel: NSObject {

//* 让该模型拥有显示歌单的模型 /

var QQMusicItem : XFJQQMusicListItem?

//* 当前播放的时间 /

var costTime : NSTimeInterval = 0

//* 总播放的时间 /

var totalTime : NSTimeInterval = 0

//** 提供当前时间和总时间的get方法 */
var costFormatTime : String {
    get{
        return XFJQQTimeTool.getTime(costTime)
    }
}

//** 提供总时间的get方法 */
var totalFormatTime : String {
    get{
        return XFJQQTimeTool.getTime(totalTime)
    }
}
//** 播放的状态 */
var isPlaying : Bool = false

}

—-> 4.3 在回到负责音乐业务类的工具类中.提供一个方法,负责返回最新的数据
    //** 设置一个方法用来提供最新的数据 */
    func getQQMusicNewMessage() ->XFJQQMusicNewMessageModel {
        //** 给暂时歌词中的模型属性赋值,保持最新的数据 */
        QQMusicMessageModel.QQMusicItem = musicList![index]
        //** 当前播放的进度 */
        QQMusicMessageModel.costTime = tool.player?.currentTime ?? 0
        //** 歌曲的总时长 */
        QQMusicMessageModel.totalTime = tool.player?.duration ?? 0
        //** 播放状态 */
        QQMusicMessageModel.isPlaying = tool.player?.playing ?? false
        //** 返回最新的数据模型 */
        return QQMusicMessageModel
    }

—-> 4.4 在该业务中提供一个方法对音乐的处理(判断音乐列表;记录当前播放音乐的索引)

    //设置一个方法对音乐的处理
    func playMusic(musicM : XFJQQMusicListItem) {
        //取出模型中的音乐文件名
        let fileName = musicM.filename ?? ""
        //传入音乐文件名播放音乐
        tool.playMusic(fileName)
        //** 判断如果列表中没有音乐就直接返回 */
        if musicList == nil {
            return
        }

        //** 并且记录当前的音乐索引 */
        index = (musicList?.indexOf(musicM))!
    }

—-> 4.5 在该类中在封装一层方法(方法内部分别调用在对单首音乐处理的方法中的继续播放和暂停播放的方法;上一首;下一首的方法)

—-> 4.5.1 重新播放和暂停播放代码 :

    //** 重新播放 */
    func playCurrentMusic() ->() {
        tool.playMusic()
    }
    //** 暂停播放 */
    func pauseCurrentMusic() {
        tool.pauseMusic()
    }

—-> 4.5.2 上一首和下一首代码 :

    //** 上一首 */
    func preMusic() {
        //索引递减
        index -= 1

        //判断
        if let muiscM = musicList {
            //取出模型
            let music = muiscM[index]
            //播放音乐
            playMusic(music)
        }
    }
    //** 下一首 */
    func nextMusic() {
        //索引增加
        index += 1
        //判断
        if let musicM = musicList {
            //取出模型
            let music = musicM[index]
            //播放
            playMusic(music)
        }
    }
—-> 4.6 工具类三 : 对音乐播放的时间和音乐总时间的处理(转化为具体的分钟和秒钟)
—-> 4.6.1 具体代码详解 :
class XFJQQTimeTool: NSObject {
    class func getTime(time : NSTimeInterval) ->String {
        let min = Int(time / 60)
        let sec = Int(time) % 60

        //** 转成字符串 */
        let resultStr = String(format: "%02d:%02d", min,sec)
        //** 返回结果 */
        return resultStr
    }
}
—-> 4.7 提供两个方法(分别用来作为该内部调用的 : 对经常执行的和不经常的方法分开存放 )—> 同样采用了分层存放的方法
—-> 4.7.1 在展示歌词的类扩展中设置两个方法(详见代码: 该部分上篇博客已经讲解了)
    //** 设置一次 */
    private func setUpOnce() {
        setUpViewOnce()
        setUpSilder()
    }
    //** 设置多次 */
    private func setUpTimes() {
        setUpViewFrame()
        setUpForeImage()
    }
—-> 4.8 根据从storyboard中得到的数据,采用最新创建的模型中的数据来对其赋值(只需要设置一次 : 详见代码)
    //** 只需要设置一次的数据 */
    private func setUpDataTime() ->() {
        let musicMessageModel = XFJQQMusicOperationTool.shareInstance.getQQMusicNewMessage()
        //** 歌曲名称 */
        nameLabel.text = musicMessageModel.QQMusicItem?.name
        //** 歌手背景图片 */
        singerImageView.image = UIImage(named: (musicMessageModel.QQMusicItem?.icon ?? ""))
        //** 歌手专辑图片 */
        backImageView.image = UIImage(named: (musicMessageModel.QQMusicItem?.icon)!)
        //** 歌手名称 */
        singerLabel.text = musicMessageModel.QQMusicItem?.singer
        //** 歌曲总时间 */
        totalLabel.text = "\(musicMessageModel.totalFormatTime)"
    }
—-> 4.9 根据从storyboard中得到的数据,采用最新创建的模型中的数据来对其赋值(只需要设置多次 : 详见代码)
    //** 设置多次的数据 */
    func setUpDatasTimes() {
        let musicMessageModel = XFJQQMusicOperationTool.shareInstance.getQQMusicNewMessage()
        //** 当前播放时间 */
        currentLabel.text = "\(musicMessageModel.costFormatTime)"
        //** 进度条 */
        silderProgress.value = Float(musicMessageModel.costTime / musicMessageModel.totalTime)

        //** 播放状态 */
        playOrPause.selected = musicMessageModel.isPlaying
    }
—-> 4.10 由于用户需要点击具体的按钮来切换相应的业务逻辑,那么我们也采取拖线的方式来达到效果
—-> 4.11 播放和暂停(用单例直接调用工具类中设置的方法)
    //** 播放和暂停 */
    @IBAction func playAndPause(button : UIButton)
    {
        //判断
        button.selected = !button.selected
        if button.selected {
            XFJQQMusicOperationTool.shareInstance.playCurrentMusic()
        }else{
            XFJQQMusicOperationTool.shareInstance.pauseCurrentMusic()
        }
    }
—-> 4.11 歌曲的切换(直接调用工具类中封装的方法和对属性进行设置的方法)
    //** 上一首 */
    @IBAction func preButton()
    {
        let operationTool = XFJQQMusicOperationTool.shareInstance

        //调用播放上一首的方法
        operationTool.preMusic()
        setUpDataTime()
    }

    //** 下一首 */
    @IBAction func nextButton()
    {
        let operationTool = XFJQQMusicOperationTool.shareInstance
        operationTool.nextMusic()
        setUpDataTime()
    }
—-> 4.12 退出(直接pop就可以)
    @IBAction func popVC()
    {
        navigationController?.popViewControllerAnimated(true)
    }
—-> 4.13 当前时间和歌曲总时间的处理(采用定时器)–>里面创建定时器和方法的调用我这里就不写出来了
    //** 设置定时器 */
    func addTimer() {
        //** 设置定时器 */
        timer = NSTimer(timeInterval: 1, target: self, selector: "setUpDatasTimes", userInfo: nil, repeats: true)
        //** 将定时器添加到主运行循环中 */
        NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSRunLoopCommonModes)
    }

    //** 移除定时器 */
    func removeTimer() ->() {
        timer?.invalidate()
        timer = nil
    }

五 歌手头像动画处理(头像旋转)

1 头像的暂停动画和恢复动画我们采用一个分类(下面的分类中的代码 : 暂时没研究是怎么实现的)

extension CALayer {
    // 暂停动画
    func pauseAnimate()
    {
        let pausedTime: CFTimeInterval = convertTime(CACurrentMediaTime(), fromLayer: nil)
        speed = 0.0;
        timeOffset = pausedTime;
    }

    // 恢复动画
    func resumeAnimate()
    {
        let pausedTime: CFTimeInterval = timeOffset
        speed = 1.0;
        timeOffset = 0.0;
        beginTime = 0.0;
        let timeSincePause: CFTimeInterval = convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime
        beginTime = timeSincePause;
    }
}

2 处理动画

—-> 2.1 通过在类扩展中提供三个方法(恢复动画;暂停动画;添加动画)
—-> 2.2 恢复动画和暂停动画
    //恢复动画
    private func resumeAnimation() {
        foreImageView.layer.resumeAnimate()
    }
    //暂停动画
    private func pauseAnimation() {
        foreImageView.layer.pauseAnimate()
    }
—-> 2.3 添加动画
    private func addAnimation() {
        //移除上移个动画
        foreImageView.layer.removeAnimationForKey("rotation")
        //添加动画
        let animation = CABasicAnimation(keyPath: "transform.rotation.z")
        //设置相关属性
        animation.duration = 30
        animation.fromValue = 0
        animation.toValue = M_PI * 2
        animation.repeatCount = MAXFLOAT

        //播放完成后不需要移除动画
        animation.removedOnCompletion = false
        //添加动画
        foreImageView.layer.addAnimation(animation, forKey: "rotation")
    }

3 动画调用 :

—-> 3.1 用户点击播放和暂停按钮的时候,需要调用
    //** 播放和暂停 */
    @IBAction func playAndPause(button : UIButton)
    {
        button.selected = !button.selected
        //判断
        if button.selected {
            XFJQQMusicOperationTool.shareInstance.pauseCurrentMusic()
            pauseAnimation()
        }else {
            XFJQQMusicOperationTool.shareInstance.playCurrentMusic()
            resumeAnimation()
        }
    }
—-> 3.2 当用户点击显示歌曲的tableView中的cell,页面开始跳转后需要动画效果.(只需要设置一次)
        //将歌词的数据源交给展示歌曲的控制器来展示
        lrcTVC.dataSource = lrcMs
        //切换歌曲开始动画
        addAnimation()

        //判断如果是选择暂停和开始的属性做相应的动画
        if musicMessageModel.isPlaying {
            resumeAnimation()
        }else {
            pauseAnimation()
        }        

六 歌词的展示(比较有难度)

1 封装处理歌词的工具类.(封装原因 : 展示出来的数据并不符合格式,需要我们做处理)–>下图展示的是需要处理的歌词数据

2 创建歌词展示的模型(模型中需要的属性 : 歌曲开始时间;歌曲结束时间;歌词内容)

class XFJQQLrcModel: NSObject {

    //** 歌词演唱的开始时间 */
    var beginTime : NSTimeInterval = 0
    //** 歌词演唱的结束时间 */
    var endTime : NSTimeInterval = 0
    //** 歌词的内容 */
    var lrcStr : String = ""
}

3 处理歌词工具类的文件

4 在该工具类中创建一个方法能返回具体哪行返回的歌词(采用元组的方法)

    //提供一个方法对某一行歌词的返回(需要传入当前播放的时间进度和模型中的开始时间,结束时间)
    //采用元组的方式来达到读取歌词的经度
    class func getLrcMusicRow(current : NSTimeInterval, lrcModel : [XFJQQLrcModel]) ->(row :Int, lrcM : XFJQQLrcModel){
        //取出模型的个数
        let count = lrcModel.count
        //遍历
        for i in 0..<count {
            let lrcM = lrcModel[i]
            //判断时间区间
            if lrcM.beginTime < current && lrcM.endTime > current {

                return (i, lrcM)
            }
        }
        return (0, XFJQQLrcModel())
    }

5 设计一个方法: 当外界调用的时候传入首歌曲的歌词,然后经过该方法,返回一个解析好的可实现的歌词.

—-> 5.1 处理该部分的歌词 :
[ti:]
[ar:]
[al:]
—-> 5.2 代码(类方法)–> 里面包括 : 异常处理;将歌词转换为一行一行的显示;过滤掉不能处理的符号;装入模型中(该方法的上部分)
    //设计一个方法用来处理歌词
    class func getLrcMusicData(fileName : String?) ->[XFJQQLrcModel] {
        //获取歌词文件的路径
        guard let path = NSBundle.mainBundle().pathForResource(fileName, ofType: nil) else{
            return [XFJQQLrcModel]()
        }
        //加载文件的内容
        var contentLrc = ""
        do {
            contentLrc = try String(contentsOfFile: path)
        }catch {
            print(error)
            return [XFJQQLrcModel]()
        }

        //将歌词转成一行一行组成的数组
        let lrcArray = contentLrc.componentsSeparatedByString("\n")

        //创建一个装XFJQQLrcModel这种类型的数组
        var lrcMs = [XFJQQLrcModel]()

        //遍历歌词的组成的数组,然后放入lrcMs数组中
        for lrcmStr in lrcArray {
            //过滤掉垃圾数据
            if lrcmStr.containsString("[ti:") || lrcmStr.containsString("[ar:") || lrcmStr.containsString("[al:") {
                continue
            }
            //装入上面的数组中
            let lrcm = XFJQQLrcModel()
            lrcMs.append(lrcm)

6 通过调用处理好的歌词的方法来处理过滤好的歌词

[00:00.89]传奇
[00:02.34]作词:刘兵
[00:03.82]作曲:李健
[00:05.48]演唱:王菲
—-> 6.1 代码 :
            //拿到的数据才是真正能解析的数据
            //替换
            let resultStr = lrcmStr.stringByReplacingOccurrencesOfString("[", withString: "")
            //开始解析
            let timeAndContent = resultStr.componentsSeparatedByString("]")
            if timeAndContent.count == 2 {
                let time = timeAndContent[0]
                lrcm.beginTime = XFJQQTimeTool.getLrcFormatTime(time)
                let content = timeAndContent[1]
                lrcm.lrcStr = content
            }
        }

        //遍历所有的数组中的时间
        let count = lrcMs.count
        for i in 0..<count {

            if i != count - 1 {
                lrcMs[i].endTime = lrcMs[i + 1].beginTime
            }
        }
        //返回处理好的时间和歌词
        return lrcMs
    }
}
—-> 6.2 对能处理的歌词方法的调用
    //** 该方法是对歌词的开唱时间和结束时间的处理(根据传入的字符串,返回一个确切的时间) */
    class func getLrcFormatTime(time : String) ->NSTimeInterval {
        //根据":"取出时间
        let minAndSec = time.componentsSeparatedByString(":")
        //判断
        if minAndSec.count == 2 {
            //分钟
            let min = NSTimeInterval(minAndSec[0]) ?? 0
            //秒钟
            let sec = NSTimeInterval(minAndSec[1]) ?? 0
            //返回处理好的时间
            return min * 60 + sec
        }
        //如果走到这里,就直接返回0
        return 0
    }

7 将处理好的歌词交给显示歌词的tableView来显示

        //获取歌词数据源
        let lrcMs = XFJQQMusicLrcDataTool.getLrcMusicData(musicMessageModel.musicModel?.lrcname)
        //打印出歌词
        print(lrcMs)

        //将歌词的数据源交给展示歌曲的控制器来展示
        lrcTVC.dataSource = lrcMs

8 之间用来显示歌词的是UIView,现在我们使用UITableView来显示,将使用到UIView显示歌词的地方全都用tableView来代替.

9 实现tableView中数据源发方法

///MARK : - 数据源方法
extension XFJQQLrcTVC {
    //组
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    //每组的cell
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //由模型数据决定
        return dataSource.count
    }
    //每行cell的内容
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        //用该方法得到cell
        let cell = XFJQQMusicLrcCell.getMusicLrcCell(tableView)
        //判断如果当前处在最中间的是正在播放的行数,那么久赋值,否则就赋值为0
        if indexPath.row == scrollRow {
            cell.progress = progress
        }else {
            cell.progress = 0.0
        }
        //取出模型
        let lrcStr = dataSource[indexPath.row]
        //赋值
        cell.lrcStr = lrcStr.lrcStr
        return cell
    }
}

10 自定义cell和自定义显示歌词的label

—-> 10.1 自定义cell(该部分是对cell中label的显示)
    @IBOutlet weak var lrcLabel: XFJQQMusicLrcLabel!

    //提供一个属性给展示歌词的view中的歌词状态
    var progress : Double = 0.0 {
        didSet {
             lrcLabel.progress = progress
        }
    }

    var lrcStr : String = "" {
        didSet {
            lrcLabel.text = lrcStr
        }
    }
}
—-> 10.2 返回创建好的cell
///MARK : - 再累扩展中提供一个方法,返回cell
extension XFJQQMusicLrcCell {
    class func getMusicLrcCell(tableView : UITableView) ->XFJQQMusicLrcCell {
        let lrcCellID = "lrcCell"
        var cell = tableView.dequeueReusableCellWithIdentifier(lrcCellID) as? XFJQQMusicLrcCell
        if cell == nil {
            cell = NSBundle.mainBundle().loadNibNamed("XFJQQMusicLrcCell", owner: nil, options: nil).first as? XFJQQMusicLrcCell
        }
        return cell!
    }
}
—-> 10.3 自定义label
class XFJQQMusicLrcLabel: UILabel {

    //自定义更新歌词的进度(提供set方法)
    var progress : Double = 0.0 {
        didSet {
            //重绘
            setNeedsDisplay()
        }
    }
    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        //设置填充颜色
        UIColor.greenColor().set()
        let progressloat = CGFloat(progress)

        let rect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width * progressloat, rect.size.height)

        UIRectFillUsingBlendMode(rect, CGBlendMode.SourceIn)
    }
}

11 当有歌词歌曲变化的时候,立刻更新歌词的数据

    var dataSource : [XFJQQLrcModel] = [XFJQQLrcModel]() {

        didSet {
            tableView.reloadData()
        }
    }

12 在显示歌词的tableView控制器中设置一个属性用来提供正在歌唱的歌词始终处于tableView的中间cell

    //设置歌词滚动到中间(set方法)
    var scrollRow : Int = 0 {
        didSet {
            //判断防止重复滚动(用旧值作为判断)
            if scrollRow != oldValue {

                tableView.reloadRowsAtIndexPaths(tableView.indexPathsForVisibleRows!, withRowAnimation: UITableViewRowAnimation.Fade)
                let indexPath = NSIndexPath(forRow: scrollRow, inSection: 0)
                //拿到角标滚动到哪个位置
                tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Middle, animated: true)
                //刷新(如果先做动画再刷新的话,动画还没做完就直接刷新了,会出现显示歌词的view跳动,所以需要先刷新再动画)
//                tableView.reloadRowsAtIndexPaths(tableView.indexPathsForVisibleRows!, withRowAnimation: UITableViewRowAnimation.Fade)

            }
        }
    }

13 给正在歌唱的cell中的歌词设置进度(set方法)

    var progress : Double = 0.0 {
        didSet {
            //获取当前正在行号
            let indexPath = NSIndexPath(forRow: scrollRow, inSection: 0)
            //将当前的cell类型转换
            let cell = tableView.cellForRowAtIndexPath(indexPath) as? XFJQQMusicLrcCell
            //给cell中progress赋值
            cell?.progress = progress
        }
    }

14 处理已进入展示歌词的页面,歌词的头部位置处于tableView的中间部位

    //在view将要布局的时候对显示歌词的view布局,让其头部显示在中间
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        tableView.contentInset = UIEdgeInsets(top: tableView.height * 0.5, left: 0, bottom: tableView.height * 0.5, right: 0)
    }

15 更新滚动时候的歌词(该方法调用频繁)

    //更新滚动的时候歌词(调用频率频繁)
     func updateLrc () ->() {
        //获取歌曲的最新值
        let musicMessageModel = XFJQQMusicOperationTool.shareInstance.getUpNewMusicMessage()
        //获取最新歌词的行号
        let rowLrm = XFJQQMusicLrcDataTool.getLrcMusicRow(musicMessageModel.costTime, lrcModel:lrcTVC.dataSource)
        let row = rowLrm.row
        //赋值
        lrcTVC.scrollRow = row

        //给歌词标签展示歌词
        let lrcm = rowLrm.lrcM
        lrcLabel.text = lrcm.lrcStr
        //歌词进度(当前的进度除以在总进度)
        let value = (musicMessageModel.costTime - lrcm.beginTime) / (lrcm.endTime - lrcm.beginTime)
        //赋值

        lrcLabel.progress = value

        //给展示歌词的view赋值
        lrcTVC.progress = value

16 怎么才能让歌词滚动呢? 通过系统的一个类CADisplayLink(和定时器作用差不多,这里我代码就不写上了)

七 注意点和性能优化

1 注意 : 退出后台动画不再旋转—> 解决方案 : 动画完成之后,不需要移除

2 怎么样将歌词分割成一行一行,然后展示呢?—>解决方案 : 通过设置一个歌词模型来展示

3 歌词不滚动?—> 解决方案 : CADisplayLink(类似定时器)

4 歌词显示的时候出现了刷新问题?—> 解决方案 : 先刷新,在滚动

5 性能优化 : 图片绘制的次数太多?

—-> 5.1 解决方案 : 通过设置一个属性记录歌词的行值,然后判断是否是同一行

6 当前显示的时间一直在更新,暂停的时候不需要更新,也可以优化(留给你们实现了)

八 功能补充(额外的想法,还未实现)

1 拖动进度条达到快进的目的

2 后台模式(后面会抽时间补上)

3 摇一摇切换歌曲

九 总结

1 该篇博客写的思路还算清晰,唯一的缺点是我无法在后面歌词显示部分更加详细点说明,因为该处真是没办法说明了,只能奉上我写的代码了.

2 本篇博客是基于QQ音乐的swift版本,还有很多的功能没有实现,希望后面我能抽出时间进一步的完善也希望阅读过我博客的程序猿能提供意见.

3 最后,由于时间一直没有抽出来,这使得博客更新的速度有点慢,后续我会慢慢补上.如果大家觉的我写的还可以,麻烦大家关注我的官方博客,谢谢!!!!

时间: 2024-10-12 07:16:01

swift版QQ音乐播放器(二)的相关文章

swift版QQ音乐播放器(一)

一 部分功能图(后面会完善) 二 讲解思路 1 项目目录结构搭建 2 抽取工具类 3 自定义cell 4 分层思想 5 业务逻辑 三 项目目录搭建和相关配置 1 采用搭建搭建结构思路 : MCV模式 --> 1.1 文件夹图片 : 2 注意 一 : 当我们在创建目录的时候,直接将info.plist文件拖入到System的时候,编译的时候,会报错.原因是找不到info.plist文件. --> 2.1 处理方式 : 直接找到工程文件,然后找到General,里面会有一个现实info的按钮,点击

基于jQuery仿QQ音乐播放器网页版代码

基于jQuery仿QQ音乐播放器网页版代码是一款黑色样式风格的网页QQ音乐播放器样式代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div class="m_player" id="divplayer" role="application" onselectstart="return false" style="left: 0px;"> <div class=&

jQuery仿QQ音乐播放器

本文通过Html+CSS+jQuery开发仿QQ版的音乐播放器,是前端技术的综合应用,所用素材来源于网络,仅供学习分享使用,如有不足之处,还请指正. 涉及知识点 在本例中用到的知识点如下,按jQuery和CSS进行区分: jQuery 是一个 JavaScript 库, 极大地简化了 JavaScript 编程,常见知识点如下: 通过标签获取jQuery对象:var $audio =$("audio"); 通过选择符获取jQuery对象并设置文本内容:$(".music_pr

如何写一个正经的Android音乐播放器 二

这一篇讲如何 与音乐播放Service交互 稍有经验的同学都知道,将长时间的操作放在Service中进行,如何做到界面和音乐播放Service的有效沟通呢,在这一章中给出我的答案,同样希望大神们给出指点. 希望你阅读(自行翻墙): Service的API Guide:http://developer.android.com/guide/components/services.html Service的API Guide中的有关bound service中的部分: http://developer

android版音乐播放器----卡拉OK歌词实现(二)

相信大家都看过酷狗的音乐播放器,特别是酷狗的滚动歌词实现起来,效果特别好.我自己也尝试看了一下酷狗的歌词文件,发现是加密的,根本就看不了,所以只能是从目前开源的歌词文件出发了.我自己选择的是ksc歌词文件. 要实现歌词的功能,那首先就是要先认识一下ksc歌词的文件与普通的lrc歌词有那方面的区别? 当然,这里我从网上找了一个文档,看了文档里面的介绍,大家大概就会明白两都的区别. http://wenku.baidu.com/link?url=Ga4ESBrytlx3qUoxX5ApHbFIZro

swift 音乐播放器项目-《lxy的杰伦情歌》开发实战演练

最近准备将项目转化为OC与swift混合开发,试着写一个swift音乐播放器的demo,体会到了swift相对OC的优势所在,废话不多说,先上效果图: ps:身为杰伦的铁粉,demo的主题必须跟杰伦有关,哈哈!而且自我感觉我有转型UI的天赋,有木有? 一.导入OC文件 创建好swift项目之后,导入OC工具类文件,Xcode会自动生成桥接文件 打开这个文件,在开头导入OC工具类的头文件,就可以调用OC工具类了 // // Use this file to import your target's

Qt版音乐播放器

    Qt版音乐播放器 转载请标明出处:牟尼的专栏 http://blog.csdn.net/u012027907 一.关于Qt 1.1 什么是Qt Qt是一个跨平台应用程序和UI开发框架.使用Qt只需一次性开发应用程序,无需重新编写源代码,便可跨不同桌面和嵌入式操作系统部署这些应用程序. Qt Creator是全新的跨平台Qt IDE,可单独使用,也可与Qt库和开发工具组成一套完整的SDK,其中包括:高级C++代码编辑器,项目和集成管理工具,集成的上下文相关的帮助系统,图形化调试器,代码管理

仿酷狗音乐播放器开发日志二十一 开发动态调色板控件(附源代码)

转载请说明原出处,谢谢~~ 上一篇仿酷狗日志结束后,整个换肤功能就仅仅剩下调色板功能没有做了.我本以为会非常easy.可是研究了酷狗的调色板功能后发现不是那么简单的事情.首先看一下酷狗的调色板的样子: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemh1aG9uZ3NodQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" > waterm

Android 实现简单音乐播放器(二)

在Android 实现简单音乐播放器(一)中,我介绍了MusicPlayer的页面设计. 现在,我将解析MusicPlayer的功能实现,就讲一些主要的点和有趣的细节,结合MainActivity.java代码进行说明(写出来可能有点碎……一向不太会总结^·^). 一.功能菜单 在MusicPlayer中,我添加了三个菜单: search(搜索手机中的音乐文件,更新播放列表). clear(清除播放列表……这个功能是最初加进去的,后来改进之后,已经没什么实际意义). exit(退出). menu