iOS开发之如何用UITableView实时显示歌词
来源:互联网 发布:淘宝防排查防举报 编辑:程序博客网 时间:2024/04/29 21:56
前一段时间一直在捣鼓做一个网络音乐播放器,已经实现了基本功能,能加载网络上专辑图片、歌曲和歌词,能播放、暂停、上一曲、下一曲,能选歌播放。现在想要加入显示歌词的功能,上网查了一下资料,大家都是用UITableView来显示歌词的,自己试着整一下,下面来分享一下我的成果!
先说一下思路:
1、将歌词中的内容解析出来,时间点和歌词内容要存储到一个存放歌词信息的类中。
2、建立UITableView,一行歌词一行cell(前面解析歌词时可以知道总共有多少行歌词)。
3、根据音乐播放的进度调整显示哪一行,给当前显示的cell添加一个小动画,增强用户体验。
这里为了方便,直接从storyboard拉一个UITableView,再拉一个cell,拉一个label,拉两个button,如图1所示:
图1
这里的cell是自定义cell,label是放到cell中铺满整个cell,因为歌词显示一行一个cell,cell只显示label,这样歌词的行距就一样了。自定义cell要设置identifier,这里设置为lrcCell,如图2所示:
图2
为UITableView新建一个class,名字为SCTableView,这个类封装了歌词解析(歌词格式如图3所示)和歌词显示的方法,其中代码如下:
//// SCTableView.swift// playerTest4//// Created by 凌 陈 on 7/15/17.// Copyright © 2017 凌 陈. All rights reserved.//import UIKitclass SCTableView: UITableView, UITableViewDelegate , UITableViewDataSource{ var mCurrentSongIndex = 0 var mUpdateTimer : Timer? var mLRCDictinary : [String : String] = [String : String]() var mTimeArray : [String] = [String]() var mIsLRCPrepared : Bool = false var mLineNumber : Int = -1 var currentCell : SCLrcCell? var lrcOldCell : SCLrcCell? var old_Index : Int = 0 var lrcProgress : CGFloat = 0.0{ didSet{ if currentCell != nil { autoUpdateLrc() } } } // 设置歌词index,显示哪一行 var Lrc_Index : Int = 0 { didSet{ if Lrc_Index == oldValue { return } // 滚动到指定index的位置 // 新的indexPath let indexPath = NSIndexPath(row: Lrc_Index, section: 0 ) self.scrollToRow(at: indexPath as IndexPath, at: .middle, animated: true) currentCell = self.cellForRow(at: indexPath as IndexPath) as? SCLrcCell currentCell?.mTitleLable.textColor = UIColor.red // 旧的indexPath let oldIndexpath = NSIndexPath(row: oldValue, section: 0) let oldCell = self.cellForRow(at: oldIndexpath as IndexPath) as? SCLrcCell old_Index = oldValue lrcOldCell = oldCell currentCell?.addAnimation(animationType: .scaleAlways) oldCell?.addAnimation(animationType: .scaleNormal) oldCell?.mTitleLable.textColor = UIColor.black // oldCell?.lrcCell_textLabel?.progress = 0 } } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.dataSource = self self.delegate = self } //MARK: - 解析歌词 func prepareLRC(lrcPath:String) { //从资源中导入 lrc let url = Bundle.main.url(forResource: lrcPath, withExtension: nil) var contentStr = "" do{ contentStr = try String(contentsOf: url!) }catch{ print(error) } let lrcArray = contentStr.components(separatedBy: "\n") self.mLRCDictinary = [String : String]() self.mTimeArray = [String]() for line in lrcArray { // 首先处理歌词中无用的东西 // [ti:][ar:][al:]这类的直接跳过 if line.contains("[0") || line.contains("[1") || line.contains("[2") || line.contains("[3") { var lineArr = line.components(separatedBy:"]") let str1 = (line as NSString).substring(with: NSRange(location: 3,length: 1)) let str2 = (line as NSString).substring(with: NSRange(location: 6,length: 1)) if str1 == ":" && str2 == "." { let lrcStr = lineArr[1] let timeStr = (lineArr[0] as NSString).substring(with: NSRange(location: 1, length: 5)) self.mLRCDictinary[timeStr] = lrcStr self.mTimeArray.append(timeStr) } } else { continue } } self.mIsLRCPrepared = true self.reloadData() } func updateLRC(currentTime:CGFloat) { for i in 0..<self.mLRCDictinary.count { var timeArr = self.mTimeArray[i].components(separatedBy:":") let time = CGFloat(Int(timeArr[0])!) * 60 + CGFloat(Int(timeArr[1])!) if i + 1 < self.mTimeArray.count { var timeArr1 = self.mTimeArray[i + 1].components(separatedBy:":") let time1 = CGFloat(Int(timeArr1[0])!) * 60 + CGFloat(Int(timeArr1[1])!) if currentTime > time && currentTime < time1 { self.mLineNumber = i self.reloadData() self.selectRow(at: NSIndexPath(row: i, section: 0) as IndexPath, animated: true, scrollPosition: UITableViewScrollPosition.middle) } } } } /// 自动更新歌词进度方法 fileprivate func autoUpdateLrc() { // let lrcModel = lrcModels[Lrc_Index] // lrcModel.progress = lrcProgress // lrcModels[Lrc_Index] = lrcModel let indexPath = NSIndexPath(row: Lrc_Index, section: 0) as IndexPath let count = self.visibleCells if count.count <= 0 || indexPath.row > self.mTimeArray.count - 1 { return } guard (self.cellForRow(at:indexPath ) != nil) else { return } let cell = self.cellForRow(at:indexPath ) } //MARK: - Table Delegate func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.mTimeArray.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "lrcCell") as! SCLrcCell cell.mTitleLable.text = self.mLRCDictinary[self.mTimeArray[indexPath.row]] cell.backgroundColor = UIColor.clear cell.selectionStyle = UITableViewCellSelectionStyle.none cell.mTitleLable.numberOfLines = 0 cell.mTitleLable.lineBreakMode = NSLineBreakMode.byWordWrapping cell.mTitleLable.sizeToFit() if self.mLineNumber == indexPath.row { cell.mTitleLable.font = UIFont.systemFont(ofSize: 24) cell.mTitleLable.textColor = UIColor.red } else { cell.mTitleLable.font = UIFont.systemFont(ofSize: 20) cell.mTitleLable.textColor = UIColor.black } return cell } }
图3
将UITableView的class设置为SCTableView,方法如图4所示:
图4
同样,为UITableViewCell新建一个class,名字为SCLrcCell,其中代码如下://// SRLrcCell.swift// TestRadio//// Created by Wentao on 15/5/14.// Copyright (c) 2015年 Wentao. All rights reserved.//import UIKitclass SCLrcCell: UITableViewCell { // 这个是storyboard拉的label的outlet @IBOutlet weak var mTitleLable: UILabel! override func awakeFromNib() { super.awakeFromNib() // Initialization code } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // Configure the view for the selected state }}将UITableViewCell的class设置为SCLrcCell。
新建一个class,名字为SCCellAnimation,这个类处理cell动画,其代码如下:
import UIKitenum SC_AnimationType { case translation case scale case rotation case scaleAlways case scaleNormal}extension UITableViewCell{ func addAnimation(animationType : SC_AnimationType) { switch animationType { case .translation: layer.removeAnimation(forKey: "translation") let animation = CAKeyframeAnimation(keyPath: "transform.translation.x") animation.values = [50 , 0 , 50 , 0]; animation.duration = 0.7 animation.repeatCount = 1 layer.add(animation, forKey: "translation") case .scale: layer.removeAnimation(forKey: "scale") let animation = CAKeyframeAnimation(keyPath: "transform.scale.x") animation.values = [0.5, 1.0 ]; animation.duration = 0.7 animation.repeatCount = 1 animation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]; layer.add(animation, forKey: "scale") case .rotation: layer.removeAnimation(forKey: "rotation") let animation = CAKeyframeAnimation(keyPath: "transform.rotation.z") animation.values = [-1 / 6 * Double.pi , 0 , 1 / 6 * Double.pi , 0]; animation.duration = 0.7 animation.repeatCount = 1 layer.add(animation, forKey: "rotation") case .scaleAlways : //layer.removeAnimation(forKey: "scale") let animation = CAKeyframeAnimation(keyPath: "transform.scale.x") animation.values = [1.0, 1.2 ]; animation.duration = 0.7 animation.repeatCount = 1 animation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]; animation.isRemovedOnCompletion = false animation.fillMode = kCAFillModeForwards; layer.add(animation, forKey: "scale") case .scaleNormal: let animation = CAKeyframeAnimation(keyPath: "transform.scale.x") animation.autoreverses = true animation.duration = 0.7 animation.repeatCount = 1 animation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]; layer.add(animation, forKey: "scale") default: break } }}
在UIViewController中添加如下代码:
//// ViewController.swift// playerTest4//// Created by 凌 陈 on 7/15/17.// Copyright © 2017 凌 陈. All rights reserved.//import UIKitclass ViewController: UIViewController { var lrcIndex = 0 @IBOutlet weak var mLrcTable: SCTableView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. self.mLrcTable.separatorStyle = .none// 去掉tableView分割线 self.mLrcTable.showsVerticalScrollIndicator = false// 去掉tableView垂直滚动条 self.mLrcTable.prepareLRC(lrcPath: "309769.lrc")// 歌词解析 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // 上一行 @IBAction func preLine(_ sender: Any) { if lrcIndex > 0 { lrcIndex = lrcIndex - 1 self.mLrcTable.Lrc_Index = lrcIndex } } // 下一行 @IBAction func nextLine(_ sender: Any) { if lrcIndex < self.mLrcTable.mTimeArray.count - 1 { lrcIndex = lrcIndex + 1 self.mLrcTable.Lrc_Index = lrcIndex } }}把歌词添加进工程就可以运行了,运行结果:
源代码已经放到github,地址:https://github.com/Mozartisnotmyname/LRCShow.git
- iOS开发之如何用UITableView实时显示歌词
- iOS开发技巧之:如何用Xcode导出ipa包
- iOS开发 UITableView之cell
- iOS开发学习之UITableView
- 4、iOS 开发之 UITableView
- ios开发系列之UITableView
- iOS开发之UITableView多选
- iOS开发之UITableView详解
- iOS开发之自定义UITableView
- IOS之UITableView的header显示问题
- 如何用ios 5开发ipad上的复杂应用程序
- 如何用Visual Studio开发iOS、Android应用
- [iOS开发]如何用KissXML生成一个XML文件
- iOS开发-如何用GCD同步若干个异步调用?
- 如何用js完美的解析lrc歌词
- 如何用深度学习来写歌词(神经网络实现)
- 【IOS开发】实时显示摄像头内容
- 如何用C#实时获取CPU利用率
- GoAccess初识
- HDU1024
- 1004 n^n的末位数字(快速幂)
- android判断点击位置在一扇形区域内
- 第十届全国大学生信息安全竞赛一道Web题的Writeup
- iOS开发之如何用UITableView实时显示歌词
- php的Error与Exception捕获问题
- ubuntu16.04 Server 安装CUDA
- poj 3320 尺取
- ARP(二)
- 2809: [Apio2012]dispatching
- 538. Convert BST to Greater Tree
- Codeforces Round #424 题解
- 寻找倍数<set集合使用>