Open blakekrone opened 4 months ago
Hi @blakekrone , thanks for the questions and the feedback! We'll work on extending the examples based on this.
In the meantime, here is some guidance:
I'm wondering if we can get a best practices for multi-key setups similar to what is in the emoji example SD Connect app.
The emoji example in the SD Connect app works by setting random emojis from a static list of emojis to the keys, and storing the key-to-emoji information locally, and we're not handling any keys individually here.
When using Stateful keys with StreamDeckKeyAreaLayout is it best practice to put the individual keys inside the KeyView or have multiple different KeyViews defined?
StreamDeckKeyAreaLayout { _ in // To react to state changes within each StreamDeckKeyView, extract the view, just as you normally would in SwiftUI // Example: Key1() Key2() Key3() }
Seems to create weird layouts given the StreamDeckKeyAreaLayout is actually a For Loop.
This creates weird layouts because each SwiftUI.View
passed to the keyView: KeyViewProvider
of StreamDeckKeyAreaLayout
is treated as its own Stream Deck Key, as you correctly said.
What you should do instead is use the index: Int
closure parameter of StreamDeckKeyAreaLayout
and provide one view per index. Same goes for the index: Int
closure parameter of StreamDeckDialAreaLayout
for the Stream Deck XL dial windows. See example on the bottom.
Ideally I would just want to detect what device it is, then provide a keyIndex value for where a button resides.
There are several levels where you can detect and react on the device type, see below!
To provide different layouts per device, you can either react to the device
closure parameter of the newDeviceHandler
of StreamDeckSession.setUp
, i.e. like so:
StreamDeckSession.setUp(
newDeviceHandler: { device in
switch device.info.product {
case .mini: device.render(StreamDeckMiniLayout())
case .regular: device.render(StreamDeckRegularLayout())
case .xl: device.render(StreamDeckXLLayout())
// case .neo: [...] and so on for the other devices
}
}
)
Or provide just one layout and determine the device type within the layout definition.
For example:
@StreamDeckView
struct StreamDeckUI {
var streamDeckBody: some View {
StreamDeckLayout {
switch streamDeck.info.product { // `streamDeck` is provided by the `@StreamDeckView` macro
case .mini: // 6 keys
StreamDeckKeyAreaLayout { index in
switch index {
case 0: Key1() // one view per index!
case 1: Key2()
case 2: Key3()
case 3: Key4()
case 4: Key5()
case 5: Key6()
default: fatalError("This case never happens")
}
}
case .regular: // 15 keys
StreamDeckKeyAreaLayout { index in
switch index {
case 0: Key1()
case 1: Key2()
case 2: Key3()
// !! Shortened - add cases as desired for all indices from 0 to 14
case 12: Key13()
case 13: Key14()
case 14: Key15()
default: fatalError("This case never happens") // or handle empty keys with i.e. `Color.black`
}
}
// case .xl: [...], and so on
default: Color.black
}
} windowArea: {
switch streamDeck.info.product {
case .xl:
StreamDeckDialAreaLayout { index in
switch index {
case 0: Dial1()
case 1: Dial2()
case 2: Dial3()
case 3: Dial4()
default: fatalError("This case never happens") // or handle empty dial window views with i.e. `Color.black`
}
}
case .neo:
MyNeoPanelView()
default: Color.black
}
}
}
}
Thanks @zahnooo for the additional information. That's the route I was working with the switch() inside the Layout, it just didn't seem efficient to always be doing a switch statement each iteration. But I suppose, with the max keys (currently) being 32 that is trivial for a switch statement.
I guess there are several ways how to save some code here, but that depends on what you're trying to achieve.
For example, depending on how flexible / adaptive you want your layout to be depending on the Stream Deck device, you could also consider storing your keys in an array and return them by index - this way you could add your most important actions in the beginning and less important ones in the end, and the less important ones would be ignored if you run out of keys.
Something like this:
@StreamDeckView
struct StreamDeckUI {
// Static list of your key definitions
private static let keys = [MostImportantKey(), SecondMostImportantKey()]
var streamDeckBody: some View {
StreamDeckLayout {
StreamDeckKeyAreaLayout { index in
if index >= StreamDeckUI.keys.count {
Color.black // ignore out-of-bounds keys
} else {
StreamDeckUI.keys[index] // return key by index in array
}
}
}
}
}
Hello, working through the code and I'm wondering if we can get a best practices for multi-key setups similar to what is in the emoji example SD Connect app.
When using Stateful keys with StreamDeckKeyAreaLayout is it best practice to put the individual keys inside the KeyView or have multiple different KeyViews defined?
StreamDeckKeyAreaLayout { _ in // To react to state changes within each StreamDeckKeyView, extract the view, just as you normally would in SwiftUI // Example: Key1() Key2() Key3() }
Seems to create weird layouts given the StreamDeckKeyAreaLayout is actually a For Loop.
Ideally I would just want to detect what device it is, then provide a keyIndex value for where a button resides.