I expect that UIKit might let you control the position of the menu more precisely, but, unfortunately, you don't have much control with SwiftUI. I am guessing, the Reminders app is not implemented using SwiftUI.
For the case shown in the first block of code, you can increase the tappable area by applying a frame to the menu label and setting a content shape. This is similar to what you were doing in the potential solution you showed in the question. It can be done without an HStack:
Menu {
// ... buttons, as before
} label: {
Text("Add Question")
.foregroundStyle(Color.white)
.background(Color.black)
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
}
However, when you tap the row, the menu always aligns to the middle of the row, even when you tap on the text (as you also explained in the question):

Here are some possible workarounds, none of which are perfect, but they might help:
1. Experiment with minWidth
In my tests on an iPhone 16 simulator, the point at which it switches from left-alignment to center-alignment is when the width of the label is greater than about 273 points. This is about two-thirds of the screen width on an iPhone 16, but it might be different on other devices. So if the frame on the label is changed from maxWidth: .infinity to, say, minWidth: 270, this gives a larger hit area with the menu still left-aligned. However, the hit area will not include the trailing area on the right of the row.
Text("Add Question")
.foregroundStyle(Color.white)
.background(Color.black)
.frame(minWidth: 270, alignment: .leading) // 👈 changed
.contentShape(Rectangle())
2. Offset the label with padding to compensate
As we have seen, when the label has maximum width, the menu is centered. It turns out that if the center of the row is shifted, the menu moves too.
The center of the row can be shifted by applying a positive x-offset to the label, then compensating with negative leading padding on the Menu.
- By using
.offset instead of leading .padding for the label, the width available for the text does not change. So a long label will only wrap if the full row width is too narrow.
- By using negative padding for the
Menu, the hit area does not move. If instead a negative offset is used, the right-side of the row is no longer receptive to taps.
private let labelOffset: CGFloat = 20
Menu {
// ... buttons, as before
} label: {
Text("Add Question")
.foregroundStyle(Color.white)
.background(Color.black)
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
.offset(x: labelOffset) // 👈 added
}
.padding(.leading, -labelOffset) // 👈 added
When the size of the offset is small, the menu moves by half this size. For example, if the offset is 20 (as above), the menu is shown 10 points to the left of center.
There seems to be a threshold, above which the menu alignment switches from the center of the row to the left side of the row. In my tests on an iPhone 16 simulator, the threshold was around 40. This value happens to be the distance from the edge of the screen to the row content. When the list row insets are changed, the threshold also changes. So the threshold probably depends on the insets.
Increasing the offset above this threshold makes no difference. So to move the menu to the left, the offset doesn't need to be an exact amount, it just needs to be more than the threshold.
Here is how it looks using an offset of 50. It also looks exactly the same if you use an offset of 100:

To fine tune the positioning, it would be nice if the menu could be moved to the right by about 12 points, so that it aligns with the row. Unfortunately, I couldn't find a way to do this😢
3. Add trailing padding to the menu
As kind of a combination of the two workarounds above, the menu can also be moved to the left side by adding trailing padding to the menu.
The amount of padding that is needed corresponds again to the threshold offset discussed above for workaround 2. So with the default list insets, the padding for an iPhone 16 needs to be at least 40. On an iPad, it needs to be larger (100 works).
The hit area on the right-side of the row is reduced by the padding amount, so it is best to keep the padding to the minimum necessary.
Negative padding can be added to the label to prevent the text from wrapping, if needed. However, this doesn't help to increase the hit area.
This variant gives better menu alignment. So even though the hit area excludes the width of the padding on the right side of the row, it is a better solution than workaround 1:
Menu {
// ... buttons, as before
} label: {
Text("Add Question")
.foregroundStyle(Color.white)
.background(Color.black)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.trailing, -40) // 👈 added
.contentShape(Rectangle())
}
.padding(.trailing, 40) // 👈 added
