I want to add the iOS16 API for 'contextMenu forSelectionType: menu:', but I also need to support iOS15, so I need a conditional ViewModifier
for this. I'm struggling with the correct syntax for it.
From what I can work out, this is what my ViewModifier
should look like:
struct CompatibilityListContextMenuModifier<V>: ViewModifier where V: View {
let menu: (Set<Person.ID>) -> V
func body(content: Content) -> some View {
if #available(iOS 16.0, macOS 13.0, *) {
content
.contextMenu(forSelectionType: Person.ID.self, menu: menu)
} else {
content
}
}
}
But how do I use this in my List:
List (selection: $contactsListOptions.multiSelectedContacts){
// ...
}
.modifier(CompatibilityListContextMenuModifier(menu: items in {
if items.isEmpty { // Empty area menu.
Button("New Item") { }
} else if items.count == 1 { // Single item menu.
Button("Copy") { }
Button("Delete", role: .destructive) { }
}
else {
Button("MultipleSelection Items") { }
}
}))
This gives me a syntax error:
Cannot find 'items' in scope
If I try to pass in the selection binding:
.modifier(CompatibilityListContextMenuModifier(menu: contactsListOptions.multiSelectedContacts in {
if contactsListOptions.multiSelectedContacts.isEmpty { // Empty area menu.
Button("New Item") { }
} else if contactsListOptions.multiSelectedContacts.count == 1 { // Single item menu.
Button("Copy") { }
Button("Delete", role: .destructive) { }
}
else {
Text("MultipleSelection Items")
}
}))
I get the error:
Cannot convert value of type 'Set<Person.ID>' (aka 'Set') to expected argument type '(Set<Person.ID>) -> V' (aka '(Set) -> V')
and
Generic parameter 'V' could not be inferred
What's the right way to conditionally compile this API?
The syntax for creating the modifier should be:
CompatibilityListContextMenuModifier { items in ... }
The closure parameter should go inside the { ... }
.
The closure should also be a @ViewBuilder
, otherwise you cannot use if
statements like that in the closure.
// I also abstracted away Person.ID as the Selection type parameter
struct CompatibilityListContextMenuModifier<Menu, Selection>: ViewModifier
where Menu: View, Selection: Hashable {
@ViewBuilder
let menu: (Set<Selection>) -> Menu
func body(content: Content) -> some View {
if #available(iOS 16.0, macOS 13.0, *) {
content
.contextMenu(forSelectionType: Selection.self, menu: menu)
} else {
content
}
}
}
Now you can use it like this:
.modifier(CompatibilityListContextMenuModifier<_, Person.ID> { items in
if items.isEmpty {
Button("New Item") { }
} else if items.count == 1 {
Button("Copy") { }
Button("Delete", role: .destructive) { }
}
else {
Button("MultipleSelection Items") { }
}
})
I'd also suggest that you write your own View
extension to write this more conveniently:
extension View {
func compatibilityContextMenu<Menu: View, Selection: Hashable>(
forSelectionType type: Selection.Type,
@ViewBuilder menu: @escaping (Set<Selection>) -> Menu
) -> some View {
modifier(CompatibilityListContextMenuModifier(menu: menu))
}
}
.compatibilityContextMenu(forSelectionType: Person.ID.self) { items in
// ...
}