SwiftUI:通过外部调用的函数更改@State变量?

问题描述 投票:0回答:1

所以也许我误解了SwiftUI的工作原理,但是我已经尝试了一个多小时,但仍然无法弄清。

struct ContentView: View, AKMIDIListener {
    @State var keyOn: Bool = false

    var key: Rectangle = Rectangle()

    var body: some View {
        VStack() {
            Text("Foo")
            key
                .fill(keyOn ? Color.red : Color.white)
                .frame(width: 30, height: 60)
        }
        .frame(width: 400, height: 400, alignment: .center)
    }

    func receivedMIDINoteOn(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel, portID: MIDIUniqueID? = nil, offset: MIDITimeStamp = 0) {
        print("foo")
        keyOn.toggle()
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

所以这个想法非常简单。我有一个使用AudioKit的外部Midi键盘。当按下键盘上的某个键时,矩形应从白色变为红色。

receivedMIDINoteOn函数被调用并且'foo'被打印到控制台,尽管keyOn.toggle()出现在同一函数中,但这仍然无法正常工作。

执行此操作的正确方法是什么?

谢谢

swift swiftui declarative
1个回答
0
投票

是的,您认为它有些错误。 @State通常用于内部状态更改。是否有您的View直接引用的按钮?使用@State。当您不(或至少不应)拥有该州时,应使用@Binding。通常,当我有一个应该影响子视图或受子视图影响的父视图时,才使用此选项。

但是您可能想要的是@ObservedObject。这允许外部对象发布更改,并且您的View订阅这些更改。因此,如果您有一些Midi监听对象,请将其设为ObservableObject

final class MidiListener: ObservableObject, AKMIDIListener {
  // 66 key keyboard, for example
  @Published var pressedKeys: [Bool] = Array(repeating: false, count: 66)

  init() {
    // set up whatever private storage/delegation you need here
  }

  func receivedMIDINoteOn(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel, portID: MIDIUniqueID? = nil, offset: MIDITimeStamp = 0) {
    // how you determine which key(s) should be pressed is up to you. if this is monophonic the following will suffice while if it's poly, you'll need to do more work
    DispatchQueue.main.async {
      self.pressedKeys[Int(noteNumber)] = true
    }
  }
}

现在在您看来:

struct KeyboardView: View {
  @ObservedObject private var viewModel = MidiListener()

  var body: some View {
    HStack {
      ForEach(0..<viewModel.pressedKeys.count) { index in
        Rectangle().fill(viewModel.pressedKeys[index] ? Color.red : Color.white)
      }
    }
  }
}

但是更好的方法是将您的收听内容包装在发布这些事件的自定义Combine.Publisher中。我将把它作为一个单独的问题,如何做。

© www.soinside.com 2019 - 2024. All rights reserved.