У меня есть представление списка, и каждая строка списка содержит HStack с некоторыми текстовыми представлениями и изображением, например:
HStack{
Text(group.name)
Spacer()
if (groupModel.required) { Text("Required").color(Color.gray) }
Image("ic_collapse").renderingMode(.template).rotationEffect(Angle(degrees: 90)).foregroundColor(Color.gray)
}.tapAction { self.groupSelected(self.group) }
Кажется, это отлично работает, за исключением случаев, когда я нажимаю на пустой раздел между своим текстом и изображением (где находится Spacer()), действие касания не регистрируется. Действие касания будет происходить только тогда, когда я касаюсь текста или изображения.
Кто-нибудь еще сталкивался с этой проблемой/знает обходной путь?
@dfd Каждая строка представляет собой просто текст с шевроном в конце, что-то вроде Object One > , так будет выглядеть строка, и я бы хотел, чтобы пользователь мог нажать в любом месте строки (это не форматировалось с пробелы, которые, как я думал, будут - просто представьте себе пробел между текстом и >)
@dfd Я думаю, что это довольно стандартное поведение, когда пользователь может щелкнуть в любом месте ячейки таблицы, поэтому у них есть метод didSelectRowAt в их табличных представлениях UIKit.
Конечно, я согласен. Но, возможно, вместо Spacer попробуйте что-нибудь другое. Может быть, превратить все это в Button? В SwiftUI Spacer — это просто интервал.
Не могу поверить, что скажу это... но да, старина, но хороша! Когда я предлагал кнопку, я имел в виду следующее: alejandromp.com/blog/2019/06/09/игра с кнопками Swiftui





