Home > Back-end >  How to scroll through items in scroll view using keyboard arrows in SwiftUI?
How to scroll through items in scroll view using keyboard arrows in SwiftUI?

Time:11-12

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

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
            }
        }
    }
}
  • Related