SwiftUI - 如何获得点击按钮的坐标/位置

Ben*_*ain 6 swift swiftui

简短版本:如何获取Button在 SwiftUI 中单击的坐标?

我正在寻找这样的东西(伪代码)在geometry.x当前视图中单击按钮的位置:

GeometryReader { geometry in      
     return Button(action: { self.xPos = geometry.x}) {  
          HStack {               
               Text("Sausages")  
          }  
     }  
}
Run Code Online (Sandbox Code Playgroud)

长版:我正在开始使用 SwiftUI 和 Swift,所以想知道如何最好地从概念上实现这一点。

举一个我正在玩的具体例子:

想象一个选项卡系统,我想将下划线指示器移动到单击按钮的位置。

[aside] 这篇文章中有一个答案在视觉上做了我想要的,但看起来相当复杂:How to make 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的结构,它创建每个选项卡项并包含一个嵌套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
    }
}
Run Code Online (Sandbox Code Playgroud)

创建一个非 SwiftUIUILabel来获取宽度Button似乎有点不稳定。有没有更好的办法?

有没有一种简单的方法来获取点击的 SwiftUI 的坐标Button

oma*_*mar 6

您可以使用最小拖动距离为 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
        })
    }
}

Run Code Online (Sandbox Code Playgroud)

coordinateSpace参数指定您是否想要触摸位置在.local.global空间中。在本地空间中,该位置是相对于您已将手势附加到的视图的。例如,如果我Text在屏幕中间有一个视图,我的本地y位置将几乎为 0,而我的全局位置y将是屏幕高度的一半。

这让我有点困惑,但这个例子展示了这个想法:

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
                })
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Ben*_*ain 1

事实证明,我通过调整https://swiftui-lab.com/communicating-with-the-view-tree-part-2/上的示例解决了这个问题

该技术的本质是使用anchorPreference一种将有关一个视图的数据发送到链上祖先视图的方法。我在苹果世界找不到任何关于此的文档,但我可以证明它是有效的。

我不会在此处添加代码,因为参考链接还包含我认为没有资格在此重复的解释!