Joby
Joby

Reputation: 91

Is there any option to set order for menu items in SwiftUI?

How to set sort order for the below button items (ignore and pin) ? Its not showing in the exact sort order in the menu list. I think os itself is updating the sort order. is there any option to restrict the same?

struct ContentView: View {

var body: some View {
    Menu {
            Button(action: {
    self.self.placeOrder()
}) {
    HStack {
        Text("Ignore")
            .multilineTextAlignment(.leading)
        Image("Menu_Ignore")
            .renderingMode(.original)
    }
}

Button(action: {
    self.adjustOrder()
}) {
    HStack {
        Text("Pin")
            .multilineTextAlignment(.leading)
        Image("Menu_Pin")
            .renderingMode(.original)
    }
}
    } label: {
        Label("Options", systemImage: "paperplane")
    }
}

func placeOrder() { }
func adjustOrder() { }}

Upvotes: 8

Views: 3327

Answers (3)

ramzesenok
ramzesenok

Reputation: 6911

Update

As mentioned by Asperi, it's possible in iOS 16 & macOS 13 https://developer.apple.com/documentation/swiftui/text/menuorder(_:)

Original answer for iOS 15 & macOS 12 and lower

Your code is totally fine and it works perfectly. The problem you have probably relates to the fact that the first menu item will always appear closest to the View that the user tapped on to trigger the Menu. ("Menu label" in your example).

So if the tapped object is low on the screen, then the system might show the menu above instead of below the tap. When it does this, it reverses the order.

This might be not clear with 2 Menu items (because if you reverse them it might look as a random change of the order) but if you add more you'll see that the order stays the same but the whole Menu might be shown either below the tap with the normal order or above the tap with the reversed order.

Upvotes: 3

Asperi
Asperi

Reputation: 257711

New in Xcode 14 / iOS 16 / macOS 13 / ...

Now we have environment value to manage this:

var body: some View {
    Menu {
       // .. buttons here
    }
    .environment(\.menuOrder, .fixed)     // << here !!
}

Upvotes: 20

tsuruken
tsuruken

Reputation: 31

It feels like a force, but I solved it by getting the global y position of Menu with GeometryReader and setting the order at the top or bottom of the screen.

The position where you tapped the Menu (top or bottom) determines the order of the elements in the Menu, so I used that characteristic.

import SwiftUI

struct ContentView: View {
    var body: some View {
        ScrollView {
            GeometryReader { geometry in
                Menu("show menu") {
                    // get global position
                    let frame = geometry.frame(in: .global)
                    // top or bottom
                    let isTop = frame.midY <= UIScreen.main.bounds.height/2
                    if isTop {
                        Button("first button", action: {})
                        Button("second button", action: {})
                    }
                    else {
                        Button("second button", action: {})
                        Button("first button", action: {})
                    }
                }
            }
            .padding(.vertical, 800)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Upvotes: 3

Related Questions