Я подал Обратная связь по этому поводу и предлагаю вам сделать то же самое.
Тем временем непрозрачный Color должен работать так же хорошо, как Spacer. К сожалению, вам придется совпадать с цветом фона, и это предполагает, что вам нечего отображать за кнопкой.
Почему бы просто не использовать Button?
Button(action: { self.groupSelected(self.group) }) {
HStack {
Text(group.name)
Spacer()
if (groupModel.required) { Text("Required").color(Color.gray) }
Image("ic_collapse").renderingMode(.template).rotationEffect(Angle(degrees: 90)).foregroundColor(Color.gray)
}
}.foregroundColor(.primary)
Если вы не хотите, чтобы кнопка применяла цвет акцента к Text(group.name), вы должны установить foregroundColor, как я сделал в моем примере.
Это изменяет цвет фона при нажатии, хотя вы, вероятно, можете изменить это: stackoverflow.com/questions/56509640/… Решение ZStack от @jim-marquardt хорошо сработало для меня.
Не то же самое поведение с navigationLink
Я смог обойти это, поместив Spacer в ZStack и добавив сплошной цвет с очень низкой непрозрачностью:
ZStack {
Color.black.opacity(0.001)
Spacer()
}
Как я недавно узнал, есть еще:
HStack {
...
}
.contentShape(Rectangle())
.onTapGesture { ... }
Хорошо работает для меня.
Это отлично сработало применительно к содержимому моего Button. В этом случае нет необходимости в onTapGesture.
Работает с HStack с разделителями внутри него.
Это хорошо работает. Принятый ответ не сработал для меня, потому что он меняет цвет акцента содержимого HStack.
Это также лучшее решение для моего случая, чем принятый ответ, поскольку у меня есть строка с различными элементами управления, а не кнопка. Но все это должно быть доступным. Кроме того, как уже упоминал Cloud9999Strife, внешний вид не меняется.
Ты спас мне жизнь, бро!
Еще несколько пояснений от Hacking With Swift: hackingwithswift.com/quick-start/swiftui/….
Тот факт, что это решение требуется, разочаровывает меня в SwiftUI в целом. В любом случае, отличное решение!
Изучил трудным путем порядок имеет значение для модификаторов, убедитесь, что contentShape стоит перед onTapGesture.
Простое расширение на основе ответа Джима
extension Spacer {
/// https://stackoverflow.com/a/57416760/3393964
public func onTapGesture(count: Int = 1, perform action: @escaping () -> Void) -> some View {
ZStack {
Color.black.opacity(0.001).onTapGesture(count: count, perform: action)
self
}
}
}
Теперь это работает
Spacer().onTapGesture {
// do something
}
Можете ли вы описать более подробно, что это делает? Это создает новый ZStack каждый раз, когда пользователь нажимает? Очищает ли он память от этого? Кстати, это сработало отлично :).
@JosephAstrahan нет, он просто создает непрозрачное представление за вашим текущим представлением, которое регистрирует нажатия пользователя.
Уничтожает ли он (освобождает память) представление после завершения крана?
Итак, он создает 1-кратный просмотр? Не создавать это представление каждый раз, когда пользователь нажимает снова и снова, я думаю, это был мой главный вопрос. Причина, по которой я спросил, заключалась в том, что она находится внутри функции on TapGesture, что подразумевает создание представления снова и снова.
Это чистый ответ, особенно для отслеживания фантомных нажатий.
Я просто добавляю цвет фона (кроме clear цвета) для HStack работ.
HStack {
Text("1")
Spacer()
Text("1")
}.background(Color.white)
.onTapGesture(count: 1, perform: {
})
работает как по волшебству при каждом просмотре:
extension View {
func onTapGestureForced(count: Int = 1, perform action: @escaping () -> Void) -> some View {
self
.contentShape(Rectangle())
.onTapGesture(count:count, perform:action)
}
}
Это круто
Хотя принятый ответ позволяет имитировать функциональность кнопки, визуально он не удовлетворяет. Не заменяйте Button на .onTapGesture или UITapGestureRecognizer, если только вам не нужна область, которая принимает события касания пальцем. Такие решения считаются хакерскими и не являются хорошей практикой программирования.
Для решения вашей проблемы вам необходимо реализовать BorderlessButtonStyle ⚠️
Создайте общую ячейку, например. SettingsNavigationCell.
struct SettingsNavigationCell: View {
var title: String
var imageName: String
let callback: (() -> Void)?
var body: some View {
Button(action: {
callback?()
}, label: {
HStack {
Image(systemName: imageName)
.font(.headline)
.frame(width: 20)
Text(title)
.font(.body)
.padding(.leading, 10)
.foregroundColor(.black)
Spacer()
Image(systemName: "chevron.right")
.font(.headline)
.foregroundColor(.gray)
}
})
.buttonStyle(BorderlessButtonStyle()) // <<< This is what you need ⚠️
}
}
struct SettingsView: View {
var body: some View {
NavigationView {
List {
Section(header: "Appearance".text) {
SettingsNavigationCell(title: "Themes", imageName: "sparkles") {
openThemesSettings()
}
SettingsNavigationCell(title: "Lorem Ipsum", imageName: "star.fill") {
// Your function
}
}
}
}
}
}
Вроде в духе всего сказанного:
struct NoButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.background(Color.black.opacity(0.0001))
}
}
extension View {
func wrapInButton(action: @escaping () -> Void) -> some View {
Button(action: action, label: {
self
})
.buttonStyle(NoButtonStyle())
}
}
Я создал NoButtonStyle, потому что BorderlessButtonStyle все еще давал анимацию, отличную от .onTapGesture.
Пример:
HStack {
Text(title)
Spacer()
Text("Select Value")
Image(systemName: "arrowtriangle.down.square.fill")
}
.wrapInButton {
isShowingSelectionSheet = true
}
Другой вариант:
extension Spacer {
func tappable() -> some View {
Color.blue.opacity(0.0001)
}
}
Я заметил, что Color не всегда действует так же, как Spacer при помещении в стек, поэтому я бы посоветовал не использовать это расширение Spacer, если вы не знаете об этих различиях. (Распорка выталкивает стек в одном направлении (если в VStack, то выталкивает вертикально, если в HStack, то выталкивает горизонтально, тогда как вид Color выталкивает во всех направлениях.)
На мой взгляд, лучший подход из соображений доступности — обернуть HStack внутри метки Button, а чтобы решить проблему, когда Spacer нельзя нажать, вы можете добавить .contentShape(Rectangle()) в HStack.
Итак, на основе вашего кода будет:
Button {
self.groupSelected(self.group)
} label: {
HStack {
Text(group.name)
Spacer()
if (groupModel.required) { Text("Required").color(Color.gray) }
Image("ic_collapse").renderingMode(.template).rotationEffect(Angle(degrees: 90)).foregroundColor(Color.gray)
}
.contentShape(Rectangle())
}
Честный вопрос: Ровно Зачем вы ожидаете, что кто-то нажмет
spacer? Это по определению космос. Может быть, ваш пользовательский интерфейс ожидает чего-то, что вы могли бы вUIKit? Если да, пожалуйста, уточните это.