How to set zIndex inside VStack and HStack

I'm trying to display overlapping images and when one image is selected and moved it is brought to the front by setting .zIndex. The code works fine when the images are just in one ZStack or just in one HStack but when the two are nested it doesn't work. Does anyone know what code changes are required to make this function the way I want it to?

enum DragState {
  case good
  case bad
}

struct MyObject: View {
  @State var dragAmount = CGSize.zero
  @State var dragState = DragState.bad
   
  var myImage: Image = Image("myImage")
  var onChanged: ((CGPoint, MyObject) -> DragState)?

  var body: some View {
    myImage
    .resizable()
    .frame(width: 100, height: 150)
    .offset(dragAmount)
    .zIndex(dragAmount == .zero ? 0 : 1)
    .gesture(
      DragGesture(coordinateSpace: .global)
        .onChanged {
          self.dragAmount = CGSize(width: $0.translation.width, height: $0.translation.height)
          self.dragState = self.onChanged?($0.location, self) ?? .bad
        }
        .onEnded {
          _ in
          self.dragAmount = .zero
        })
  }
}

struct ContentView: View {
  @State private var cardFrames = [CGRect](repeating: .zero, count: 7)

  var body: some View {
    var i = 0
    VStack (alignment: .trailing, spacing: -85) {
      ForEach(0..<3) { row in
        HStack {
          ForEach(row..<3) { col in
            MyObject()
              .allowsHitTesting(true)
              .overlay(
                GeometryReader { geo in
                Color.clear
                  .onAppear {
                    self.cardFrames[i] = geo.frame(in: .global)
                i = i + 1
                  }
                }
              )
          }
        }
      }
    }
  }
   
  func moved(p1:CGPoint, object: MyObject) -> DragState {
     return .good
  }
}
Answered by Claude31 in 745388022

Using a ZStack and positioning MyObject directly seems to achieve pretty well what you expect.

struct ContentView: View {
  var body: some View {

      ZStack() {
          ForEach(0..<3) { row in
              ForEach(0..<3) { col in
                  MyObject()
                      .offset(x: 120 * CGFloat(col-1), y: 160 * CGFloat(row))
              }
          }
      }

  }
}

I do not see any ZStack in your code, so you probably don't show the relavant code.

Why do you create several ZStack and not a single one ?

Hi Claude. Thanks for your reply. Here is a smaller code example that illustrates my problem. I'm using nested VStacks and HStacks to get the 3x3 grid layout I desire. If I select an image in row 1 it will jump to the front of all other images on row 1 but not in front of images on row 2 or row 3. If I select a card on row 2 it will jump in front of cards on row 1 and 2 but not 3. If I select a card on row 3 it jumps in front of all cards on the screen. I'd like to bring any card, regardless of what row it is in to the front of the screen. I'm very new to swift so maybe this is not something that can be done using this layout. If not what is the approach I should use. Thanks

struct MyObject: View {
  @State var dragAmount = CGSize.zero

  var myImage: Image = Image("myImage")

  var body: some View {
    myImage
    .resizable()
    .frame(width: 100, height: 150)
    .offset(dragAmount)
    .zIndex(dragAmount == .zero ? 0 : 1)
    .gesture(
      DragGesture(coordinateSpace: .global)
        .onChanged {
          self.dragAmount = CGSize(width: $0.translation.width, height: $0.translation.height)
        }
        .onEnded {
          _ in
          self.dragAmount = .zero
        })
  }
}

struct ContentView: View {
  var body: some View {
    VStack () {
      ForEach(0..<3) { row in
        HStack {
          ForEach(0..<3) { col in
            MyObject()
          }
        }
      }
    }
  }
}

AFAIK, you need to put this in a ZStack: https://www.hackingwithswift.com/quick-start/swiftui/how-to-change-the-order-of-view-layering-using-z-index

Accepted Answer

Using a ZStack and positioning MyObject directly seems to achieve pretty well what you expect.

struct ContentView: View {
  var body: some View {

      ZStack() {
          ForEach(0..<3) { row in
              ForEach(0..<3) { col in
                  MyObject()
                      .offset(x: 120 * CGFloat(col-1), y: 160 * CGFloat(row))
              }
          }
      }

  }
}
How to set zIndex inside VStack and HStack
 
 
Q