SwiftUI 在变量更改时调用函数

标签 swift swiftui watchos

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

音量控制根据当前状态改变铃声的进度volumevolume当我转动数码表冠时也会发生变化。
如果没有 SwiftUI,就可以在转动表冠时调用函数。这已经改变,系统只允许我将一个变量绑定(bind)到它。

我想知道的是绑定(bind)一个变量并在该变量的每次更改时调用一个函数。因为正常的 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:))

OnReceive 将在每次旋转表冠时调用,但也会在 View 的其他所有更新时调用。

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

表冠旋转 → crownAccumulator更改 → 函数调用异步 → 函数更新 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:))



这是当前 View :
Current View

最佳答案

您可以使用自定义 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)
            }
        )
    }
}

关于SwiftUI 在变量更改时调用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59379132/

相关文章:

iOS - watchOS App 发布问题 CFBundleIdentifier 冲突

ios - 使用 WatchOS App 时未调用 viewDidLoad

ios - 如何以编程方式检查用户是否为 iOS 应用程序启用了计划摘要通知?

SwiftUI onTapGesture 不适用于 Mac 应用程序中的 ObservedObject

ios - 为什么 NSDate 字符串中有一些奇怪的字符

ios - 导航栏按钮项目 swift 的图像

swift - 分段选择器移除了可访问性

ios - 在 iOS 和 watchOS 之间共享类

ios - 使用 Cocoapods v1.0.x 拉取 pod 时未构建 MagicalRecord

ios - 通知控制中心同时播放多个音频