iosarraysswiftuibuttonuiaction

Why can't a UIDeferredMenuElement be added to an array of UIAction, whereas you can form an array with one?


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)
    }
})

Solution

  • 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.