参考链接:https://www.jianshu.com/nb/13566182
1 import UIKit 2 3 class MCLrcDecoder: NSObject { 4 5 // 存储歌词的数组 6 lazy var lrcArray = [String]() 7 8 // 存储歌词时间的数组 9 lazy var timeArray = [String]() 10 11 // 歌词文件名 12 var fileName = "" 13 14 public func loadLrc(fileName: String) -> NSString { 15 let filePath = Bundle.main.path(forResource: fileName, ofType: "lrc") 16 let data = NSData.init(contentsOfFile: filePath!) 17 return NSString(data: data! as Data, encoding: String.Encoding.utf8.rawValue)! 18 } 19 20 func decodeLrc(lrcString: NSString) { 21 var sepArray = lrcString.components(separatedBy: "[") 22 var lineArray = [String]() 23 24 for i in 0..<sepArray.count { 25 if sepArray[i].count > 0 { 26 lineArray = sepArray[i].components(separatedBy: "]") 27 if (lineArray[0] != "\\n") { 28 self.timeArray.append(lineArray[0]) 29 self.lrcArray.append(lineArray.count > 1 ? lineArray[1] : "") 30 } 31 } 32 } 33 } 34 }
1 import UIKit 2 3 class MCLrcCell: UITableViewCell { 4 5 @IBOutlet weak var iconImage: UIImageView! 6 @IBOutlet weak var lrcLabel: UILabel! 7 @IBOutlet weak var imgLeadConstrains: NSLayoutConstraint! 8 9 // 属性,使用计算属性观察者 10 var lrcSelecting = false { 11 didSet { 12 self.didChangeLrcSelecting() 13 } 14 } 15 16 var lrcSelected = false { 17 didSet { 18 self.didChangedLrcSelected() 19 } 20 } 21 22 var lrc: String = "" { 23 didSet { 24 self.lrcLabel.text = lrc 25 } 26 } 27 28 override func awakeFromNib() { 29 super.awakeFromNib() 30 // Initialization code 31 32 self.backgroundColor = UIColor.clear 33 self.reset() 34 } 35 36 func reset() { 37 // 重置 selecting 选项 38 self.lrcSelecting = false 39 // 重置 selected 选项 40 self.lrcSelected = false 41 // FIXME: 42 } 43 44 // MARK: - 私有方法 API 45 let IMG_CONSTRAIN_SELE: CGFloat = 8 46 let IMG_CONSTRAIN_NONSELE: CGFloat = -8 + -8 + -20 47 private func didChangeLrcSelecting() { 48 // 设置约束 49 if self.lrcSelecting { 50 self.imgLeadConstrains.constant = IMG_CONSTRAIN_SELE 51 } else { 52 self.imgLeadConstrains.constant = IMG_CONSTRAIN_NONSELE 53 } 54 } 55 56 private func didChangedLrcSelected() { 57 self.iconImage.image = self.lrcSelected ? #imageLiteral(resourceName: "img_selected") : #imageLiteral(resourceName: "img_nonselected") 58 } 59 60 override func setSelected(_ selected: Bool, animated: Bool) { 61 super.setSelected(selected, animated: animated) 62 63 // Configure the view for the selected state 64 } 65 66 }
1 import UIKit 2 3 class MCLrcVC: UIViewController, UITableViewDelegate, UITableViewDataSource { 4 5 // UI 控件 6 var tableView: UITableView! 7 @IBOutlet weak var createButton: UIBarButtonItem! 8 9 // 属性列表 10 var decoder: MCLrcDecoder = MCLrcDecoder() 11 var fileName: String? 12 var cellSelecting: Bool? 13 14 // 存储 cell 的选择状态 15 var cellDict = [String: Bool]() 16 17 override func viewDidLoad() { 18 super.viewDidLoad() 19 20 // Do any additional setup after loading the view. 21 22 fileName = "song" 23 setupTableView() 24 setupButton() 25 loadLrc() 26 } 27 28 // MARK: - 私有 API 29 fileprivate func setupTableView() { 30 let tableView = UITableView(frame: UIScreen.main.bounds) 31 tableView.delegate = self 32 tableView.dataSource = self 33 tableView.backgroundColor = UIColor.black 34 tableView.separatorStyle = UITableViewCellSeparatorStyle.none 35 cellSelecting = false 36 37 // 添加长按手势 38 let longPressGes = UILongPressGestureRecognizer(target: self, action: #selector(tableViewLongPressAction(_:))) 39 tableView.addGestureRecognizer(longPressGes) 40 41 self.view.addSubview(tableView) 42 self.tableView = tableView 43 44 // 注册所用的 cell 45 registeCell() 46 } 47 48 fileprivate func setupButton() { 49 createButton.isEnabled = false 50 createButton.tintColor = UIColor.clear 51 createButton.target = self 52 createButton.action = #selector(createShareImage) 53 } 54 55 fileprivate func registeCell() { 56 tableView.register(UINib(nibName: "MCLrcCell", bundle: Bundle.main), forCellReuseIdentifier: "cellId") 57 } 58 59 fileprivate func loadLrc() { 60 let filePath = self.decoder.loadLrc(fileName: self.fileName!) 61 self.decoder.decodeLrc(lrcString: filePath) 62 self.tableView.reloadData() 63 } 64 65 @objc fileprivate func createShareImage() { 66 if cellSelecting == false { 67 return 68 } 69 70 // 生成歌词数组 71 var lrcArr = [String]() 72 for i in 0..<decoder.lrcArray.count { 73 // 生成 Key, 通过 Key 访问字典 74 let isLrcSelected = cellDict["0-\\(i)"] 75 if isLrcSelected != nil && isLrcSelected == true { 76 lrcArr.append(self.decoder.lrcArray[i]) 77 } 78 } 79 80 // 如果没有选择歌词,则提示 81 if lrcArr.count <= 0 { 82 let alert = UIAlertController(title: "提示", message: "请至少选择一句歌词", preferredStyle: .alert) 83 alert.addAction(UIAlertAction (title: "确定", style: .default, handler: nil)) 84 self.present(alert, animated: true, completion: nil) 85 return 86 } 87 88 // 显示歌词分享界面 89 let storyBoard = UIStoryboard (name: "MCShareVC", bundle: nil) 90 let vc = storyBoard.instantiateViewController(withIdentifier: "MCShareVC") as! MCShareVC 91 92 vc.lrcArr = lrcArr 93 self.navigationController?.pushViewController(vc, animated: true) 94 } 95 96 @objc fileprivate func tableViewLongPressAction(_ longPresas: UILongPressGestureRecognizer) { 97 if longPresas.state != UIGestureRecognizerState.began { 98 return 99 } 100 101 // 切换选择模式 102 cellSelecting = cellSelecting == true ? false : true 103 if cellSelecting == true { 104 // 清空映射字典 105 cellDict.removeAll() 106 // 显示按钮 107 createButton.isEnabled = true 108 createButton.tintColor = UIColor.red 109 } else { 110 // 隐藏按钮 111 createButton.isEnabled = false 112 createButton.tintColor = UIColor.clear 113 } 114 115 // 更新 tableView 116 self.tableView.reloadData() 117 } 118 119 120 override func didReceiveMemoryWarning() { 121 super.didReceiveMemoryWarning() 122 // Dispose of any resources that can be recreated. 123 } 124 125 126 /* 127 // MARK: - Navigation 128 129 // In a storyboard-based application, you will often want to do a little preparation before navigation 130 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 131 // Get the new view controller using segue.destinationViewController. 132 // Pass the selected object to the new view controller. 133 } 134 */ 135 136 } 137 138 // MARK: - 代理方法实现 139 extension MCLrcVC { 140 func numberOfSections(in tableView: UITableView) -> Int { 141 return 1 142 } 143 144 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 145 return self.decoder.lrcArray.count 146 } 147 148 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 149 let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! MCLrcCell 150 151 cell.reset() 152 cell.lrc = self.decoder.lrcArray[indexPath.row] 153 cell.lrcSelecting = cellSelecting! 154 155 // 判断 cell 是否正处于选择状态 156 let isCellSelected = cellDict["\\(indexPath.section)-\\(indexPath.row)"] 157 if let isCellSelected = isCellSelected { 158 cell.lrcSelected = isCellSelected 159 } 160 161 162 return cell 163 } 164 165 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 166 tableView.deselectRow(at: indexPath, animated: true) 167 168 // 如果没有在选择状态,则推出 169 if cellSelecting == false { 170 return 171 } 172 173 let cell = tableView.cellForRow(at: indexPath) as! MCLrcCell 174 cell.lrcSelected = cell.lrcSelected == true ? false : true 175 self.cellDict["\\(indexPath.section)-\\(indexPath.row)"] = cell.lrcSelected 176 } 177 178 }
1 import UIKit 2 3 class MCShareVC: UIViewController, UIScrollViewDelegate { 4 5 // UI 控件 6 @IBOutlet weak var scrollView: UIScrollView! 7 @IBOutlet weak var shareButton: UIButton! 8 @IBOutlet weak var saveButton: UIButton! 9 10 var bkgImage: UIImageView! 11 12 // 数据 13 var lrcArr = [String]() 14 15 override func viewDidLoad() { 16 super.viewDidLoad() 17 18 // Do any additional setup after loading the view. 19 20 setupView() 21 setupButton() 22 23 drawLrcBackground() 24 addLrcToBackground() 25 26 } 27 28 // MARK: - 初始化私有方法 29 func setupView() { 30 scrollView.backgroundColor = UIColor.clear 31 32 if #available(ios 11.0, *) { 33 scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentBehavior.never 34 } else { 35 automaticallyAdjustsScrollViewInsets = false 36 } 37 38 self.bkgImage = UIImageView() 39 bkgImage.contentMode = UIViewContentMode.scaleToFill 40 } 41 42 func setupButton() { 43 shareButton.addTarget(self, action: #selector(shareImageHandler), for: .touchUpInside) 44 saveButton.addTarget(self, action: #selector(saveImageHandler), for: .touchUpInside) 45 } 46 47 let kItemHeight: CGFloat = 20 48 let kItemMargin: CGFloat = 5 49 50 // 通过图片拉伸,将背景图片适配各种屏幕 51 func drawLrcBackground() { 52 let kItemDefaultHeight: CGFloat = 150 53 54 let imageWidth = view.frame.size.width 55 let imageHeight = CGFloat(lrcArr.count) * (kItemHeight + kItemMargin) 56 57 bkgImage.frame = CGRect(x: 0, y: 0, width: imageWidth, height: kItemDefaultHeight + imageHeight) 58 scrollView.addSubview(bkgImage) 59 60 // 获取图片 61 let sourceImage = UIImage(named: "shareBkg.png") 62 bkgImage.image = sourceImage 63 64 // 设置图片拉伸参数 65 let topInset = sourceImage!.size.height * 0.4 66 let leftInset = sourceImage!.size.width * 0.7 67 let bottomInset = sourceImage!.size.height - topInset - 1 68 let rightInset = sourceImage!.size.width - leftInset - 1 69 70 let bkgEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset) 71 72 // 拉伸图片 73 let resizedImage = sourceImage?.resizableImage(withCapInsets: bkgEdgeInsets, resizingMode: UIImageResizingMode.stretch) 74 75 // 设置图片和 ScrollView 76 bkgImage.image = resizedImage 77 scrollView.contentSize = bkgImage.frame.size 78 } 79 80 // 向背景中添加歌词 81 func addLrcToBackground() { 82 let kLrcStartX: CGFloat = 40 83 let kLrcStartY: CGFloat = 50 84 85 let kLrcRightMargin: CGFloat = 20 86 87 let xCursor = kLrcStartX 88 var yCursor = kLrcStartY 89 90 // 按顺序添加歌词到背景上 91 for i in 0..<lrcArr.count { 92 // 获取当前歌词 93 let curLrc = lrcArr[i] 94 // 创建歌词对应的 Label 95 let lrcLabel = UILabel(frame: CGRect(x: xCursor, y: yCursor, width: view.frame.size.width - 2 * kLrcStartX, height: kItemHeight)) 96 97 lrcLabel.text = curLrc 98 lrcLabel.textColor = UIColor.darkGray 99 lrcLabel.font = UIFont.systemFont(ofSize: 15) 100 101 bkgImage.addSubview(lrcLabel) 102 103 // y 坐标递增 104 yCursor += kItemHeight + kItemMargin 105 } 106 107 // 添加底部歌名 108 let rightText = "--[Minecode - iOS.Dev]" 109 110 yCursor += kLrcRightMargin 111 112 let rightLabel = UILabel (frame: CGRect (x: xCursor, y: yCursor, width: view.frame.size.width - 2 * kLrcStartX, height: kItemHeight)) 113 114 rightLabel.text = rightText 115 rightLabel.textColor = UIColor.darkGray 116 rightLabel.textAlignment = NSTextAlignment.right 117 rightLabel.font = UIFont.systemFont(ofSize: 15) 118 119 bkgImage.addSubview(rightLabel) 120 } 121 122 // MARK: - 分享和保存 123 124 // 保存到相册中 125 @objc func saveImageHandler() { 126 // 生成 View 的图片 127 UIGraphicsBeginImageContextWithOptions(self.bkgImage.bounds.size, true, 0) 128 bkgImage.layer.render(in: UIGraphicsGetCurrentContext()!) 129 let bitmap = UIGraphicsGetImageFromCurrentImageContext() 130 UIGraphicsEndImageContext() 131 132 // let imageData = UIImagePNGRepresentation(bitmap!) 133 134 // let imageData = UIImageJPEGRepresentation(bitmap!, 0.1) 135 // let tempImg = UIImage.init(data: imageData!) 136 137 // 保存到相册 138 UIImageWriteToSavedPhotosAlbum(bitmap!, self, #selector(image(image:didFinishSavingWithError:contextInfo:)), nil) 139 } 140 141 // 使用系统分享功能分享图片 142 @objc func shareImageHandler() { 143 144 // 生成 View 的图片 145 UIGraphicsBeginImageContextWithOptions(self.bkgImage.bounds.size, true, 0) 146 bkgImage.layer.render(in: UIGraphicsGetCurrentContext()!) 147 let bitmap = UIGraphicsGetImageFromCurrentImageContext() 148 UIGraphicsEndImageContext() 149 150 let activityVc = UIActivityViewController (activityItems: [bitmap!], applicationActivities: nil) 151 self.present(activityVc, animated: true, completion: nil) 152 } 153 154 // 保存到相册后的回调 155 @objc func image(image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: AnyObject) { 156 157 if error == nil { 158 159 let alert = UIAlertController(title: "成功", message: "保存成功", preferredStyle: .alert) 160 alert.addAction(UIAlertAction(title: "确定", style: .destructive, handler: nil)) 161 self.present(alert, animated: true, completion: nil) 162 163 } else { 164 165 let alert = UIAlertController(title: "失败", message: "保存失败", preferredStyle: .alert) 166 alert.addAction(UIAlertAction(title: "确定", style: .destructive, handler: nil)) 167 self.present(alert, animated: true, completion: nil) 168 169 } 170 } 171 172 override func didReceiveMemoryWarning() { 173 super.didReceiveMemoryWarning() 174 // Dispose of any resources that can be recreated. 175 } 176 177 /* 178 // MARK: - Navigation 179 180 // In a storyboard-based application, you will often want to do a little preparation before navigation 181 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 182 // Get the new view controller using segue.destinationViewController. 183 // Pass the selected object to the new view controller. 184 } 185 */ 186 187 }