博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Swift版音乐播放器(简化版),swift音乐播放器
阅读量:5152 次
发布时间:2019-06-13

本文共 19921 字,大约阅读时间需要 66 分钟。

这几天闲着也是闲着,学习一下Swift的,于是到开源社区Download了个OC版的音乐播放器,练练手,在这里发扬开源精神,

希望对大家有帮助!

这个DEMO里,使用到了

AudioPlayer(对音频封装的库)

FreeStreamer(老外写的音频高效处理库)

LKDBHelper(将数据模型直接写到数据库中的库)

AFNetworking (网络库)

SDWebImage (图片获取库)

另外,我也把OC版的ProgressHUD转成了Swift版本的HYBProgressHUD,希望对大家有用啊!

目前只实现了这几个简单的功能,希望有时间且爱研究的同学,追加更多的功能再开源出来哦!

下面我说一下封装的网络请求类:

import Foundation/// 请求成功与失败的回调typealias requestSuccessCloser = (responseObject: AnyObject?) ->Voidtypealias failCloser = (error: NSError?) ->Void////// 描述:网络请求基础类,所有GET请求方式都是以GET开头的类方法,POST请求方式会以POST开头命名类方法////// 作者:huangyibiaoclass HYBBaseRequest: NSObject {    struct BaseURL {        static var baseURL: String = kServerBase    }        ///    /// 描述:解析JSON数据    ///    /// 参数:jsonObject 网络请求获取下来数据    ///    /// 返回:如果解析成功,返回字典,否则返回nil    class func parseJSON(#jsonObject: AnyObject?) ->NSDictionary? {        if let result = jsonObject as? NSDictionary {            return result        }        return nil    }        ///    /// 描述: GET请求方式    ///    /// 参数: serverPath --请求路径,不包含基础路径    ///       success    --请求成功时的回调闭包    ///       fail       --请求失败时的回调闭包    ///    /// 返回: AFHTTPRequestOperation类型对象,外部可以通过引用此对象实例,在需要取消请求时,调用cancel()方法    class func GETRequest(serverPath: String, success: requestSuccessCloser, fail: failCloser) ->AFHTTPRequestOperation {        var op =  manager().GET(serverPath, parameters: nil, success: { (op, responseObject) -> Void in            success(responseObject: responseObject)            }, failure: { (op, error) -> Void in                fail(error: error)        })        return op    }        class func downloadFile(serverPath: String, success: requestSuccessCloser, fail: failCloser) ->AFHTTPRequestOperation {        var op =  AFHTTPRequestOperation(request: NSURLRequest(URL: NSURL(string: String(format: "%@%@", kServeBase1, serverPath))))        op.setCompletionBlockWithSuccess({ (requestOp, responseObject) -> Void in            success(responseObject: responseObject)        }, failure: { (requestOP, error) -> Void in            fail(error: error)        })        op.start()        return op    }        ///    /// 私有方法区    ///    private  class func manager() ->AFHTTPRequestOperationManager {        var manager = AFHTTPRequestOperationManager(baseURL: NSURL(string: BaseURL.baseURL))                manager.requestSerializer.setValue("application/json", forHTTPHeaderField: "Accept")        manager.requestSerializer.setValue("application/json", forHTTPHeaderField: "content-type")        manager.requestSerializer.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Accept")        manager.requestSerializer.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")        // 设置响应头支持的格式        manager.responseSerializer.acceptableContentTypes = NSSet(array: ["application/json", "application/javascript", "application/lrc", "application/x-www-form-urlencoded"])        return manager    }}

因为资源类型不同,所以要在请求头添加支持的格式才能访问到资源哦。

下面是封装歌词显示的UI,这里没有细化对时间的把握,只是粗略实现功能,有时间的同学可以对播放进度把握得更好!

