iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

06-01 1500阅读

iOS移动应用开发技术课程设计——音乐播放器

  • 一、环境与技术说明
  • 二、项目需求分析
    • 1.用户需求分析
    • 2.系统体系结构设计
    • 3.组织结构设计
    • 三、项目实现
      • 1.整体场景布局设计(小程序界面总的说明)
      • 2.登录功能
      • 3.注册功能
      • 4.歌曲列表功能
      • 5.音乐播放及添加收藏功能
      • 6.收藏列表功能
      • 7.跳转云音乐平台功能
      • 四、代码附录
        • RegisterViewController类:
        • LoginViewController类:
        • MusicTableViewController类:
        • MusicTableViewCell子类:
        • PlayingViewController类:
        • CloudViewController类:
        • FavoritesTableViewCell子类
        • FavoritesTableViewController类:
        • MusicFavorites类:
        • Music类:

          本文是基于项目功能的分析与说明,无基础但想尝试实现小程序的同学建议直接去看我的视频教程,一步一步跟做:

          ios开发实战——手把手制作音乐播放器APP

          一、环境与技术说明

          系统:iOS 17.2

          开发平台:Xcode 15.1

          编程语言:Swift 5.9.2

          图形界面设计:UIKit 框架

          数据持久化:UserDefaults

          音乐播放:AVFoundation 框架

          二、项目需求分析

          本项目的主要目标是提供一个直观、简单易用的平台,让用户能够方便地浏览、收藏和播放自己喜欢的音乐(大概)。

          1.用户需求分析

          • 用户登录和注册:提供用户注册和登录功能,确保用户信息的安全性。登录成功后,用户可以享受个性化的体验,如收藏自己喜欢的音乐并管理收藏列表。
          • 音乐浏览:用户需要能够轻松地浏览音乐列表,查看歌曲信息,包括歌曲名称、歌手等。
          • 音乐播放:提供用户播放、停止、切歌、调整播放进度等基本音乐播放控制功能。
          • 音乐收藏:用户希望能够将自己喜欢的音乐添加到收藏列表,方便日后查看和播放。
          • 数据持久化:用户希望应用能够记住他们的收藏列表,即使应用关闭或重启后,之前的收藏信息仍然保留。
          • 网易云音乐链接:用户可以通过应用直接跳转至网易云音乐平台,选择更多自己喜爱的音乐。

            2.系统体系结构设计

            • 用户管理模块:处理用户的登录、注册等操作,管理用户信息。
            • 音乐浏览模块:主要包括音乐列表页面,展示所有可播放的音乐信息。
            • 音乐播放模块:实现音乐的播放、停止、切换、调整进度等功能,包括播放控制按钮和当前歌曲信息显示。
            • 音乐收藏模块:提供用户管理收藏列表的功能,包括查看收藏列表和删除收藏信息。
            • 数据持久化模块:使用 UserDefaults 进行数据持久化,确保用户的收藏信息在应用关闭和重启后得以保存。

              3.组织结构设计

              • AppDelegate:应用程序的入口,用于初始化应用和处理应用生命周期事件。
              • ViewControllers:包括主界面、单曲播放界面和收藏列表界面的控制器。
              • Models:包括 Music 类用于表示音乐信息,MusicFavorites 类用于管理用户的音乐收藏。
              • Utilities:包括一些通用的工具类,用于处理数据持久化等操作。

                三、项目实现

                1.整体场景布局设计(小程序界面总的说明)

                故事板是应用程序界面的可视化设计工具,通过连接各个场景(View Controller)来呈现应用的导航流程。以下是项目的六个主要界面的场景布局描述:

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                ①登录界面(LoginViewController):

                登录界面是应用的起始点,用户需要输入账号和密码进行登录;

                包含登录按钮和链接到注册界面的按钮。

                ②注册界面(RegisterViewController):

                注册界面允许用户创建新账户,要求填写必要的注册信息;

                包含注册按钮;提供返回按钮,使用户可以返回登录界面。

                ③歌曲列表界面(MusicTableViewController):

                歌曲列表界面展示所有可播放的歌曲,用户可以点击进入单曲播放界面;

                提供返回按钮,使用户可以返回登录界面。

                ④单曲播放界面(PlayingViewController):

                单曲播放界面显示当前播放歌曲的信息,包括海报图片以及播放状态;

                包含播放控制按钮和停止控制按钮;包含添加收藏按钮和链接到收藏列表界面的按钮、链接到云音乐平台的按钮;包含切换至上一首和下一首歌曲的按钮;

                提供进度条;

                提供返回按钮,使用户可以返回歌曲列表界面。

                ⑤收藏列表界面(FavoritesTableViewController):

                收藏列表界面展示用户收藏的歌曲信息;

                提供编辑按钮,允许用户删除收藏列表中的歌曲以及调整列表歌曲的顺序;

                提供返回按钮,使用户可以返回单曲播放界面。

                ⑥云音乐平台界面(CloudMusicViewController):

                云音乐平台界面是一个外部链接,跳转至网易云音乐平台。

                2.登录功能

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                登录功能负责验证用户输入的账号和密码是否匹配,并根据验证结果执行相应的操作。以下是对登录功能的实现描述:

                ①用户界面设计:登录界面包括两个文本框,分别用于输入账号和密码。提供登录按钮,用户点击该按钮将触发登录操作。

                ②用户输入获取:通过userId和password两个UITextField获取用户输入的用户名和密码。

                **③用户验证:**在用户点击登录按钮后,通过获取输入的用户名和密码;从UserDefaults中获取保存的用户名和密码的字典userpwd,默认为空字典。要求验证输入的用户名是否存在于userpwd中,并且对应的密码是否匹配。

                **④登录结果处理:**如果用户名和密码匹配成功,执行Segue跳转至主界面;如果验证失败,弹出UIAlertController提示用户登录失败,显示警告信息。利用DispatchQueue延时1秒后自动关闭提示信息。

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                **⑤数据存储:**用户信息存储在UserDefaults中,以便下次登录时验证用户身份。

                主要代码:

                    let user = userId.text!
                        let pwd = password.text!
                        userpwd = UserDefaults.standard.object(forKey: "userpwd") as?[String:String] ?? [String:String]()
                        
                        if userpwd[user] == pwd {
                            self.performSegue(withIdentifier: "LOGIN", sender: nil)
                        }else{
                            let alertController = UIAlertController(title: "登录失败", message: nil, preferredStyle: .alert)
                            self.present(alertController, animated: true, completion: nil)
                            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1){
                                self.presentedViewController?.dismiss(animated: false, completion: nil)
                            }
                        }
                

                3.注册功能

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                注册功能负责让用户在应用中创建新账户。以下是对注册功能的实现描述:

                **①用户界面设计:**注册界面包括两个文本框,分别用于输入新用户名和密码。提供注册按钮,用户点击该按钮将触发注册操作。

                **②用户输入获取:**通过 userId 和 password 两个 UITextField 获取用户输入的新用户名和密码。

                **③用户注册:**在用户点击注册按钮后,获取输入的新账号和密码。从 UserDefaults 中获取保存的用户名和密码的字典 userpwd,默认为空字典。最后将新用户名和密码添加到 userpwd 中。

                **④数据存储:**将更新后的 userpwd存储回 UserDefaults 中。使用 synchronize 确保数据及时同步。

                **⑤注册结果处理:**弹出 UIAlertController 提示用户注册成功,显示成功信息。

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                主要代码:

                	let user = userId.text!
                	let pwd = password.text!
                	userpwd = UserDefaults.standard.object(forKey: "userpwd") as?[String:String] ?? [String:String]()
                	userpwd[user] = pwd
                        
                	UserDefaults.standard.set(userpwd, forKey: "userpwd")
                	UserDefaults.standard.synchronize()
                        
                	let ok = UIAlertAction(title:"确定", style:.default){
                		action in self.dismiss(animated: true, completion: nil)
                	}
                	let alter = UIAlertController(title: "注册成功", message: nil, preferredStyle: .alert)
                	alter.addAction(ok)
                	self.present(alter,animated: true, completion: nil)
                

                4.歌曲列表功能

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                歌曲列表功能负责展示用户可以播放的歌曲列表。以下是对歌曲列表功能的实现描述:

                **①数据源准备:**在 viewDidLoad 方法中,通过 Bundle 获取应用中的 MusicList.plist 文件路径,并将其中的数据加载到 listMusics 数组中。

                override func viewDidLoad() {
                        super.viewDidLoad()
                        
                        let plistPath = Bundle.main.path(forResource: "MusicList", ofType: "plist")
                        self.listMusics = NSArray(contentsOfFile: plistPath!)
                      
                    }
                

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                listMusics 数组包含了每首歌曲的信息,如歌曲名、歌手名和图片文件名等。

                ②表格显示:

                在 numberOfSections 方法中返回表格的分区数,这里只有一个分区。

                在 tableView(:numberOfRowsInSection:) 方法中返回表格的行数,即歌曲的数量。

                在 tableView(:cellForRowAt:) 方法中配置每个表格行的显示,使用 MusicTableViewCell 自定义单元格来展示歌曲信息。

                override func numberOfSections(in tableView: UITableView) -> Int {
                        return 1
                    }
                    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                        return listMusics.count
                    }
                    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                        let cell = tableView.dequeueReusableCell(withIdentifier: "ID", for: indexPath) as! MusicTableViewCell
                	let row = indexPath.row
                        
                        let rowDict = self.listMusics[row] as! [String:String]
                        
                        cell.myName.text = rowDict["name"]
                        cell.mySinger.text = rowDict["singer"]
                        
                        let imagePath = String(format: "%@.png" , rowDict["image"]!)
                        cell.myImageView.image = UIImage(named: imagePath)
                        
                        cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
                        
                        return cell
                    }
                

                ③表格行点击处理:

                实现 prepare(for segue: UIStoryboardSegue, sender: Any?) 方法,在表格行点击时跳转到单曲播放界面。

                通过获取选中行的索引,从 listMusics 数组中取得相应的歌曲信息,将其传递给 PlayingViewController。

                override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
                        if segue.identifier == "PLAY" {
                            let playingViewController = segue.destination as! PlayingViewController
                            let indexPath = self.tableView.indexPathForSelectedRow as IndexPath?
                            let selectedIndex = indexPath!.row
                            let rowDict = self.listMusics[selectedIndex] as! [String:String]
                            
                            playingViewController.listData = rowDict
                        }
                    }
                

                5.音乐播放及添加收藏功能

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                音乐播放功能负责展示用户选择的单曲信息以及提供播放、停止等音乐控制功能。以下是对该部分功能的实现描述:

                **①界面初始化:**在 viewDidLoad 方法中,设置界面初始状态,包括显示歌曲封面图片、设置播放状态标签等。

                override func viewDidLoad() {
                        super.viewDidLoad()
                        
                        self.label.text = "播放停止"
                        
                        let imagePath = String(format: "%@.png", listData["image"]!)
                        ImageView.image = UIImage(named: imagePath)
                        
                    }
                

                ②播放按钮处理: 实现 play(_ : ) 方法,用于处理播放按钮点击事件。在方法中,检查播放器是否已经初始化,若未初始化,则根据 listData 中的歌曲文件名获取音频文件路径,并创建 AVAudioPlayer 实例。播放器准备就绪后,根据当前播放状态进行播放或暂停操作,并更新界面文字显示。

                if self.player == nil {
                            let path = Bundle.main.path(forResource: listData["name"]!, ofType: "mp3")
                            
                            let url = URL(fileURLWithPath: path!)
                            
                            do {
                                self.player = try AVAudioPlayer(contentsOf: url)
                            } catch let error as NSError {
                                self.player = nil
                                print(error.description)
                                self.label.text = "播放错误"
                                return
                            }
                            
                            self.player.prepareToPlay()
                            self.player.numberOfLoops = -1
                            player.delegate = self
                        }
                        
                        if !self.player.isPlaying {
                            player.play()
                            self.label.text = "正在播放"
                            
                            let pauseImage = UIImage(named: "pause")
                            self.btnplay.setImage(pauseImage, for: UIControl.State())
                        }else{
                            player.pause()
                            self.label.text = "播放暂停"
                            let playImage = UIImage(named: "play")
                            self.btnplay.setImage(playImage, for: UIControl.State())
                        }
                    }
                

                音乐播放时,文字状态改变:

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                **③停止按钮处理:**实现 stop(_: ) 方法,用于处理停止按钮点击事件。如果播放器正在播放,停止播放并释放资源,同时更新界面显示。

                if let player = self.player, player.isPlaying {
                            player.stop()
                            player.delegate = nil
                            self.player = nil
                            self.label.text = "播放停止"
                            
                            let playImage = UIImage(named: "play")
                            self.btnplay.setImage(playImage, for: .normal)
                        }
                

                **④添加到收藏功能:**实现 addToFavorites(_: ) 方法,用于将当前歌曲信息添加到收藏列表。

                guard let listData = listData else {return}
                        let music = Music(name: listData["name"]!, singer: listData["singer"]!)
                        
                        MusicFavorites.shared.addFavorite(music)
                        sender.setTitle("已收藏", for: .normal)
                

                通过 MusicFavorites 类的共享实例,将当前歌曲信息创建为 Music 对象并添加到收藏列表中。

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                **⑤查看收藏列表:**实现 viewFavorites(_: ) 方法,用于在点击"查看我的收藏"按钮时跳转到收藏列表界面。

                performSegue(withIdentifier: "ViewFavorites", sender: self)
                

                使用 performSegue(withIdentifier:sender:) 进行界面跳转,并将当前视图控制器作为 sender 传递给目标视图控制器。

                ⑥ 进度条功能(未完善):

                在界面中添加 UISlider 控件,用于显示和调整音乐播放进度。

                实现 updateUI() 方法,更新界面元素,包括进度条的位置。

                实现 playCurrentSong() 方法中,监听播放进度的变化,更新进度条位置。

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                ⑦ 歌曲切换功能(未完善):

                实现 previousSong( : ) 和 nextSong(: ) 方法,处理切换上一首和下一首按钮点击事件。 根据按钮点击的不同,更新当前歌曲索引,然后调用 playCurrentSong() 方法播放对应歌曲。

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                    @IBAction func previousSong(_ sender: UIButton) {
                        currentSongIndex -= 1
                        if currentSongIndex = allSongs.count {
                            currentSongIndex = 0
                        }
                        playCurrentSong()
                    }
                

                6.收藏列表功能

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                收藏列表功能允许用户将歌曲添加至收藏列表,并管理自己的收藏列表,包括查看、删除和移动歌曲。以下是对歌曲收藏功能的实现描述:

                **①加载和保存收藏列表:**在 loadFavorites() 方法中,从 UserDefaults 中加载保存的收藏歌曲数据,使用 PropertyListDecoder 进行解码,将解码后的数据赋值给 favorites 数组。

                func loadFavorites(){
                        if let savedFavoritesData = UserDefaults.standard.data(forKey: "favorites"),
                           let savedFavorites = try? PropertyListDecoder().decode([Music].self, from: savedFavoritesData){
                            favorites = savedFavorites
                        }
                    }
                

                在 saveFavorites() 方法中,使用 PropertyListEncoder 将当前的收藏歌曲数组进行编码,并保存到 UserDefaults 中,实现数据的持久化。

                func saveFavorites(){
                        if let encodedData = try? PropertyListEncoder()
                            .encode(favorites){
                            UserDefaults.standard.set(encodedData, forKey: "favorites")
                        }
                    }
                

                ②编辑模式切换:实现 editAction(_: ) 方法,该方法在点击编辑按钮时被调用,用于切换表格视图的编辑模式。通过设置 tableView.isEditing 属性,切换表格视图的编辑状态,并根据编辑状态改变编辑按钮的标题。

                self.tableView.isEditing = !self.tableView.isEditing
                            switch tableView.isEditing {
                            case true:
                                editButton.title = "确认"
                            default:
                                editButton.title = "管理收藏"
                            }
                

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                ③表格数据源方法:

                重写 numberOfSections(in:) 方法,返回表格视图的分区数,此处只有一个分区。

                重写 tableView(:numberOfRowsInSection:) 方法,返回收藏歌曲的数量。

                重写 tableView(:cellForRowAt:) 方法,配置表格视图中的每一行,显示歌曲信息。

                ④编辑样式和操作:

                重写 tableView(:editingStyleForRowAt:) 方法,根据编辑状态返回相应的编辑样式,此处为删除样式。

                重写 tableView(:commit:forRowAt:) 方法,处理用户对行的删除操作,包括从数组中移除歌曲、删除对应的表格行以及保存更新后的收藏列表到 UserDefaults。

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                **⑤移动行操作:**重写 tableView(:moveRowAt:to:) 方法,处理用户移动行的操作,包括在收藏数组中移动歌曲,并保存更新后的收藏列表到 UserDefaults。

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                **⑥刷新数据:**在 viewWillAppear(: ) 方法中,每次视图即将显示时调用 loadFavorites() 方法加载最新的收藏列表,并刷新表格视图显示。

                7.跳转云音乐平台功能

                iOS移动应用开发技术课程设计——音乐播放器(附代码与视频教程)

                云音乐平台功能是通过使用 WKWebView 控件在应用内显示网易云音乐网站的功能。以下是对该功能的实现描述:

                **①导航至网易云音乐网站:**在 viewDidLoad() 方法中,初始化 WKWebView 控件并设置其导航代理为当前视图控制器;构建一个 URLRequest 对象,其中包含网易云音乐网站的 URL。通过调用 load(_: ) 方法加载网页请求,显示网易云音乐网站内容。

                override func viewDidLoad() {
                        super.viewDidLoad()
                        self.webView.navigationDelegate = self
                        if let url = URL(string: "http://music.163.com") {
                            let request = URLRequest(url: url)
                            self.webView.load(request)
                        }
                    }
                

                ②WKNavigationDelegate 方法:

                实现 webView(_:didFinish:) 方法,该方法在网页加载完成时被调用,此处打印了 “Finish” 表示加载完成。

                func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
                        print("Finish")
                    }
                

                四、代码附录

                RegisterViewController类:

                import UIKit
                class RegisterViewController: UIViewController {
                    
                    
                    @IBOutlet weak var userId: UITextField!
                    
                    @IBOutlet weak var password: UITextField!
                    
                    var userpwd = [String:String]()
                    
                    override func viewDidLoad() {
                        super.viewDidLoad()
                    }
                    
                    @IBAction func register(_ sender: UIButton) {
                        let user = userId.text!
                        let pwd = password.text!
                        userpwd = UserDefaults.standard.object(forKey: "userpwd") as?[String:String] ?? [String:String]()
                        userpwd[user] = pwd
                        
                        UserDefaults.standard.set(userpwd, forKey: "userpwd")
                        UserDefaults.standard.synchronize()
                        
                        let ok = UIAlertAction(title:"确定", style:.default){
                            action in self.dismiss(animated: true, completion: nil)
                        }
                        let alter = UIAlertController(title: "注册成功", message: nil, preferredStyle: .alert)
                        alter.addAction(ok)
                        self.present(alter,animated: true, completion: nil)
                    }
                    
                }
                

                LoginViewController类:

                import UIKit
                class LoginViewController: UIViewController {
                    
                    @IBOutlet weak var userId: UITextField!
                    @IBOutlet weak var password: UITextField!
                    
                    var userpwd = [String:String]()
                    override func viewDidLoad() {
                        super.viewDidLoad()
                    }
                    
                    @IBAction func Login(_ sender: UIButton) {
                        let user = userId.text!
                        let pwd = password.text!
                        userpwd = UserDefaults.standard.object(forKey: "userpwd") as?[String:String] ?? [String:String]()
                        
                        if userpwd[user] == pwd {
                            self.performSegue(withIdentifier: "LOGIN", sender: nil)
                        }else{
                            let alertController = UIAlertController(title: "登录失败", message: nil, preferredStyle: .alert)
                            self.present(alertController, animated: true, completion: nil)
                            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1){
                                self.presentedViewController?.dismiss(animated: false, completion: nil)
                            }
                        }
                    }
                }
                

                MusicTableViewController类:

                import UIKit
                class MusicTableViewController: UITableViewController {
                    
                    var listMusics:NSArray!
                    var listData:[String:String]!
                    
                    override func viewDidLoad() {
                        super.viewDidLoad()
                        
                        let plistPath = Bundle.main.path(forResource: "MusicList", ofType: "plist")
                        self.listMusics = NSArray(contentsOfFile: plistPath!)
                      
                    }
                    
                    // MARK: - Table view data source
                    override func numberOfSections(in tableView: UITableView) -> Int {
                        return 1
                    }
                    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                        return listMusics.count
                    }
                    
                    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                        let cell = tableView.dequeueReusableCell(withIdentifier: "ID", for: indexPath) as! MusicTableViewCell
                        let row = indexPath.row
                        
                        let rowDict = self.listMusics[row] as! [String:String]
                        
                        cell.myName.text = rowDict["name"]
                        cell.mySinger.text = rowDict["singer"]
                        
                        let imagePath = String(format: "%@.png" , rowDict["image"]!)
                        cell.myImageView.image = UIImage(named: imagePath)
                        
                        cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
                        
                        return cell
                    }
                    // MARK: - Navigation
                    
                    // In a storyboard-based application, you will often want to do a little preparation before navigation
                    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
                        if segue.identifier == "PLAY" {
                            let playingViewController = segue.destination as! PlayingViewController
                            let indexPath = self.tableView.indexPathForSelectedRow as IndexPath?
                            let selectedIndex = indexPath!.row
                            let rowDict = self.listMusics[selectedIndex] as! [String:String]
                            
                            playingViewController.listData = rowDict
                        }
                    }
                }
                

                MusicTableViewCell子类:

                import UIKit
                class MusicTableViewCell: UITableViewCell {
                    
                    
                    @IBOutlet weak var myImageView: UIImageView!
                    
                    @IBOutlet weak var myName: UILabel!
                    
                    @IBOutlet weak var mySinger: UILabel!
                    
                    override func awakeFromNib() {
                        super.awakeFromNib()
                    }
                    override func setSelected(_ selected: Bool, animated: Bool) {
                        super.setSelected(selected, animated: animated)
                    }
                }
                

                PlayingViewController类:

                import UIKit
                import AVFoundation
                extension Collection {
                    subscript(safe index: Index) -> Element? {
                        return indices.contains(index) ? self[index] : nil
                    }
                }
                class PlayingViewController: UIViewController, AVAudioPlayerDelegate {
                    
                    
                    var listData: [String:String]!
                    var allSongs: [Music] = []
                    var currentSongIndex: Int = 0
                    
                    @IBOutlet weak var ImageView: UIImageView!
                    
                    @IBOutlet weak var label: UILabel!
                    
                    @IBOutlet weak var btnplay: UIButton!
                    
                    var player:AVAudioPlayer!
                    
                    override func viewDidLoad() {
                        super.viewDidLoad()
                        
                        allSongs = loadMusicData()
                        
                        self.label.text = "播放停止"
                        
                        let imagePath = String(format: "%@.png", listData["image"]!)
                        ImageView.image = UIImage(named: imagePath)
                        
                        progressSlider.maximumValue = Float(player.duration)
                            progressSlider.value = 0.0
                            
                        // 添加事件处理函数
                        progressSlider.addTarget(self, action: #selector(progressSliderValueChanged(_:)), for: .valueChanged)
                    }
                    
                    func loadMusicData() -> [Music] {
                        
                        return []
                    }
                    
                    func updateUI(){
                        
                    }
                    @objc func progressSliderValueChanged(_ sender: UISlider) {
                        // 当进度条数值发生变化时,更新音乐播放器的播放进度
                        let newPosition = Double(sender.value)
                        player.currentTime = TimeInterval(newPosition)
                    }
                    
                    func playCurrentSong(){
                        guard let currentSong = allSongs[safe: currentSongIndex] else {
                            return
                        }
                        
                        let songURL = Bundle.main.url(forResource: currentSong.name, withExtension: "mp3")
                        
                        do{
                            player = try AVAudioPlayer(contentsOf: songURL!)
                            player.prepareToPlay()
                            player.numberOfLoops = -1
                            player.delegate = self
                            player.play()
                        } catch {
                            print("创建失败: \(error.localizedDescription)")
                        }
                        updateUI()
                    }
                    
                    
                    @IBOutlet weak var progressSlider: UISlider!
                    
                    @IBAction func play(_ sender: UIButton) {
                        /*
                         sender.isHighlighted = false
                         //检查播放按钮状态
                         //print("按钮状态:\(sender.state.rawValue)")
                         if self.player == nil {
                         // 获取音乐文件的完整路径
                         if let musicFilePath = Bundle.main.url(forResource: listData["name"], withExtension: "mp3", subdirectory: "MusicFile") {
                         //检查是否读取了正确的文件路径
                         //print("\(musicFilePath)")
                         
                         */
                        
                        if self.player == nil {
                            let path = Bundle.main.path(forResource: listData["name"]!, ofType: "mp3")
                            
                            let url = URL(fileURLWithPath: path!)
                            
                            do {
                                self.player = try AVAudioPlayer(contentsOf: url)
                            } catch let error as NSError {
                                self.player = nil
                                print(error.description)
                                self.label.text = "播放错误"
                                return
                            }
                            
                            self.player.prepareToPlay()
                            self.player.numberOfLoops = -1
                            player.delegate = self
                        }
                        
                        if !self.player.isPlaying {
                            player.play()
                            self.label.text = "正在播放"
                            
                            let pauseImage = UIImage(named: "pause")
                            self.btnplay.setImage(pauseImage, for: UIControl.State())
                        }else{
                            player.pause()
                            self.label.text = "播放暂停"
                            let playImage = UIImage(named: "play")
                            self.btnplay.setImage(playImage, for: UIControl.State())
                        }
                    }
                    
                    @IBAction func stop(_ sender: UIButton) {
                        if let player = self.player, player.isPlaying {
                            player.stop()
                            player.delegate = nil
                            self.player = nil
                            self.label.text = "播放停止"
                            
                            let playImage = UIImage(named: "play")
                            self.btnplay.setImage(playImage, for: .normal)
                        }
                    }
                    
                    
                    @IBAction func addToFavorites(_ sender: UIButton) {
                        guard let listData = listData else {return}
                        let music = Music(name: listData["name"]!, singer: listData["singer"]!)
                        
                        MusicFavorites.shared.addFavorite(music)
                        
                        sender.setTitle("已收藏", for: .normal)
                    }
                    
                    @IBAction func viewFavorites(_ sender: UIButton) {
                        performSegue(withIdentifier: "ViewFavorites", sender: self)
                    }
                    
                    
                    @IBAction func previousSong(_ sender: UIButton) {
                        currentSongIndex -= 1
                        if currentSongIndex = allSongs.count {
                            currentSongIndex = 0
                        }
                        playCurrentSong()
                    }
                }
                

                CloudViewController类:

                import UIKit
                import WebKit
                class CloudViewController: UIViewController, WKNavigationDelegate {
                    
                    
                    @IBOutlet weak var webView: WKWebView!
                    
                    override func viewDidLoad() {
                        super.viewDidLoad()
                        self.webView.navigationDelegate = self
                        if let url = URL(string: "http://music.163.com") {
                            let request = URLRequest(url: url)
                            self.webView.load(request)
                        }
                    }
                    // WKNavigationDelegate 方法
                    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
                        print("Finish")
                    }
                }
                

                FavoritesTableViewCell子类

                import UIKit
                class FavoritesTableViewCell: UITableViewCell {
                    @IBOutlet weak var songNameLabel: UILabel!
                    
                    @IBOutlet weak var singerLabel: UILabel!
                    
                    func configureCell(with song: Music){
                        songNameLabel.text = song.name
                        singerLabel.text = song.singer
                    }
                    
                    override func awakeFromNib() {
                        super.awakeFromNib()
                    }
                    override func setSelected(_ selected: Bool, animated: Bool) {
                        super.setSelected(selected, animated: animated)
                    }
                }
                

                FavoritesTableViewController类:

                import UIKit
                class FavoritesTableViewController: UITableViewController {
                    
                    @IBOutlet weak var editButton: UIBarButtonItem!
                    
                    var favorites: [Music] = []
                    
                    override func viewDidLoad() {
                        super.viewDidLoad()
                        loadFavorites()
                        
                        tableView.reloadData()
                        
                    }
                    
                    func loadFavorites(){
                        if let savedFavoritesData = UserDefaults.standard.data(forKey: "favorites"),
                           let savedFavorites = try? PropertyListDecoder().decode([Music].self, from: savedFavoritesData){
                            favorites = savedFavorites
                        }
                    }
                    
                    func saveFavorites(){
                        if let encodedData = try? PropertyListEncoder()
                            .encode(favorites){
                            UserDefaults.standard.set(encodedData, forKey: "favorites")
                        }
                    }
                    
                    
                    @IBAction func editAction(_ sender: UIBarButtonItem) {
                        //切换编辑模式
                        self.tableView.isEditing = !self.tableView.isEditing
                            switch tableView.isEditing {
                            case true:
                                editButton.title = "确认"
                            default:
                                editButton.title = "管理收藏"
                            }
                    }
                    
                    
                    // MARK: - Table view data source
                    
                    override func numberOfSections(in tableView: UITableView) -> Int {
                        return 1
                    }
                    
                    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                        // 获取收藏的歌曲数量
                        //return MusicFavorites.shared.favoriteSongs.count
                        return favorites.count
                    }
                    
                    
                    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                        let cell = tableView.dequeueReusableCell(withIdentifier: "FavoritesCell", for: indexPath) as! FavoritesTableViewCell
                        //获取对应位置的歌曲信息
                        //let song = MusicFavorites.shared.favoriteSongs[indexPath.row]
                        let song = favorites[indexPath.row]
                        cell.configureCell(with: song)
                        return cell
                    }
                    
                    override func viewWillAppear(_ animated: Bool) {
                        super.viewWillAppear(animated)
                        loadFavorites()
                        tableView.reloadData()
                    }
                    
                    override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
                        if (self.tableView.isEditing) {
                            return UITableViewCell.EditingStyle.delete
                        }
                        return UITableViewCell.EditingStyle.none
                    }
                    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
                        if editingStyle == .delete {
                            favorites.remove(at: indexPath.row)
                            tableView.deleteRows(at: [indexPath], with: .fade)
                            // 保存收藏数组到UserDefaults
                            saveFavorites()
                        }
                    }
                    override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
                        let movedSong = favorites[sourceIndexPath.row]
                        favorites.remove(at: sourceIndexPath.row)
                        favorites.insert(movedSong, at: destinationIndexPath.row)
                        saveFavorites()
                    }
                    
                }
                

                MusicFavorites类:

                import Foundation
                class MusicFavorites{
                    static let shared = MusicFavorites()
                    
                    private init(){
                        
                    }
                    var favoriteSongs: [Music] = []
                    
                    func addFavorite(_ song: Music){
                        favoriteSongs.append(song)
                        saveFavorites()
                    }
                    
                    func saveFavorites(){
                        if let encodedData = try? PropertyListEncoder() .encode(favoriteSongs){
                            UserDefaults.standard.set(encodedData, forKey: "favorites")
                        }
                    }
                }
                

                Music类:

                import Foundation
                class Music: Codable {
                    var name: String
                    var singer: String
                    init(name: String, singer: String) {
                        self.name = name
                        self.singer = singer
                    }
                }
                
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码