SwiftUI 无法点击 HStack 的 Spacer
SwiftUI can't tap in 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()
所在的部分)时,点击操作未注册。仅当我点击文本或图像时才会发生点击操作。
有没有其他人遇到过这个问题/知道解决方法?
我已经就此提交了 feedback,建议您也这样做。
与此同时,不透明的 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
。
我已经能够通过将 Spacer 包裹在 ZStack 中并添加不透明度非常低的纯色来解决这个问题:
ZStack {
Color.black.opacity(0.001)
Spacer()
}
据我最近了解到还有:
HStack {
...
}
.contentShape(Rectangle())
.onTapGesture { ... }
很适合我。
基于吉姆回答的简单扩展
extension Spacer {
///
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
}
我只是为 HStack
作品添加背景颜色(clear
颜色除外)。
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
,除非您只需要一个接受手指点击事件的区域。这样的解决方案被认为是 hacky,不是好的编程实践。
要解决您的问题,您需要实施 BorderlessButtonStyle
⚠️
示例
创建一个通用单元格,例如SettingsNavigationCell
.
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 cannot be tap 的问题,您可以添加一个 .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())
}
我有一个列表视图,列表的每一行都包含一个带有一些文本视图和图像的 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()
所在的部分)时,点击操作未注册。仅当我点击文本或图像时才会发生点击操作。
有没有其他人遇到过这个问题/知道解决方法?
我已经就此提交了 feedback,建议您也这样做。
与此同时,不透明的 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
。
我已经能够通过将 Spacer 包裹在 ZStack 中并添加不透明度非常低的纯色来解决这个问题:
ZStack {
Color.black.opacity(0.001)
Spacer()
}
据我最近了解到还有:
HStack {
...
}
.contentShape(Rectangle())
.onTapGesture { ... }
很适合我。
基于吉姆回答的简单扩展
extension Spacer {
///
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
}
我只是为 HStack
作品添加背景颜色(clear
颜色除外)。
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
,除非您只需要一个接受手指点击事件的区域。这样的解决方案被认为是 hacky,不是好的编程实践。
要解决您的问题,您需要实施 BorderlessButtonStyle
⚠️
示例
创建一个通用单元格,例如SettingsNavigationCell
.
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 cannot be tap 的问题,您可以添加一个 .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())
}