SwiftUI 在变量更改时调用函数

Sn0*_*eze 17 swift watchos swiftui

我正在尝试将 watchOS 应用程序的视图转换为 Swift UI。我想将 watchKit 中的音量控制移植到带有自定义控件的 SwiftUI。在下图中,您可以看到视图的当前状态。

音量控制会根据当前状态更改铃声的进度,volume并且volume在我转动数码表冠时也会更改。如果没有 SwiftUI,就可以在旋转表冠时调用一个函数。这已经改变了,系统只允许我绑定一个变量。

我想知道的是绑定一个变量并在该变量的每次更改时调用一个函数。因为正常的 Digital Crown 行为不能满足我的需求。

一件事有效,但远非完美是:

.digitalCrownRotation($crownAccumulator, from: -100.0, through: 100.0, by: 1, sensitivity: .low, isContinuous: true, isHapticFeedbackEnabled: true)
.onReceive(Just(self.crownAccumulator), perform: self.crownChanged(crownAccumulator:))
Run Code Online (Sandbox Code Playgroud)

OnReceive 将在每次旋转表冠时调用,但也会在视图的每一次其他更新时调用。

所以我想要的是这样的管道:

皇冠旋转?crownAccumulator变化 ?名为 async 的函数?功能更新volume

过去我会使用 didSet 来完成此操作,但这不再可用

这里是它的代码:


    @ObservedObject var group: Group
    @State var animateSongTitle: Bool = false

    @State var songTitle: String = "Very long song title that should be scrolled"
    @State var artist: String = "Artist name"


    @State var volume: Int = 30
    @State var isMuted = false
    @State var crownAccumulator: CGFloat = 0.0


    var body: some View {

       VStack(alignment: .center) {
            TitleView(songTitle: $songTitle, artist: $artist)
            GroupControlButtons(
                skipPreviousAction: skipPrevious,
                skipNextAction: skipNext,
                playPauseAction: playPause,
                group: group)

            ZStack {
                HStack {
                    VolumeControl(
                        volumeLevel: $volume,
                        isMuted: $isMuted,
                        muteAction: self.muteButtonPressed)
                            .frame(minWidth: 40.0, idealWidth: 55, minHeight: 40.0, idealHeight: 55, alignment: .center)
                            .aspectRatio(1.0, contentMode: .fit)


                }
            }


        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
        .edgesIgnoringSafeArea([.bottom])
        .focusable(true)
//        .digitalCrownRotation($crownAccumulator)
        .digitalCrownRotation($crownAccumulator, from: -100.0, through: 100.0, by: 1, sensitivity: .low, isContinuous: true, isHapticFeedbackEnabled: true)
        .onReceive(Just(self.crownAccumulator), perform: self.crownChanged(crownAccumulator:))


Run Code Online (Sandbox Code Playgroud)

这是当前的视图:
目前来看

Yon*_*nat 14

您可以使用自定义Binding,它在set. 例如来自:

extension Binding {
    /// Execute block when value is changed.
    ///
    /// Example:
    ///
    ///     Slider(value: $amount.didSet { print($0) }, in: 0...10)
    func didSet(execute: @escaping (Value) ->Void) -> Binding {
        return Binding(
            get: {
                return self.wrappedValue
            },
            set: {
                self.wrappedValue = $0
                execute($0)
            }
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我真的不明白这个“黑客”。SwiftUI 的原理是自动对绑定变化做出反应,那么为什么要添加一些额外的代码呢?您在“@Published”属性上设置一个值,然后所有视图都会重新绘制。如果您仅使用组合,所有观察者都会收到通知,然后您在正确的位置执行所需的操作。$property.sink { // 执行此操作或执行此操作 }。 (2认同)

Ami*_*mir 6

正如您所说的“过去我会使用 didSet 来完成此操作,但这不再可用”我测试了下面的代码,它可以完美运行

struct pHome: View {
 @State var prog:Int = 2 {
    willSet{
        print("willSet: newValue =\(newValue)  oldValue =\(prog)") 
    }
    didSet{
        print("didSet: oldValue=\(oldValue) newValue=\(prog)")
        //Write code, function do what ever you want todo 
    }
} 
 var body: some View {
VStack{
                Button("Update"){
                    self.prog += 1
                }
                Text("\(self.prog)%")
  }
 }
}
Run Code Online (Sandbox Code Playgroud)

  • didSet 不为我执行。State 变量被传递到它所绑定的单独 View。在这种情况下,父视图中的 didSet 显然没有被调用。 (2认同)