简短版本::如何获取SwiftUI中被点击的Button
的坐标?
我正在寻找类似的东西(伪代码),其中geometry.x
是当前视图中被单击按钮的位置:
GeometryReader { geometry in
return Button(action: { self.xPos = geometry.x}) {
HStack {
Text("Sausages")
}
}
}
长版:我正在开始使用SwiftUI和Swift,因此想知道如何最好地从概念上实现这一目标。
举一个具体的例子,我正在玩:
想象一下一个标签系统,我想将下划线指示器移动到单击按钮的位置。
[放在一边] 这篇文章中有一个答案可以直观地实现我的目标,但似乎相当复杂:How to make view the size of another view in SwiftUI [/ aside]
这是我的外部结构,用于构建选项卡栏和矩形(当前指示器),我正在尝试调整大小和位置:
import SwiftUI
import UIKit
struct TabBar: View {
var tabs:ITabGroup
@State private var selected = "Popular"
@State private var indicatorX: CGFloat = 0
@State private var indicatorWidth: CGFloat = 10
@State private var selectedIndex: Int = 0
var body: some View {
ScrollView(.horizontal) {
HStack(spacing: 0) {
ForEach(tabs.tabs) { tab in
EachTab(tab: tab, choice: self.$selected, tabs: self.tabs, x: self.$indicatorX, wid: self.$indicatorWidth)
}
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 40, maxHeight: 40).padding(.leading, 10)
.background(Color(UIColor(hex: "#333333")!))
Rectangle()
.frame(width: indicatorWidth, height: 3 )
.foregroundColor(Color(UIColor(hex: "#1fcf9a")!))
.animation(Animation.spring())
}.frame(height: 43, alignment: .leading)
}
}
这是我的结构,它创建每个选项卡项目,并包括一个嵌套的func
以获取所单击项目的宽度:
struct EachTab: View {
// INCOMING!
var tab: ITab
@Binding var choice: String
var tabs: ITabGroup
@Binding var x: CGFloat
@Binding var wid: CGFloat
@State private var labelWidth: CGRect = CGRect()
private func tabWidth(labelText: String, size: CGFloat) -> CGFloat {
let label = UILabel()
label.text = labelText
label.font = label.font.withSize(size)
let labelWidth = label.intrinsicContentSize.width
return labelWidth
}
var body: some View {
Button(action: { self.choice = self.tab.title; self.x = HERE; self.wid = self.tabWidth(labelText: self.choice, size: 13)}) {
HStack {
// Determine Tab colour based on whether selected or default within black or green rab set
if self.choice == self.tab.title {
Text(self.tab.title).foregroundColor(Color(UIColor(hex: "#FFFFFF")!)).font(.system(size: 13)).padding(.trailing, 10).animation(nil)
} else {
Text(self.tab.title).foregroundColor(Color(UIColor(hex: "#dddddd")!)).font(.system(size: 13)).padding(.trailing, 10).animation(nil)
}
}
}
// TODO: remove default transition fade on button click
}
}
创建一个非SwiftUI UILabel
来获得Button
的宽度似乎有点不可思议。有更好的方法吗?
是否有一种简单的方法来获取所单击的SwiftUI Button
的坐标?
答案 0 :(得分:1)
您可以使用最小拖动距离为0的DragGesture识别器,它可以为您提供位置信息。但是,如果将DragGesture与按钮结合使用,则在正常单击按钮时不会触发拖动手势。仅当拖动结束于按钮之外时才会触发。
您可以完全摆脱按钮,但是当然您会丢失默认的按钮样式。
在这种情况下,视图将如下所示:
struct MyView: View {
@State var xPos: CGFloat = 0
var body: some View {
GeometryReader { geometry in
HStack {
Text("Sausages: \(self.xPos)")
}
}.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global).onEnded { dragGesture in
self.xPos = dragGesture.location.x
})
}
}
coordinateSpace
参数指定您是否希望在.local
或.global
空间中放置触摸位置。在局部空间中,位置是相对于将手势附加到的视图的。例如,如果我在屏幕中间有一个Text
视图,则我的本地y
位置将几乎为0,而我的全局y
将是屏幕高度的一半。 / p>
这让我有点震惊,但是这个例子说明了这个想法:
struct MyView2: View {
@State var localY: CGFloat = 0
@State var globalY: CGFloat = 0
var body: some View {
VStack {
Text("local y: \(self.localY)")
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local).onEnded { dragGesture in
self.localY = dragGesture.location.y
})
Text("global y: \(self.globalY)")
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global).onEnded { dragGesture in
self.globalY = dragGesture.location.y
})
}
}
}
答案 1 :(得分:1)
struct ContentView: View {
var body: some View {
VStack {
Button(action: { print("Button pressed")}) { Text("Button") }
}.simultaneousGesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
.onEnded { print("Changed \($0.location)") })
}
}
此解决方案似乎可以工作,在Button上同时添加手势,但是不幸的是,如果Button放置在窗体中,则无法正常工作
答案 2 :(得分:-1)
原来,我通过改编https://swiftui-lab.com/communicating-with-the-view-tree-part-2/上的示例来解决了这个问题
该技术的本质是使用anchorPreference
,这是一种将有关一个视图的数据发送回链到祖先视图的方法。我在Apple世界中找不到与此相关的任何文档,但我可以证明它可以正常工作。
我没有在此处添加代码,因为参考链接还包含一些说明,说明我不具备在此处重复的资格!