跨多个子视图的 SwiftUI 拖动手势

Posted

技术标签:

【中文标题】跨多个子视图的 SwiftUI 拖动手势【英文标题】:SwiftUI drag gesture across multiple subviews 【发布时间】:2020-01-18 06:02:55 【问题描述】:

我正在尝试创建一个由小方块视图组成的网格,当用户用拇指将鼠标悬停在它们上方(或在它们上滑动)时,小方块会暂时“弹出”并摇晃。然后,如果他们继续长按该视图,它将打开另一个包含更多信息的视图。

我认为在方形视图上实现拖动手势就足够了,但看起来一次只有一个视图可以捕获拖动手势。

有没有办法让多个视图捕捉拖动手势,或者为 ios 实现“悬停”手势?

这是我的主网格视图:

import SwiftUI

struct ContentView: View 
  @EnvironmentObject var data: PlayerData

    var body: some View 
      VStack 
        HStack 
      PlayerView(player: self.data.players[0])
      PlayerView(player: self.data.players[1])
      PlayerView(player: self.data.players[2])
        

        HStack 
      PlayerView(player: self.data.players[3])
      PlayerView(player: self.data.players[4])
      PlayerView(player: self.data.players[5])
        

        HStack 
      PlayerView(player: self.data.players[6])
      PlayerView(player: self.data.players[7])
      PlayerView(player: self.data.players[8])
        

        HStack 
      PlayerView(player: self.data.players[9])
      PlayerView(player: self.data.players[10])
        
      
  

这是我的 Square 视图,其中包含一个小摘要以显示在 Square 上:

import SwiftUI

struct PlayerView: View 
  @State var scaleFactor: CGFloat = 1.0
  var player: Player = Player(name: "Phile", color: .green, age: 42)

    var body: some View 
      ZStack(alignment: .topLeading) 
        Rectangle().frame(width: 100, height: 100).foregroundColor(player.color).cornerRadius(15.0).scaleEffect(self.scaleFactor)

        VStack 
          Text(player.name)
          Text("Age: \(player.age)")
        .padding([.top, .leading], 10)
      .gesture(DragGesture().onChanged  _ in
        self.scaleFactor = 1.5
      .onEnded _ in
        self.scaleFactor = 1.0
      )

  

【问题讨论】:

【参考方案1】:

这里是一个可能的方法的演示......(它是您的应用数据设置的简化版本,但应该明确的想法和发展方向)

主要思想不是在项目视图中捕获拖动,而是在内容视图中在需要时(或如果)将所需状态(或可计算的相关数据)传输到项目视图中。

struct PlayerView: View 
    var scaled: Bool = false
    var player: Player = Player(name: "Phile", color: .green, age: 42)

    var body: some View 
        ZStack(alignment: .topLeading) 
            Rectangle().frame(width: 100, height: 100).foregroundColor(player.color).cornerRadius(15.0).scaleEffect(scaled ? 1.5 : 1)

            VStack 
                Text(player.name)
                Text("Age: \(player.age)")
            .padding([.top, .leading], 10)
        .zIndex(scaled ? 2 : 1)
    



struct ContentView: View 
    @EnvironmentObject var data: PlayerData

    @GestureState private var location: CGPoint = .zero
    @State private var highlighted: Int? = nil

    private var Content: some View 
        VStack 
            HStack 
                ForEach(0..<3)  i in
                    PlayerView(scaled: self.highlighted == i, player: self.data.players[i])
                        .background(self.rectReader(index: i))
                
            
            .zIndex((0..<3).contains(highlighted ?? -1) ? 2 : 1)

            HStack 
                ForEach(3..<6)  i in
                    PlayerView(scaled: self.highlighted == i, player: self.data.players[i])
                        .background(self.rectReader(index: i))
                
            
            .zIndex((3..<6).contains(highlighted ?? -1) ? 2 : 1)
        
    

    func rectReader(index: Int) -> some View 
        return GeometryReader  (geometry) -> AnyView in
            if geometry.frame(in: .global).contains(self.location) 
                DispatchQueue.main.async 
                    self.highlighted = index
                
            
            return AnyView(Rectangle().fill(Color.clear))
        
    

    var body: some View 
        Content
        .gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
            .updating($location)  (value, state, transaction) in
                state = value.location
            .onEnded _ in
                self.highlighted = nil
            )
    

【讨论】:

我只是想用你的回答澄清一些事情,因为我对 swift 还很陌生,而且苹果文档似乎没有多大帮助......打电话给.background()在每个PlayerView 上都有一个View,这就是为什么我们可以给它一个GeometryReader 视图。 GeometryReader 视图捕获直接父几何图形,在这种情况下是 PlayerView 而不是 ContentView,对吗?以及对updating 的调用,value, state, transaction 参数是新值、与当前值的绑定(所以是$location)以及任何动画信息?

以上是关于跨多个子视图的 SwiftUI 拖动手势的主要内容,如果未能解决你的问题,请参考以下文章

将多个子视图传递给 SwiftUI 中的视图

SwiftUI:Toggle详解

有没有办法在 SwiftUI 中复制手势或处理它们并将它们传递给子视图?

在 SwiftUI 中,如何使手势在容器视图之外处于非活动状态?

如何检测哪个视图正在平移手势 Swift

通过 Swift 泛型初始化 SwiftUI 视图