技术:SwiftUI、SwiftUI3.0、音频播放器、自定义音频播放器、AVAudioPlayer
运行环境:
SwiftUI3.0 + Xcode13.4.1 + MacOS12.5 + iPhone Simulator iPhone 13 Pro Max
SwiftUI自定义音频播放器
使用SwiftUI自定义音频播放器


思路:
1.搭建歌曲图片、标题
2.搭建歌曲的操作按钮 比如 上一首、向前15秒、开始暂停、向后15秒、下一首
3.进度条的控制 - 和当前歌曲播放进度一样
4.检查开始和暂停的控制 - 是不是自动播放完成的操作、手动暂停的操作、控制是否正在播放的属性
5.监听手势拖拽进度条的时候 同步歌曲
MusicPlayer

图片logo1张
音频文件2个


主要是展示音频播放器UI 以及提供2个方法
- 获取歌曲的数据 比如标题、歌曲的图片
- 提供一个 切换歌曲的方法
- 监听 歌曲播放完成的方法
//
// ContentView.swift
// Shared
//
// Created by lyh on 2022/8/26.
//
import SwiftUI
import AVKit
struct ContentView: View {
var body: some View {
NavigationView{
MusicPlayer().navigationTitle("Music Player")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct MusicPlayer : View{
@State var data : Data = .init(count: 0)
@State var title = ""
@State var player : AVAudioPlayer!
@State var playing = false
@State var width : CGFloat = 0
@State var songs = ["black","bad"] // 总音频列表
@State var current = 0 // 当前播放的第几首音频
@State var finish = false // 是否播放完成
@State var del = AVDelegate() // 播放器的代理
var body: some View{
VStack(spacing:20){
Image(uiImage:self.data.count == 0 ? UIImage(named: "itunes")! : UIImage(data: self.data)!)
.resizable()
.frame(width: self.data.count == 0 ? 250 : nil, height: 250)
.cornerRadius(15)
Text(self.title).font(.title).padding(.top)
ZStack(alignment: .leading) {
Capsule().fill(Color.black.opacity(0.08)).frame(height:8)
// 红色进度等于播放进度
Capsule().fill(Color.red).frame(width:self.width,height:8)
// 拖拽进度条处理
.gesture(DragGesture()
.onChanged({ value in
let x = value.location.x
self.width = x
})
.onEnded({ value in
let x = value.location.x
let screen = UIScreen.main.bounds.width - 30
// 百分比
let percent = x / screen
self.player.currentTime = Double(percent) * self.player.duration
})
)
}
HStack(spacing:UIScreen.main.bounds.width / 5 - 30) {
// 1.返回上一首
Button {
// 检测歌曲的索引
if self.current > 0{
self.current -= 1
self.ChangeSongs()
}
} label: {
Image(systemName: "backward.fill").font(.title)
}
// 2.返回前15秒
Button {
self.player.currentTime -= 15
} label: {
Image(systemName: "gobackward.15").font(.title)
}
// 3.播放暂停
Button {
if self.player.isPlaying {
self.player.pause()
self.playing = false
}
else {
if self.finish {
self.player.currentTime = 0
self.width = 0
self.finish = false
}
self.player.play()
self.playing = true
}
} label: {
// 如果是播放完成 也要变回到播放按钮
Image(systemName:self.playing && !self.finish ? "pause.fill" : "play.fill").font(.title)
}
// 4.向后跳15秒
Button {
let increase = self.player.currentTime + 15
if increase < self.player.duration {
self.player.currentTime = increase
}
} label: {
Image(systemName: "goforward.15").font(.title)
}
// 5.下一首
Button {
// 检测歌曲的索引是否越界
if self.songs.count - 1 != self.current {
self.current += 1
self.ChangeSongs()
}
} label: {
Image(systemName: "forward.fill").font(.title)
}
}
.padding(.top,25)
.foregroundColor(Color.black)
}
.padding()
.onAppear{
let url = Bundle.main.path(forResource: self.songs[self.current], ofType: "mp3")
self.player = try! AVAudioPlayer(contentsOf: URL(fileURLWithPath: url!))
self.player.prepareToPlay()
self.player.delegate = self.del
// 获取音频数据 并且把音频数据得到的data存储起来
self.getData()
// 启动定时器 播放时候 进度同步更新
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (_) in
if self.player.isPlaying {
// 测试播放进度
//print(self.player.currentTime)
let screen = UIScreen.main
.bounds.width - 30
let value = self.player.currentTime / self.player.duration
self.width = screen * CGFloat(value)
}
}
// 使用通知监听歌曲是否播放完成
NotificationCenter.default.addObserver(forName: NSNotification.Name("Finish"), object: nil, queue: .main) { (_) in
self.finish = true
}
}
}
func getData(){
let asset = AVAsset(url: self.player.url!)
for i in asset.commonMetadata {
if i.commonKey?.rawValue == "artwork" {
let data = i.value as! Data
self.data = data
}
if i.commonKey?.rawValue == "title"{
let title = i.value as! String
self.title = title
}
}
}
func ChangeSongs(){
let url = Bundle.main.path(forResource: self.songs[self.current], ofType: "mp3")
self.player = try! AVAudioPlayer(contentsOf: URL(fileURLWithPath: url!))
self.player.prepareToPlay()
// 切换歌曲 重新初始化音频的数据 比如 data 歌曲的图片 title 歌曲的名称
self.data = .init(count: 0)
self.title = ""
self.getData()
self.playing = true
self.finish = false
self.width = 0
// 切换之后 直接播放歌曲
self.player.play()
}
}
// 通过代理监听歌曲是否播放完成
// 使用通知告诉视图 操作播放完成的操作
class AVDelegate : NSObject,AVAudioPlayerDelegate {
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
NotificationCenter.default.post(name: NSNotification.Name("Finish"), object: nil)
}
}
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or
我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano