首页 > 解决方案 > FocusedValue / FocusedBinding 在 iPad 上不起作用

问题描述

目标

问题

问题:

环境

代码

@main
struct TestApp: App {
    @State private var price = 0

    var body: some Scene {
        WindowGroup {
            ContentView(price: $price)
                .focusedValue(\.price, $price)
        }
        .commands {
            PriceCommands()
        }
    }
}

struct ContentView: View {
    
    @Binding var price: Int
    
    var body: some View {
        IncreasePriceButton(price: $price)
    }
}

struct IncreasePriceButton: View {
    
    @Binding var price: Int
    
    var body: some View {
        Button("Increase Price") {
            price += 1
            print("price = \(price)")
        }
    }
}

struct PriceCommandButton: View {
    
    @FocusedBinding(\.price) var price
    
    var body: some View {
        Button("Print Price") {
            print("price = \(price)")
        }
    }
}

struct PriceCommands: Commands {
    var body: some Commands {
        CommandMenu("Custom") {
            PriceCommandButton()
                .keyboardShortcut(KeyboardShortcut("C", modifiers: [.command, .shift]))
        }
    }
}

struct FocusedPriceKey: FocusedValueKey {
    typealias Value = Binding<Int>
}

extension FocusedValues {
    var price: FocusedPriceKey.Value? {
        get { self[FocusedPriceKey.self] }
        set { self[FocusedPriceKey.self] = newValue }
    }
}

标签: iosswiftmacosswiftuiwwdc

解决方案


所以我拿了演示代码并添加了一点调试,如下所示。

据我所知,有两个问题。

首先,没有设置focusedValueon ContentView,因为 View 没有被聚焦。可以看出这是因为@FocusBindinginOtherView没有price按预期获取值。

这可能是一个 SwiftUI 错误,但通过切换到focusedSceneValue修改器很容易解决。无论如何,有了它,就可以price保持同步。ContentViewOtherView

第二个问题是,即使第一个修复/解决方法到位,更改price也不会导致Custom菜单在初始渲染后重建。

我认为这是 SwiftUI 的一个错误,建议向 Apple 提交反馈,让他们知道。

作为应用程序全局状态的解决方法(在这种情况下)price将只是将其PriceCommands作为参数传递(正如已经为 所做的那样ContentView)。

亲切的问候

import SwiftUI

@main
struct TestApp: App {
    @State private var price = 0

    var body: some Scene {
        WindowGroup {
            ContentView(price: $price)
                .focusedSceneValue(\.price, $price) // <== Changed
        }
        .commands {
            PriceCommands()
        }
    }
}

struct OtherView: View {
    @FocusedBinding(\.price) var price: Int?

    var body: some View {
        Text("OtherView price = \(String(describing: price))")
    }
}

struct ContentView: View {
    @Binding var price: Int

    var body: some View {
        VStack {
            OtherView()
            IncreasePriceButton(price: $price)
        }
    }
}

struct IncreasePriceButton: View {
    @Binding var price: Int

    var body: some View {
        Button("Increase Price") {
            price += 1
            print("price = \(price)")
        }
    }
}

struct PriceCommandButton: View {
    @FocusedBinding(\.price) var price
    var priceText: String {
        String(price ?? -1)
    }

    var body: some View {
        print("Buiding commands menu with \(String(describing: price))")
        return Button("Print Price \(priceText)") {
            print("price = \(String(describing: price))")
        }
    }
}

struct PriceCommands: Commands {
    var body: some Commands {
        CommandMenu("Custom") {
            PriceCommandButton()
                .keyboardShortcut(KeyboardShortcut("C", modifiers: [.command, .shift]))
        }
    }
}

struct FocusedPriceKey: FocusedValueKey {
    typealias Value = Binding<Int>
}

extension FocusedValues {
    var price: FocusedPriceKey.Value? {
        get { self[FocusedPriceKey.self] }
        set { self[FocusedPriceKey.self] = newValue }
    }
}

推荐阅读