0

In the example below I want to modify the struct in an array that is used to generate a view from that view. It results in an obvious error (in ForEach the reference to the currently processed item is a let constant). How else I can modify the original array row upon interaction with the view at runtime? In other words, how do I store/pass a reference to the corresponding array item?

I also tried passing the array index to inside the view and accessing it there directly by array[index].someproperty. It worked great... until I tried deleting rows. In such case the dynamic views no longer contain correct indexes and it generally leads to out of range errors.

In the example below I want to save drag location to the original array rather than to the view itself so that I can conduct logic on it later (can't iterate through views, but can easily iterate over their underlaying array). Could someone point me to the right approach? Thanks!

EDIT: Clarification: The goal here is for the drag gesture to write values to the ballStorage object's array and have generated views react accordingly. But it needs to know which row to modify!

import SwiftUI


let screenWidth = UIScreen.main.bounds.size.width
let screenHeight = UIScreen.main.bounds.size.height


struct SingleBall: Identifiable {
    var id = UUID()
    var position = CGPoint.zero
}

class BallStorage: ObservableObject {
    
    @Published var balls: [SingleBall] = [
        
        SingleBall(position: CGPoint(x: 110, y: 220)),
        SingleBall(position: CGPoint(x: 150, y: 120)),
        SingleBall(position: CGPoint(x: 200, y:160)),
        SingleBall(position: CGPoint(x: 200, y: 200))
        
    ]
    
}


struct StartScreen: View {
    
    
    @StateObject var ballStorage = BallStorage()
    
    
    var body: some View {
        
        
        
        ZStack { // stack of balls
            
            
            ForEach(ballStorage.balls, id: \.id) {
                
                ball in
                littleBall(id: ball.id).position(ball.position).gesture(DragGesture()
                                                                            
                .onChanged {
                gesture in
 ball.position = gesture.location //ERROR! can't modify ball (it's a let constant)
                                                                            }
                                                                        
                )
                
            }
            
        }.frame(width:screenWidth, height:screenHeight) //end stack balls
        
        
    }
    
}

struct test_Previews: PreviewProvider {
    static var previews: some View {
        
        StartScreen()
        
    }
}

EDIT: Another thing I tried was passing 'ball' (see the code) to inside the view. I can modify it there, and the view reacts to the changes, but in this case what gets passed is a copy of the array item, not a reference to the actual struct in the original array. So modifying the array has no effect on views.

darekm
  • 285
  • 1
  • 12

1 Answers1

1

You'd need to modify the original array instead, so you'd need an index, which you can get by doing ForEach over indices:

ForEach(ballStorage.balls.indices, id: \.self) { index in
   littleBall(id: ballStorage.balls[index].id)
      .position(ballStorage.balls[index].position)
      //...
      .onChanged { gesture in
          // modify ballStorage.balls[index]
          ballStorage.balls[index].position = gesture.location
      }
}
New Dev
  • 48,427
  • 12
  • 87
  • 129
  • Thanks! So this works great until I delete a row from the array (which I need to be able to do). In such case it will result in a Out-of-range error and a crash. – darekm Dec 09 '20 at 20:28
  • So, *that* is a different issue, similar to this: https://stackoverflow.com/q/63079221/968155 – New Dev Dec 09 '20 at 20:30
  • I was afraid of that - I hate to use weird hacky extensions to get around some super basic problem. I'd rather wait for Apple to fix this. Anyways it sure looks like the best approach with accessing the original array with index which was the original problem. Thank you! – darekm Dec 09 '20 at 20:41