Say you have†
var ele = // an [UIAction]
So,
yourButton.menu = UIMenu(children: ele)
Every app needs to know when the menu opens, so you use the usual trick,
let trick = UIDeferredMenuElement.uncached({completion in
print("menu opened")
completion([])
})
However! If you try doing this:
ele.append(trick)
It does not work!!
[X] Cannot convert value of type 'UIDeferredMenuElement' to expected argument type 'UIAction'
However! It's fine to do this! ->
aButton.menu = UIMenu(children: [ele[0], ele[1], ele[2], trick])
this for example will also work
aButton.menu = UIMenu(children: ele + [trick])
but this does not work
ele.append(contentsOf: [trick])
Note too that ele.append(trick as! UIAction)
does not work, unrelated type
.
What is the explanation for this arcane mystery?
† for completeness, I usually make items something like this idiom ...
let noms = [
("TAG", "Do crazy things", "arrowtriangle.right.and.line.vertical.and.arrowtriangle.left"),
("BLAH", "Even more\nwild stuff.", "rectangle.grid.3x2"),
("LOUT", "Logout now", "lock.open")
]
let ele = noms.map({ fn in
return UIAction(title: fn.1, image: UIImage(systemName: fn.2)) { [weak self] _ in
guard let self else { return }
aButtonProcess(fn.0)
}
})
The explanation is that children
is explicitly typed as an array of a superclass, UIMenuElement, from which both UIMenu and UIAction inherit.
https://developer.apple.com/documentation/uikit/uimenuelement
If you type ele
explicitly as an array of UIMenuElement you can do the same.
var ele : [UIMenuElement] = [myAction]
// now append your deferred menu element
Whereas, if you form the array with both from the start, Swift infers the superclass as the array element type.
In other words, if Cat is an Animal and Dog is an Animal, you cannot append a dog to an array of Cat, but an array with a dog and a cat is an array of Animal and you can append another animal to it.