Home > Net >  SwiftUI - How to animate components corresponding to array elements?
SwiftUI - How to animate components corresponding to array elements?

Time:02-19

I have an HStack of circles in SwiftUI, and the number of circles is determined based on the length of an array, like this:

@State var myArr = [...]
...
ScrollView(.horizontal) {
  HStack {
    ForEach(myArr) { item in
      Circle()
      //.frame(...)
      //.animation(...) I tried this, it didn't work
    }
  }
}

Then I have a button that appends an element to this array, effectively adding a circle to the view:

Button {
  myArr.append(...)
} label: {
  ...
}

The button works as intended, however, the new circle that is added to the view appears very abruptly, and seems choppy. How can I animate this in any way? Perhaps it slides in from the side, or grows from a very small circle to its normal size.

CodePudding user response:

You are missing transition, here is what you looking:

 struct ContentView: View {
    
    @State private var array: [Int] = Array(0...2)
    
    var body: some View {
        
        ScrollView(.horizontal) {
            HStack {
                ForEach(array, id:\.self) { item in
                    
                    Circle()
                        .frame(width: 50, height: 50)
                        .transition(AnyTransition.scale)
                    
                }
            }
        }
        .animation(.default, value: array.count)
        
        
        Button("add new circle") {
            array.append(array.count)
        }
        
        Button("remove a circle") {
            if array.count > 0 {
                array.remove(at: array.count  - 1)
            }
        }
 
    }
}

CodePudding user response:

a version with automatic scroll to the last circle:

struct myItem: Identifiable, Equatable {
    let id = UUID()
    var size: CGFloat
}

struct ContentView: View {
    
    @State private var myArr: [myItem] = [
        myItem(size: 10),
        myItem(size: 40),
        myItem(size: 30)
    ]
    
    var body: some View {
        
        ScrollViewReader { scrollProxy in
            VStack(alignment: .leading) {
                Spacer()
                
                ScrollView(.horizontal) {
                    HStack {
                        ForEach(myArr) { item in
                            Circle()
                                .id(item.id)
                                .frame(width: item.size, height: item.size)
                                .transition(.scale)
                        }
                    }
                }
                .animation(.easeInOut(duration: 1), value: myArr)
                
                Spacer()
                
                Button("Add One") {
                    let new = myItem(size: CGFloat.random(in: 10...100))
                    myArr.append(new)
                }
                
                .onChange(of: myArr) { _ in
                    withAnimation {
                        scrollProxy.scrollTo(myArr.last!.id, anchor: .trailing)
                    }
                }
                
                .frame(maxWidth: .infinity, alignment: .center)
            }
            .padding()
        }
    }
}
  • Related