import Foundation////// 描述: 显示歌词控件////// 作者: huangyibiaoclass HYBSongLRCView: UIView {    private var scrollView: UIScrollView!    private var keyArray = NSMutableArray()    private var titleArray = NSMutableArray()    private var lineLabelArray = NSMutableArray()    private var currentPlayingLineTime: float_t = 0.0        ///    /// 重写父类的方法    ///    override init(frame: CGRect) {        super.init(frame: frame)                self.scrollView = UIScrollView(frame: CGRectMake(0, 10, self.width(), self.height() - 20))        // 暂时关闭可交互功能        self.scrollView.userInteractionEnabled = false        self.addSubview(self.scrollView)    }        required init(coder aDecoder: NSCoder) {        fatalError("init(coder:) has not been implemented")    }        ///    /// public方法区    ///        ///    /// 描述:解析歌词    ///    /// 参数:lrcPath LRC歌词的路径    func parseSong(lrcPath: String) {        self.keyArray.removeAllObjects()        self.titleArray.removeAllObjects()                var content = NSString(contentsOfFile: lrcPath, encoding: NSUTF8StringEncoding, error: nil)        var array = content.componentsSeparatedByString("\n")        // 解析每一行        for line in array {            if let lrcLine = line as? NSString {                if lrcLine.length != 0 {                    self.parseLRCLine(lrcLine)                }            }        }                self.bubbleSortLrcLines(self.keyArray)                self.scrollView.contentOffset = CGPointZero        self.scrollView.contentSize = CGSizeMake(scrollView.width(), CGFloat(keyArray.count * 25))        self.configureLRCLineLabels()    }        ///    /// 描述:移除显示歌词的标签    func removeAllSubviewsInScrollView() {        for subview in self.scrollView.subviews {            subview.removeFromSuperview()        }                self.lineLabelArray.removeAllObjects()    }        ///    /// 描述:移除之前的歌词数据    func clearLRCContents() {        self.keyArray.removeAllObjects()        self.titleArray.removeAllObjects()    }        ///    /// 描述:指定歌词播放的时间,会根据时间滚动到对应的歌词行    ///    /// 参数:time 歌词行播放的时间    func moveToLRCLine(#time: NSString) {        if self.keyArray.count != 0 {            var currentTimeValue = self.timeToFloat(time)                        var index = 0            var hasFound = false            for index = 0; index < self.keyArray.count; index++ {                if let lrcTime = self.keyArray[index] as? NSString {                    var tmpTimeValue = self.timeToFloat(lrcTime)                    if fabs(tmpTimeValue - currentTimeValue) <= fabs(0.000000001) {                        hasFound = true                        currentPlayingLineTime = tmpTimeValue                        break                    }                }            }                        if hasFound || (!hasFound && currentPlayingLineTime < currentTimeValue) {                if index < self.lineLabelArray.count {                    if let label = self.lineLabelArray[index] as? UILabel {                        updateCurrentTimeLRC(label)                        self.scrollView.setContentOffset(CGPointMake(0.0, 25.0 * CGFloat(index)),                            animated: true)                    }                }            }        }    }        ///    /// private方法区    ///        ///    /// 描述:解析歌词行    ///    /// 参数:lrcLine 该行歌词    private func parseLRCLine(lrcLine: NSString) {        if lrcLine.length == 0 {            return        }                var array = lrcLine.componentsSeparatedByString("\n")        for var i = 0; i < array.count; i++ {            var tempString = array[i] as NSString            var lineArray = tempString.componentsSeparatedByString("]")                        for var j = 0; j < lineArray.count - 1; j++ {                var line = lineArray[j] as NSString                                if line.length > 8 {                    var str1 = tempString.substringWithRange(NSMakeRange(3, 1))                    var str2 = tempString.substringWithRange(NSMakeRange(6, 1))                                        if str1 == ":" && str2 == "." {                        var lrc = lineArray.last as NSString                        var time = lineArray[j].substringWithRange(NSMakeRange(1, 8)) as NSString                        // 时间作为KEY                        self.keyArray.addObject(time.substringToIndex(5))                        // 歌词会为值                        self.titleArray.addObject(lrc)                    }                }            }        }    }        ///    /// 描述:对所有歌词行进行冒泡排序    ///    /// 参数:array 要进行冒泡排序的数组    private func bubbleSortLrcLines(array: NSMutableArray) {        for var i = 0; i < array.count; i++ {            var firstValue = self.timeToFloat(array[i] as NSString)                        for var j = i + 1; j < array.count; j++ {                var secondValue = self.timeToFloat(self.keyArray[j] as NSString)                                if firstValue > secondValue {                    array.exchangeObjectAtIndex(i, withObjectAtIndex: j)                    self.titleArray.exchangeObjectAtIndex(i, withObjectAtIndex: j)                }                            }        }    }        ///    /// 描述:把时间字符串转换成浮点值    ///    /// 参数:time 时间字符串,格式为:"05:11"    private func timeToFloat(time: NSString) ->float_t {        var array = time.componentsSeparatedByString(":")                var result: NSString = "\(array[0])"        if array.count >= 2 {            result = "\(array[0]).\(array[1])"        }        return result.floatValue    }        ///    /// 描述:创建显示歌词的标签    private func configureLRCLineLabels() {        self.removeAllSubviewsInScrollView()                for var i = 0; i < titleArray.count; i++ {            var title = titleArray[i] as String            var label = UIMaker.label(CGRectMake(0.0,                25.0 * CGFloat(i) + scrollView.height() / 2.0,                scrollView.width(),                25.0),                title: title)            label.textColor = UIColor.lightGrayColor()            label.font = UIFont.systemFontOfSize(14.0)                        scrollView.addSubview(label)            lineLabelArray.addObject(label)        }    }        ///    /// 描述:更新当前显示的歌词    private func updateCurrentTimeLRC(currentLabel: UILabel) {        for label in self.lineLabelArray {            if let item = label as? UILabel {                if item == currentLabel {                    item.textColor = kNavColor                    item.font = UIFont.boldSystemFontOfSize(16.0)                } else {                    item.textColor = UIColor.lightGrayColor()                    item.font = UIFont.systemFontOfSize(14.0)                }            }        }    }}

