SwiftUI - 如何获取新创建的按钮的确切中心坐标/位置

Posted

技术标签:

【中文标题】SwiftUI - 如何获取新创建的按钮的确切中心坐标/位置【英文标题】:SwiftUI - how to get the exactly center coordinate/position of newly created Button 【发布时间】:2021-08-15 12:21:57 【问题描述】:

我是 swift 新手,遇到了一个问题,我在网上搜索了一整天,但仍然没有得到解决方案。

有一个类似的问题,我也尝试使用同时手势来获取位置,但它非常不精确,并且基于我拖动的方向有相对偏差。 所以我想知道在呈现按钮后是否有任何其他方法可以调用我的代码并给我按钮的引用以便我可以获得位置? 刚刚发现我可以使用 startLocation 属性,但它可能不是按钮的中心点,因为它取决于用户拖动的开始位置。

SwiftUI - how to get coordinate/position of clicked Button

问题: 渲染后如何获取按钮相对于屏幕的 x 和 y 位置?

我的目标: 我想根据指定按钮的位置生成其他一些视图。一旦用户按下它,它和其他一些视图会在用户释放按钮时弹出并消失。因此需要一个精确的原始位置来计算新的位置。

我想创建的这个功能有点像游戏应用程序,但我正在尝试创建一个普通的简单应用程序。如果有人能提供帮助,不胜感激!

【问题讨论】:

你检查过按钮的框架吗?它包含高度、宽度、x 和 y 位置... @FelipeCruzV10 我认为是 SwiftUI。 @FelipeCruzV10 我知道我们可以通过Frame定义它的位置,但我想知道在渲染后是否有任何方法可以知道中心位置而不定义位置? 【参考方案1】:

实现这一点的一种方法是利用“锚偏好”。

这个想法是,在创建按钮时创建按钮的边界锚并将其存储到锚首选项中。

要获得实际的边界值,我们需要一个 GeometryProxy,在其中关联边界锚点并获取边界值。

当我们有边界值时,我们将它存储在一个状态变量中,当按钮操作执行时它们可以访问。

以下解决方案创建了许多按钮,其中的边界可通过字典访问,其中键是按钮的标签。

import SwiftUI

struct ContentView: View 

    let labels = (0...4).map  "- \($0) -" 

    @State private var bounds: [String: CGRect] = [:]

    var body: some View 
        VStack 
            ForEach(labels, id: \.self)  label in
                Button(action: 
                    let bounds = bounds[label]
                    print(bounds ?? "")
                ) 
                    Text(verbatim: label)
                
                // Store bounds anchors into BoundsAnchorsPreferenceKey:
                .anchorPreference(
                    key: BoundsAnchorsPreferenceKey.self,
                    value: .bounds,
                    transform:  [label: $0] )
            
        
        .frame(width: 300, height: 300, alignment: .center)
        .backgroundPreferenceValue(BoundsAnchorsPreferenceKey.self)  anchors in
            // Get bounds relative to VStack:
            GeometryReader  proxy in
                let localBoundss = anchors.mapValues  anchor in
                    CGRect(origin: proxy[anchor].origin, size: proxy[anchor].size)
                
                Color.clear.border(Color.blue, width: 1)
                    .preference(key: BoundsPreferenceKey.self, value: localBoundss)
            
        
        .onPreferenceChange(BoundsPreferenceKey.self)  bounds in
            // Store bounds into the state variable:
            self.bounds = bounds
        
    



extension CGRect: Hashable 
    public func hash(into hasher: inout Hasher) 
        hasher.combine(origin.x)
        hasher.combine(origin.y)
        hasher.combine(size.width)
        hasher.combine(size.height)
    


struct BoundsAnchorsPreferenceKey: PreferenceKey 
    typealias Value = [String: Anchor<CGRect>]
    static var defaultValue: Value = [:]

    static func reduce(value: inout Value, nextValue: () -> Value) 
        value = value.merging(nextValue())  (_, new) in new 
    


struct BoundsPreferenceKey: PreferenceKey 
    typealias Value = [String:  CGRect]
    static var defaultValue: Value = [:]

    static func reduce(value: inout Value, nextValue: () -> Value) 
        value = value.merging(nextValue())  (_, new) in new 
    



import PlaygroundSupport
PlaygroundPage.current.setLiveView(
    NavigationView 
        ContentView()
    
    .navigationViewStyle(.stack)
)

目前的解决方案看起来有点复杂 - 但没有使用任何“技巧”。我们可以通过使用 ViewModifiers 来缓解这种情况。

【讨论】:

以上是关于SwiftUI - 如何获取新创建的按钮的确切中心坐标/位置的主要内容,如果未能解决你的问题,请参考以下文章

如何从导航栏按钮导航到新视图在 SwiftUI 中单击

SwiftUI:如何在单击 ActionSheet 按钮时呈现新视图?

日期选择器按钮 / Swiftui

如何使用 SwiftUI 在动作关闭中获取按钮本身?

SwiftUI 如何在新视图中链接 ForEach 索引和照片索引?

如何使用 SwiftUI 在多个屏幕上获取键盘高度并移动按钮