In SwiftUI, there is a thing called a Menu, and in it you can have Buttons, Dividers, other Menus, etc. Here's an example of one I'm building below:
import SwiftUI
func testing() {
print("Hello")
}
struct ContentView: View {
var body: some View {
VStack {
Menu {
Button(action: testing) {
Label("Button 1", systemImage: "pencil.tip.crop.circle.badge.plus")
}
Button(action: testing) {
Label("Button 2", systemImage: "doc")
}
}
label: {
Label("", systemImage: "ellipsis.circle")
}
}
}
}
So, in the SwiftUI Playgrounds app, they have this menu:
My question is:
How did they make the circled menu option? I’ve found a few other cases of this horizontal set of buttons in a Menu, like this one below:
HStacks and other obvious attempts have all failed. I’ve looked at adding a MenuStyle, but the Apple’s docs on that are very lacking, only showing an example of adding a red border to the menu button. Not sure that’s the right path anyway.
I’ve only been able to get Dividers() and Buttons() to show up in the Menu:
I’ve also only been able to find code examples that show those two, despite seeing examples of other options in Apps out there.
CodePudding user response:
It looks as this is only available in UIKit
at present (and only iOS 16 ), by setting
menu.preferredElementSize = .medium
To add this to your app you can add a UIMenu
to UIButton
and then use UIHostingController
to add it to your SwiftUI app.
Here's an example implementation:
Subclass a UIButton
class MenuButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
let inspectAction = self.inspectAction()
let duplicateAction = self.duplicateAction()
let deleteAction = self.deleteAction()
setImage(UIImage(systemName: "ellipsis.circle"), for: .normal)
menu = UIMenu(title: "", children: [inspectAction, duplicateAction, deleteAction])
menu?.preferredElementSize = .medium
showsMenuAsPrimaryAction = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func inspectAction() -> UIAction {
UIAction(title: "Inspect",
image: UIImage(systemName: "arrow.up.square")) { action in
//
}
}
func duplicateAction() -> UIAction {
UIAction(title: "Duplicate",
image: UIImage(systemName: "plus.square.on.square")) { action in
//
}
}
func deleteAction() -> UIAction {
UIAction(title: "Delete",
image: UIImage(systemName: "trash"),
attributes: .destructive) { action in
//
}
}
}
Create a Menu
using UIViewRepresentable
struct Menu: UIViewRepresentable {
func makeUIView(context: Context) -> MenuButton {
MenuButton(frame: .zero)
}
func updateUIView(_ uiView: MenuButton, context: Context) {
}
}
Works like a charm!
struct ContentView: View {
var body: some View {
Menu()
}
}