Swift版的HYBProgressHUD控件,调用方式是非常简单的,使用的都是公开的类方法调用方式:

import Foundationimport UIKit////// @brief 样式enum HYBProgressHUDStyle {    case BlackHUDStyle /// 黑色风格    case WhiteHUDStyle /// 白色风格}////// @brief 定制显示通知的视图HUD/// @author huangyibiaoclass HYBProgressHUD: UIView {    var hud: UIToolbar?    var spinner: UIActivityIndicatorView?    var imageView: UIImageView?    var titleLabel: UILabel?        ///    /// private 属性    ///    private let statusFont = UIFont.boldSystemFontOfSize(16.0)    private var statusColor: UIColor!    private var spinnerColor: UIColor!    private var bgColor: UIColor!    private var successImage: UIImage!    private var errorImage: UIImage!        ///    /// @brief 单例方法,只允许内部调用    private class func sharedInstance() ->HYBProgressHUD {        struct Instance {            static var onceToken: dispatch_once_t = 0            static var instance: HYBProgressHUD?        }                dispatch_once(&Instance.onceToken, { () -> Void in            Instance.instance = HYBProgressHUD(frame: UIScreen.mainScreen().bounds)            Instance.instance?.setStyle(HYBProgressHUDStyle.WhiteHUDStyle)        })                return Instance.instance!    }        override init(frame: CGRect) {        super.init(frame: frame)                hud = nil        spinner = nil        imageView = nil        titleLabel = nil        self.alpha = 0.0    }        required init(coder aDecoder: NSCoder) {        fatalError("init(coder:) has not been implemented")    }        ///    /// 公开方法    ///        /// 显示信息    class func show(status: String) {        sharedInstance().configureHUD(status, image: nil, isSpin: true, isHide: false)    }        /// 显示成功信息    class func showSuccess(status: String) {        sharedInstance().configureHUD(status, image: sharedInstance().successImage, isSpin: false, isHide: true)    }        /// 显示出错信息    class func showError(status: String) {        sharedInstance().configureHUD(status, image: sharedInstance().errorImage, isSpin: false, isHide: true)    }        /// 隐藏    class func dismiss() {        sharedInstance().hideHUD()    }        ///    /// 私有方法    ///        ///    /// @brief 创建并配置HUD    private func configureHUD(status: String?, image: UIImage?, isSpin: Bool, isHide: Bool) {        configureProgressHUD()                /// 标题        if status == nil {            titleLabel!.hidden = true        } else {            titleLabel!.text = status!            titleLabel!.hidden = false        }        // 图片        if image == nil {            imageView?.hidden = true        } else {            imageView?.hidden = false            imageView?.image = image        }                // spin        if isSpin {            spinner?.startAnimating()        } else {            spinner?.stopAnimating()        }                rotate(nil)        addjustSize()        showHUD()                if isHide {            NSThread.detachNewThreadSelector("hideWhenTimeout", toTarget: self, withObject: nil)        }    }        ///    /// @brief 设置风格样式,默认使用的是黑色的风格,如果需要改成白色的风格,请在内部修改样式    private func setStyle(style: HYBProgressHUDStyle) {        switch style {        case .BlackHUDStyle:            statusColor = UIColor.whiteColor()            spinnerColor = UIColor.whiteColor()            bgColor = UIColor(white: 0, alpha: 0.8)            successImage = UIImage(named: "ProgressHUD.bundle/success-white.png")            errorImage = UIImage(named: "ProgressHUD.bundle/error-white.png")            break        case .WhiteHUDStyle:            statusColor = UIColor.whiteColor()            spinnerColor = UIColor.whiteColor()            bgColor = UIColor(red: 192.0 / 255.0, green: 37.0 / 255.0, blue: 62.0 / 255.0, alpha: 1.0)            successImage = UIImage(named: "ProgressHUD.bundle/success-white.png")            errorImage = UIImage(named: "ProgressHUD.bundle/error-white.png")            break        default:            break        }    }        ///    /// @brief 获取窗口window    private func getWindow() ->UIWindow {        if let delegate: UIApplicationDelegate = UIApplication.sharedApplication().delegate {            if let window = delegate.window {                return window!            }        }                return UIApplication.sharedApplication().keyWindow    }        ///    /// @brief 创建HUD    private func configureProgressHUD() {        if hud == nil {            hud = UIToolbar(frame: CGRectZero)            hud?.barTintColor = bgColor            hud?.translucent = true            hud?.layer.cornerRadius = 10            hud?.layer.masksToBounds = true                        /// 监听设置方向变化            NSNotificationCenter.defaultCenter().addObserver(self,                selector: "rotate:",                name: UIDeviceOrientationDidChangeNotification,                object: nil)        }                if hud!.superview == nil {            getWindow().addSubview(hud!)        }                if spinner == nil {            spinner = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.WhiteLarge)            spinner!.color = spinnerColor            spinner!.hidesWhenStopped = true        }                if spinner!.superview == nil {            hud!.addSubview(spinner!)        }                if imageView == nil {            imageView = UIImageView(frame: CGRectMake(0, 0, 28, 28))        }                if imageView!.superview == nil {            hud!.addSubview(imageView!)        }                if titleLabel == nil {            titleLabel = UILabel(frame: CGRectZero)            titleLabel?.backgroundColor = UIColor.clearColor()            titleLabel?.font = statusFont            titleLabel?.textColor = statusColor            titleLabel?.baselineAdjustment = UIBaselineAdjustment.AlignCenters            titleLabel?.numberOfLines = 0            titleLabel?.textAlignment = NSTextAlignment.Center            titleLabel?.adjustsFontSizeToFitWidth = false        }                if titleLabel!.superview == nil {            hud!.addSubview(titleLabel!)        }    }        ///    /// @brief 释放资源    private func destroyProgressHUD() {        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIDeviceOrientationDidChangeNotification, object: nil)                titleLabel?.removeFromSuperview()        titleLabel = nil                spinner?.removeFromSuperview()        spinner = nil                imageView?.removeFromSuperview()        imageView = nil                hud?.removeFromSuperview()        hud = nil    }        ///    /// @brief 设置方向变化通知处理    func rotate(sender: NSNotification?) {        var rotation: CGFloat = 0.0        switch UIApplication.sharedApplication().statusBarOrientation {        case UIInterfaceOrientation.Portrait:            rotation = 0.0            break        case .PortraitUpsideDown:            rotation = CGFloat(M_PI)            break        case .LandscapeLeft:            rotation = -CGFloat(M_PI_2)            break        case .LandscapeRight:            rotation = CGFloat(M_PI_2)            break        default:            break        }                hud?.transform = CGAffineTransformMakeRotation(rotation)    }        ///    /// @brief 调整大小    private func addjustSize() {        var rect = CGRectZero        var width: CGFloat = 100.0        var height: CGFloat = 100.0                /// 计算文本大小        if titleLabel!.text != nil {            var style = NSMutableParagraphStyle()            style.lineBreakMode = NSLineBreakMode.ByCharWrapping            var attributes = [NSFontAttributeName: statusFont, NSParagraphStyleAttributeName: style.copy()]            var option = NSStringDrawingOptions.UsesLineFragmentOrigin            var text: NSString = NSString(CString: titleLabel!.text!.cStringUsingEncoding(NSUTF8StringEncoding)!,                encoding: NSUTF8StringEncoding)            rect = text.boundingRectWithSize(CGSizeMake(160, 260), options: option, attributes: attributes, context: nil)            rect.origin.x = 12            rect.origin.y = 66                        width = rect.size.width + 24            height = rect.size.height + 80                        if width < 100 {                width = 100                rect.origin.x = 0                rect.size.width = 100            }        }                hud!.center = CGPointMake(kScreenWidth / 2, kScreenHeight / 2)        hud!.bounds = CGRectMake(0, 0, width, height)                var h = titleLabel!.text == nil ? height / 2 : 36        imageView!.center = CGPointMake(width / 2, h)        spinner!.center = CGPointMake(width / 2, h)                titleLabel!.frame = rect    }        ///    /// @brief 显示    private func showHUD() {        if self.alpha == 0.0 {            self.alpha = 1.0                        hud!.alpha  = 0.0            self.hud!.transform = CGAffineTransformScale(self.hud!.transform, 1.4, 1.4)            UIView.animateKeyframesWithDuration(0.15,                delay: 0.0,                options: UIViewKeyframeAnimationOptions.AllowUserInteraction,                animations: { () -> Void in                    self.hud!.transform = CGAffineTransformScale(self.hud!.transform, 1.0 / 1.4, 1.0 / 1.4)                    self.hud!.alpha = 1.0                }, completion: { (isFinished) -> Void in            })        }    }        ///    /// @brief 隐藏    private func hideHUD() {        if self.alpha == 1.0 {            UIView.animateKeyframesWithDuration(0.2,                delay: 0.0,                options: UIViewKeyframeAnimationOptions.AllowUserInteraction,                animations: { () -> Void in                    self.hud!.transform = CGAffineTransformScale(self.hud!.transform, 0.35, 0.35)                    self.hud!.alpha = 0.0                }, completion: { (isFinished) -> Void in                    self.destroyProgressHUD()                    self.alpha = 0.0            })        }    }        ///    /// @brief 在指定时间内隐藏    func hideWhenTimeout() {        autoreleasepool { () -> () in            var length = countElements(self.titleLabel!.text!)            var sleepTime: NSTimeInterval = NSTimeInterval(length) * 0.04 + 0.5            NSThread.sleepForTimeInterval(sleepTime)                        self.hideHUD()        }    }}

