我的 Swift 程序中有几个相互嵌套的视图。在最低级别的视图中,我有一个滑块。我想将它的值从这个视图传递到全局变量存储在结构中的主 Swift 文件。该值将用于控制使用 AudioKit 播放的音频的音量。
滑块代码(此处不包括完整视图,因为其余部分无关紧要):
struct MasterFaderView: View {
var body: some View {
Slider(
value: master.Value,
in: 0...127,
step: 1.0)
}
}
正在设置的顶级全局变量:
struct channel {
var Value: Double = 0.0 // Default fader value
... // Other variables being stored
}
var master = channel()
@main
struct MEngProjectApp: App {
var body: some Scene {
WindowGroup {
FreePlayView()
}
}
}
FreePlayView() 是主视图,其中包含 audioKit 内容并将控制音量:
class MixerClass: ObservableObject {
let engine = AudioEngine()
var fullTrack = AudioPlayer()
init() {
engine.output = fullTrack
loadFiles()
try? engine.start()
}
func loadFiles() {
do {
if let fileURL = Bundle.main.url(forResource: "DQ_FullTrack", withExtension: "wav") {
try fullTrack.load(url: fileURL)
} else {
Log("Could not find file")
}
} catch {
Log("Could not load full track")
}
}
}
struct FreePlayView: View {
@StateObject var conductor = MixerClass()
@State var trackPlaying: Bool = false
var body: some View {
Button(action: {
if trackPlaying == false {
conductor.fullTrack.start()
conductor.fullTrack.volume = master.Value
trackPlaying = true
} else {
conductor.fullTrack.stop()
trackPlaying = false
}
}) {
Text("Start Audio")
}
我在 MasterFaderView 中得到的错误代码是
Cannot convert value of type 'Double' to expected argument type 'Binding<Double>'
有人知道如何解决这个问题吗?
提前致谢。
要在你的应用程序的不同地方使用一个变量,你可以使用一个
ObservableObject
类,
带有@Published var value: Double = 0.0
,如class ChannelModel: ObservableObject
所示。
每当 value
改变时,UI 将刷新。
在您的应用程序中声明此
ChannelModel
,并且
使用 .environmentObject(channelModel)
和传递它
@EnvironmentObject var channel: ChannelModel
如图所示
FreePlayView
和 MasterFaderView
.
这里是这种方法的一些示例代码:
@main
struct MEngProjectApp: App {
@StateObject var channelModel = ChannelModel() // <-- here
var body: some Scene {
WindowGroup {
FreePlayView()
.environmentObject(channelModel) // <-- here
}
}
}
class ChannelModel: ObservableObject { // <-- here
@Published var value: Double = 0.0 // <-- here
// ... Other variables being stored
}
struct FreePlayView: View {
@EnvironmentObject var channelModel: ChannelModel // <-- here
@StateObject var conductor = MixerClass()
@State var trackPlaying: Bool = false
var body: some View {
Button(action: {
if trackPlaying == false {
conductor.fullTrack.start()
conductor.fullTrack.volume = channelModel.value // <-- here
trackPlaying = true
} else {
conductor.fullTrack.stop()
trackPlaying = false
}
}) {
Text("Start Audio")
}
}
}
struct MasterFaderView: View {
@EnvironmentObject var channelModel: ChannelModel // <-- here
var body: some View {
Slider(value: $channelModel.value, in: 0...127, step: 1.0) // <-- here
}
}
@Wpitchy,
在您的代码片段中,我没有看到您在哪里通过滑块的值更新
fullTrack.volume
。
我还会将
@Published var fullTrackVolume
变量保留在 Conductor
中,而不是在它自己的 ChannelMode
类中,因为它会影响在那里实例化的 fullTrack
AudioPlayer。
这是我要改变的:
MEngProjectApp.swift
import SwiftUI
@main
struct MEngProjectApp: App {
// If you want the MixerClass to be available in multiple views (not just FreePlayView), you will want to instantiate it as an EnvironmentObject in the App level.
@StateObject var conductor = MixerClass()
var body: some Scene {
WindowGroup {
FreePlayView()
.environmentObject(conductor) // <- Passing this object into this parent view will allow the MixerClass to be accessible from the FreePlayView and any of its child views.
}
}
}
MixerClass.swift
import AudioKit
import AVFoundation
import Foundation
final class MixerClass: ObservableObject {
let engine = AudioEngine()
@Published var fullTrack = AudioPlayer() // <- Published vars broadcast any updates that occur, such as volume level changes.
@Published var isTrackPlaying: Bool = false
init() {
// Importing AVFoundation is required in order to access the AVAudioSession settings.
do {
Settings.bufferLength = .medium
try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(Settings.bufferLength.duration)
try AVAudioSession.sharedInstance().setCategory(.playAndRecord,
options: [.defaultToSpeaker,
.mixWithOthers,
.allowBluetoothA2DP])
try AVAudioSession.sharedInstance().setActive(true)
} catch let err {
print(err)
}
loadFiles()
fullTrack.isLooping = true // <- Set this to true, if you don't want the audio clip to stop playing.
engine.output = fullTrack
try? engine.start()
}
func loadFiles() {
do {
if let fileURL = Bundle.main.url(forResource: "DQ_FullTrack", withExtension: "wav") {
try fullTrack.load(url: fileURL)
} else {
Log("Could not find file")
}
} catch {
Log("Could not load full track")
}
}
func convertVolumeToPercent() -> String {
"\(Int(fullTrack.volume * 100))%" // <- Converts 0.5 to 50% and 1.0 to 100%, etc.
}
// MARK: User Intent Actions from any or multiple views.
// This way, the business logic "decision-making" from triggered events will remain outside of any of the views. These actions can be repurposed, and not tied to any view code.
func playStopToggle() {
isTrackPlaying ? fullTrack.stop() : fullTrack.start()
isTrackPlaying.toggle()
}
}
FreePlayView.swift
import SwiftUI
struct FreePlayView: View {
@EnvironmentObject var conductor: MixerClass
var body: some View {
VStack {
MasterFaderView()
playStopButton
}
}
private var playStopButton: some View {
Button(action: {
conductor.playStopToggle()
}) {
Text("\(Image(systemName: conductor.isTrackPlaying ? "stop.fill" : "play.fill")) \(conductor.isTrackPlaying ? "Stop" : "Start") Audio")
}
}
}
struct FreePlayView_Previews: PreviewProvider {
static var previews: some View {
FreePlayView()
.environmentObject(MixerClass()) // <- Pass in the MixerClass in order for the SwiftUI Preview to work without crashing.
}
}
MasterFaderView.swift
import SwiftUI
struct MasterFaderView: View {
@EnvironmentObject var conductor: MixerClass
var body: some View {
VStack {
Text(conductor.convertVolumeToPercent())
.font(.title).fontWeight(.thin)
Slider(value: $conductor.fullTrack.volume, in: 0...1) // <- Volume in the AudioPlayer is 0.0-1.0, not 0-127.
.padding()
}
}
}
struct MasterFaderView_Previews: PreviewProvider {
static var previews: some View {
MasterFaderView()
.environmentObject(MixerClass()) // <- Pass in the MixerClass in order for the SwiftUI Preview to work without crashing.
}
}
如果有帮助,请告诉我。
如果您想在更大的 Xcode 项目中看到它,请查看以下内容: