#swiftui #swiftui-list
#swiftui #swiftui-список
Вопрос:
У меня есть SwiftUI List
на iOS14.3 (Xcode12.3), и внутри его ячеек я хочу, чтобы Menu
всплывающее окно появлялось, когда пользователь нажимает на кнопку. Кроме того, в этих ячейках есть onTapGesture
и проблема в том, что этот жест также запускается при нажатии кнопки меню.
Пример:
List {
HStack {
Text("Cell content")
Spacer()
// Triggers also onTapGesture of cell
Menu(content: {
Button(action: { }) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: { }) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: { }) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
}
.background(Color(.systemBackground))
.onTapGesture {
print("Cell tapped")
}
}
Если вы нажмете на изображение с многоточием, откроется меню, которое также "Cell tapped"
будет выведено на консоль. Это проблема для меня, потому что в моем примере с реальным миром я сворачиваю ячейку жестом касания и, конечно, я не хочу, чтобы это происходило, когда пользователь нажимает кнопку меню.
Я обнаружил, что при длительном нажатии на кнопку жест не срабатывает. Но, на мой взгляд, это неприемлемый UX, мне потребовалось некоторое время, чтобы понять это самому.
Я также заметил, что когда я заменяю Menu
на обычный Button
, то жест ячейки не срабатывает при нажатии (только с BorderlessButtonStyle
помощью или PlainButtonStyle
, в противном случае он вообще не активен). Но я не знаю, как открыть меню из его действия.
List {
HStack {
Text("Cell content")
Spacer()
// Does not trigger cells onTapGesture, but how to open Menu from action?
Button(action: { print("Button tapped") }) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
.buttonStyle(BorderlessButtonStyle())
}
.background(Color(.systemBackground))
.onTapGesture {
print("Cell tapped")
}
}
Комментарии:
1. Это проект iOS или macOS?
2. О, извините, это на iOS14.3 с Xcode 12.3
3. Я тестировал его также на macOS сейчас, и там жест cells не запускается при открытии меню. Несмотря на то, что меню на Mac выглядит совсем по-другому (на самом деле это выпадающий список), я бы ожидал одинакового поведения на обеих платформах. Итак, я только что сообщил об ошибке Apple.
Ответ №1:
Кроме того, вы можете переопределить меню onTapGesture
:
struct ContentView: View {
var body: some View {
List {
HStack {
Text("Cell content")
Spacer()
Menu(content: {
Button(action: {}) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: {}) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: {}) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
.onTapGesture {} // override here
}
.contentShape(Rectangle())
.onTapGesture {
print("Cell tapped")
}
}
}
}
Кроме того, нет необходимости использовать .background(Color(.systemBackground))
для нажатия на разделители. Это не очень гибко — что, если вы хотите изменить цвет фона?
Вы можете использовать contentShape
вместо:
.contentShape(Rectangle())
Комментарии:
1. Спасибо, хотя я думаю, что ответ @Asperi немного более элегантный и, вероятно, более перспективный, я отмечу это как принятый ответ. На самом деле, в моем реальном примере ячейка значительно сложнее, а кнопка меню не является последним подразделом с правой стороны. Однако основная проблема заключается в том, что я повторно использую эту ячейку в разных местах своего приложения и хочу иметь разные onTapGestures в зависимости от того, где она используется, поэтому я применю этот жест вне тела ячеек. В этих обстоятельствах ваше решение намного лучше.
2. Кроме этого, спасибо за подсказку с contentShape вместо background, чтобы сделать разделители доступными. Не знал этого, я все еще новичок в SwftUI, работая над своим первым большим приложением, использующим его.
3. Похоже, этот ответ больше не работает на iOS 15.5 (по крайней мере). Хуже того: результат случайный (иногда вы получаете двойные нажатия, а иногда нет). Кто-нибудь еще это видит? Альтернатива ?!
Ответ №2:
Я не уверен, является ли это ошибкой или ожидаемым поведением, но вместо того, чтобы бороться с этим, я бы рекомендовал избегать такого перекрытия (потому что даже при сегодняшнем успехе он может перестать работать в разных системах / обновлениях).
Вот возможное решение (протестировано с Xcode 12.1 / iOS 14.1)
List {
HStack {
// row content area
HStack {
Text("Cell content")
Spacer()
}
.background(Color(.systemBackground)) // for tap on spacer area
.onTapGesture {
print("Cell tapped")
}
// menu area
Menu(content: {
Button(action: { }) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: { }) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: { }) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
}
}
Комментарии:
1. Спасибо, это, конечно, также решает проблему и, возможно, является более элегантным и перспективным решением, но я принял ответ @pawello2222. Причину смотрите в моем комментарии к его ответу.