SwiftUI не могу подключиться к Spacer of HStack
У меня есть представление списка, и каждая строка списка содержит 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()
есть) действие крана не зарегистрировано. Действие касания произойдет только тогда, когда я коснусь текста или изображения.
Кто-нибудь еще сталкивался с этой проблемой / знает обходной путь?
13 ответов
Почему бы просто не использовать 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
как я сделал в моем примере.
Как я недавно узнал, есть также:
HStack {
...
}
.contentShape(Rectangle())
.onTapGesture { ... }
У меня хорошо работает.
работает как по волшебству на всех представлениях:
extension View {
func onTapGestureForced(count: Int = 1, perform action: @escaping () -> Void) -> some View {
self
.contentShape(Rectangle())
.onTapGesture(count:count, perform:action)
}
}
На мой взгляд, лучший подход из соображений доступности - обернуть HStack внутри метки кнопки, и для решения проблемы с 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())
}
Простое расширение на основе ответа Джима
extension Spacer {
/// https://stackru.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
}
Я смог обойти это, обернув Spacer в ZStack и добавив сплошной цвет с очень низкой непрозрачностью:
ZStack {
Color.black.opacity(0.001)
Spacer()
}
Я просто добавляю цвет фона (кроме
clear
цвет) для
HStack
работает.
HStack {
Text("1")
Spacer()
Text("1")
}.background(Color.white)
.onTapGesture(count: 1, perform: {
})
Просто добавление пустого модификатора фона работает.
HStack {
...
}
.background()
.onTapGesture { ... }
Хотя принятый ответ позволяет имитировать функциональность кнопки, визуально он не удовлетворяет. Не заменяйте
Button
с
.onTapGesture
или же
UITapGestureRecognizer
если все, что вам нужно, это область, которая принимает события касания пальцем. Такие решения считаются взломанными и не являются хорошей практикой программирования.
Для решения вашей проблемы вам необходимо реализовать
BorderlessButtonStyle
⚠️
Пример
Создайте общую ячейку, например
SettingsNavigationCell
.
НастройкиNavigationCell
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
, К сожалению, вам придется подобрать цвет фона, и это предполагает, что вам нечего отображать за кнопкой.
Обновление ответа sergioblancoo.
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
Этот веб-сайт помог мне ответить на тот же вопрос: https://www.hackingwithswift.com/quick-start/swiftui/how-to-control-the-tappable-area-of-a-view-using-contentshape .
Попробуйте применить.ContentShape(Rectangle())
кHStack
.