I've built a view that has scroll view of horizontal type with HStack for macOS app. Is there a way to circle those items using keyboard arrows?
(I see that ListView has a default behavior but for other custom view types there are none)
click here to see the screenshot
var body: some View {
VStack {
ScrollView(.horizontal, {
HStack {
ForEach(items.indices, id: \.self) { index in
//custom view for default state and highlighted state
}
}
}
}
}
any help is appreciated :)
CodePudding user response:
Approach I used
- Uses keyboard shortcuts on a button
Alternate approach
- To use commands (How to detect keyboard events in SwiftUI on macOS?)
Code:
Model
struct Item: Identifiable {
var id: Int
var name: String
}
class Model: ObservableObject {
@Published var items = (0..<100).map { Item(id: $0, name: "Item \($0)")}
}
Content
struct ContentView: View {
@StateObject private var model = Model()
@State private var selectedItemID: Int?
var body: some View {
VStack {
Button("move right") {
moveRight()
}
.keyboardShortcut(KeyEquivalent.rightArrow, modifiers: [])
ScrollView(.horizontal) {
LazyHGrid(rows: [GridItem(.fixed(180))]) {
ForEach(model.items) { item in
ItemCell(
item: item,
isSelected: item.id == selectedItemID
)
.onTapGesture {
selectedItemID = item.id
}
}
}
}
}
}
private func moveRight() {
if let selectedItemID {
if selectedItemID 1 >= model.items.count {
self.selectedItemID = model.items.last?.id
} else {
self.selectedItemID = selectedItemID 1
}
} else {
selectedItemID = model.items.first?.id
}
}
}
Cell
struct ItemCell: View {
let item: Item
let isSelected: Bool
var body: some View {
ZStack {
Rectangle()
.foregroundColor(isSelected ? .yellow : .blue)
Text(item.name)
}
}
}
CodePudding user response:
You could try this using my previous post, but with a horizontal scrollview instead of a list. You will have to adjust the code to your particular app. My approach consists only of a few lines of code that monitors the key events.
import Foundation
import SwiftUI
import AppKit
struct ContentView: View {
let fruits = ["apples", "pears", "bananas", "apricot", "oranges"]
@State var selection: Int = 0
var body: some View {
ScrollView(.horizontal) {
HStack(alignment: .center, spacing: 0) {
ForEach(fruits.indices, id: \.self) { index in
VStack {
Image(systemName: "globe")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(10)
Text(fruits[index]).tag(index)
}
.background(selection == index ? Color.red : Color.clear)
.padding(10)
}
}
}
.onAppear {
NSEvent.addLocalMonitorForEvents(matching: [.keyDown]) { nsevent in
if nsevent.keyCode == 124 { // arrow right
selection = selection < fruits.count ? selection 1 : 0
} else {
if nsevent.keyCode == 123 { // arrow left
selection = selection > 1 ? selection - 1 : 0
}
}
return nsevent
}
}
}
}