19

I try to create a menu with buttons containing primary and secondary labels like the photo bellow using SwiftUI, so I embed the button and the text (as secondary label) in the VStack, but it's doesn't work. How I can create this view in swiftUI?

enter image description here

The Code

 Menu {
        Section {
            search
        }
        Section {
// The sort button
        Menu  {
            Picker("Sorting options", selection: $sort) {
                ForEach(SortType.allCases, id: \.self) { type in
                    Label("title", systemImage: "chevron.up")
                        .tag(type)
                }
            }
        } label: {
// Here I should embed title and subtitle like the photo but I don't know how.
            Label("Sort By", systemImage: "arrow.up.arrow.down")
        }
        }
    } label: {
        menuLabel
    }
7
  • 1
    You should use this community to ask questions that can help you solve an issue, not for sharing your plans. If that was actually a question, please provide details of what you have tried and why you didn't succeed, after you tried here. Commented Mar 12, 2022 at 8:15
  • 1
    I'm sorry about the miss understanding, I edit the question to be clearer about what I want to ask. Commented Mar 12, 2022 at 9:11
  • Can you show your code so we can help you ? We can not supposed what you have done so far. Commented Mar 12, 2022 at 11:11
  • Present your attempt(s) and folks fill comment / make changes to help out. There’s got to be a bit of an initiative though from your side Commented Mar 12, 2022 at 14:56
  • 2
    As far as I know this is not possible in pure SwiftUI. Menu only accepts labels from one Text and one Image. Commented Mar 12, 2022 at 18:02

5 Answers 5

11

NEW ANSWER

If you are attempting to get behavior similar to the below, use the content closure of a Button

enter image description here

Menu("HI") {
    Button(action: {}) {
        Text(temperatureType.localizedDescription)
        Text(formatTemperature(sensor.temperature, to: 
    temperatureType.toUnitTemperature()))
    }
}

Here, I am mimicking the new data detectors functionality provided in iOS 16's UITextView Then you can add any of the modifiers you would like


OLD ANSWER

I found a partial way to do this, but it is without the subtitle text smaller and in a foregroundStyle(.secondary) :

enter image description here

Picker(selection: $pointsSortType) {
    Button(PointsSortType.date            .displayName) {}.tag(PointsSortType.date            .rawValue)
    Button(PointsSortType.type            .displayName) {}.tag(PointsSortType.type            .rawValue)
    Button(PointsSortType.submissionStatus.displayName) {}.tag(PointsSortType.submissionStatus.rawValue)
    Button(PointsSortType.duration        .displayName) {}.tag(PointsSortType.duration        .rawValue)
} label: {
    Label {
        Text("""
             Sort By
             Manual
             """)
    } icon: {
        Image(systemName: "arrow.up.arrow.down")
    }
}
.pickerStyle(.menu)

Here, we are using Multi-Line Strings to trick it. I tried messing around with adding multiple Texts in a bunch of Stacks. I even tried type erasing it with AnyView(erasing:), but somehow it's still smarter. I even tried interpolating Text views and adding Text modifiers like Bold, but it overrode that as well.

Sorry for the spaces in between, but that is how I like to format my code.

Sign up to request clarification or add additional context in comments.

4 Comments

I know this is not the best solution, but this can be used in the meanwhile. Another way of going about showing subtitle data is by using a Bullet Point Text("Sort By • Manual") ![i.sstatic.net/aRGNz.png](Image)
Apparently a simple break line character also works "\n".
This is a nice find. Also, it's completely unexpected that the Menu would "accept" the second piece of Text and automatically style it to description style. Adding the third Text won't work though. Keep in mind the system limits the description to two lines of text, the tail will be trimmed.
does it work on macos too? would be amazing
7

You can achieve this in SwiftUI by adding and extra Text component next to your menu label. For example:

Menu("Sort") {
  Menu {
    Picker("Sort By", selection: $sortBy) {
      ForEach(SortBy.allCases) { sortOption in
        Text(sortOption.rawValue)
          .tag(sortOption)
      }
    }
  } label: {
     Label("Sort By", systemImage: "arrow.up.arrow.down")
     Text(sortBy.rawValue)
  }
}

Comments

2

You can achieve this by using UIKit since iOS 15.0

var menu = UIMenu()

if #available(iOS 15.0, *) {
   menu = UIMenu(title: "Sort By",
                 subtitle: "Name",
                 children: provideJobSortOptions())
} else {
   menu = UIMenu(title: "Sort By"), children: provideJobSortOptions())
}

1 Comment

Correct me if I'm wrong but this is referring to a UIMenu, which is part of UIKit, not SwiftUI which the OP mentioned specifically. Can you expand your example to show using a UIMenu in a SwiftUI Menu? Thanks!
1

All of these answers didn't work for me. What did work however was the following...

Picker(selection: $viewModel.speedMultiplier) {
    ForEach(viewModel.speedOptions, id: \.self) { speed in
        Text(String(format: "%.2fx", speed))
            .tag(speed)
    }
} label: {
    Text("Speed")
    Text(String(format: "%.2fx", viewModel.speedMultiplier))
    Image(systemName: "hare.fill")
}
.pickerStyle(.menu)

Screenshot of menu with subtitle

Comments

-1

A bit of a hacky solution you can use is to extend UIMenu and override its subtitle property. You can create a menu observer class that allows mutation of the value of that property.

something like this:

extension UIMenu {
  
  open override var subtitle: String? {
    get { MenuObserver.shared.menuSubtitle }
    set { }
  }
}

class MenuObserver {
  
  static var shared = MenuObserver()
  
  var menuSubtitle: String? = nil
}

Then you can update the menu subtitle anywhere in your code by changing the property of the observer

MenuObserver.shared.menuSubtitle = "someString"

This solution won't work though if you have multiple menu's embedded inside a menu as the same subtitle will show for each

PS. This solution works for SwiftUI as SwiftUI's menu is just a UIMenu under the hood

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.