这几天闲着也是闲着,学习一下Swift的,于是到开源社区Download了个OC版的音乐播放器,练练手,在这里发扬开源精神,
希望对大家有帮助!
这个DEMO里,使用到了
AudioPlayer(对音频封装的库)
FreeStreamer(老外写的音频高效处理库)
LKDBHelper(将数据模型直接写到数据库中的库)
AFNetworking (网络库)
SDWebImage (图片获取库)
另外,我也把OC版的ProgressHUD转成了Swift版本的HYBProgressHUD,希望对大家有用啊!
目前只实现了这几个简单的功能,希望有时间且爱研究的同学,追加更多的功能再开源出来哦!
下面我说一下封装的网络请求类:
import Foundation /// 请求成功与失败的回调 typealias requestSuccessCloser = (responseObject: AnyObject?) ->Void typealias failCloser = (error: NSError?) ->Void /// /// 描述:网络请求基础类,所有GET请求方式都是以GET开头的类方法,POST请求方式会以POST开头命名类方法 /// /// 作者:huangyibiao class 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 /// /// 描述: 显示歌词控件 /// /// 作者: huangyibiao class 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 Foundation import UIKit /// /// @brief 样式 enum HYBProgressHUDStyle { case BlackHUDStyle /// 黑色风格 case WhiteHUDStyle /// 白色风格 } /// /// @brief 定制显示通知的视图HUD /// @author huangyibiao class 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() } } }
剩下的部分, 就需要有耐心的同学们去研究代码了,点击这里可以下载到源代码