MacOS 上列表中的 SwiftUI 键盘导航

Avd*_*ept 2 macos swift swiftui swiftui-list

我正在尝试实现一个可以使用箭头键(向上/向下)导航的列表。我已经创建了布局,但现在我不完全理解如何(以及在​​哪里)拦截向上/向下键,以便我可以添加自定义逻辑。我已经尝试过onMoveCommandfocusable但没有用(根本没有触发)

我的代码 - 下面

public var body: some View {
        VStack(spacing: 0.0) {
            VStack {
                HStack(alignment: .center, spacing: 0) {
                    Image(systemName: "command")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 20, height: 20)
                        .padding(.leading, 20)
                        .offset(x: 0, y: 1)
                    TextField("Search Commands", text: $state.commandQuery)
                        .font(.system(size: 20, weight: .light, design: .default))
                        .textFieldStyle(.plain)
                        .onReceive(
                            state.$commandQuery
                                .debounce(for: .seconds(0.1), scheduler: DispatchQueue.main)
                        ) { val in
                            state.fetchMatchingCommands(val: val)
                        }
                    .padding(16)
                    .foregroundColor(Color(.systemGray).opacity(0.85))
                    .background(EffectView(.sidebar, blendingMode: .behindWindow))
                }
            }
            Divider()
            VStack(spacing: 0) {
                List(state.filteredCommands.isEmpty && state.commandQuery.isEmpty ?
                     commandManager.commands : state.filteredCommands, selection: $selectedItem) { command in
                        VStack(alignment: .leading, spacing: 0) {
                            Text(command.title).foregroundColor(Color.white)
                                .padding(EdgeInsets.init(top: 0, leading: 10, bottom: 0, trailing: 0))
                                .frame(height: 10)
                        }.frame(maxWidth: .infinity, maxHeight: 15, alignment: .leading)
                            .listRowBackground(self.selectedItem == command ?
                                               RoundedRectangle(cornerRadius: 5, style: .continuous)
                                .fill(Color(.systemBlue)) :
                                                RoundedRectangle(cornerRadius: 5, style: .continuous)
                                .fill(Color.clear) )

                        .onTapGesture {
                            self.selectedItem = command
                            callHandler(command: command)
                        }.onHover(perform: { _ in self.selectedItem = command })
                    }.listStyle(SidebarListStyle())
            }
        }
        .background(EffectView(.sidebar, blendingMode: .behindWindow))
        .foregroundColor(.gray)
        .edgesIgnoringSafeArea(.vertical)
        .frame(minWidth: 600,
           minHeight: self.state.isShowingCommandsList ? 400 : 28,
           maxHeight: self.state.isShowingCommandsList ? .infinity : 28)
    }
Run Code Online (Sandbox Code Playgroud)

这就是它的样子 - 我想让焦点在找到的列表项之间移动

在此输入图像描述

wor*_*dog 7

如果我正确理解您的问题,您需要使用箭头键从search TextField, 移动到项目列表,然后使用向上/向下箭头键导航列表。

尝试像此示例代码这样简单的操作,以监视向上/向下箭头键,并采取适当的操作。

调整/调整逻辑以满足您的需求。

import Foundation
import SwiftUI
import AppKit


struct ContentView: View {
    let fruits = ["apples", "pears", "bananas", "apricot", "oranges"]
    @State var selection: Int?
    @State var search = ""
    
    var body: some View {
        VStack {
            VStack {
                HStack(alignment: .center, spacing: 0) {
                    Image(systemName: "command")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 20, height: 20)
                        .padding(.leading, 20)
                        .offset(x: 0, y: 1)
                    TextField("Search", text: $search)
                        .font(.system(size: 20, weight: .light, design: .default))
                        .textFieldStyle(.plain)
                }
            }
            Divider()
            List(selection: $selection) {
                ForEach(fruits.indices, id: \.self) { index in
                    Text(fruits[index]).tag(index)
                }
            }
            .listStyle(.bordered(alternatesRowBackgrounds: true))
        }
        .onAppear {
            NSEvent.addLocalMonitorForEvents(matching: [.keyDown]) { nsevent in
                if selection != nil {
                    if nsevent.keyCode == 125 { // arrow down
                        selection = selection! < fruits.count ? selection! + 1 : 0
                    } else {
                        if nsevent.keyCode == 126 { // arrow up
                            selection = selection! > 1 ? selection! - 1 : 0
                        }
                    }
                } else {
                    selection = 0
                }
                return nsevent
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)