剩下的部分, 就需要有耐心的同学们去研究代码了,点击这里可以下载到源代码

 

最精简版的音乐播放器

转载于:https://www.cnblogs.com/Free-Thinker/p/5053880.html

你可能感兴趣的文章
Hdu - 1002 - A + B Problem II
查看>>
Android设置Gmail邮箱
查看>>
js编写时间选择框
查看>>
JIRA
查看>>
小技巧——直接在目录中输入cmd然后就打开cmd命令窗口
查看>>
深浅拷贝(十四)
查看>>
HDU 6370(并查集)
查看>>
BZOJ 1207(dp)
查看>>
HDU 2076 夹角有多大(题目已修改,注意读题)
查看>>
洛谷P3676 小清新数据结构题(动态点分治)
查看>>
九校联考-DL24凉心模拟Day2T1 锻造(forging)
查看>>
Attributes.Add用途与用法
查看>>
L2-001 紧急救援 (dijkstra+dfs回溯路径)
查看>>
javascript 无限分类
查看>>
spring IOC装配Bean(注解方式)
查看>>
[面试算法题]有序列表删除节点-leetcode学习之旅(4)
查看>>
SpringBoot系列五:SpringBoot错误处理(数据验证、处理错误页、全局异常)
查看>>
kubernetes_book
查看>>
OpenFire 的安装和配置
查看>>
侧边栏广告和回到顶部
查看>>