Add animations to ForEach loop elements (SwiftUI)
It looks like this problem is still up to day (Xcode 11.4), because by just copy-pasting the observed effect is the same. So, there are a couple of problems here: first, it needs to setup combination of animation and transition correctly; and, second, the ForEach
container have to know which exactly item is removed, so items must be identified, instead of indices, which are anonymous.
As a result we have the following effect (transition/animation can be others):
struct TestAnimationInStack: View {
@State var ContentArray = ["A","B","C", "D", "E", "F", "G", "I", "J"]
var body: some View {
ScrollView{
VStack{
ForEach(Array(ContentArray.enumerated()), id: \.element){ (i, item) in // << 1) !
ZStack{
// Object
Text(item)
.frame(width:100,height:100)
.background(Color.gray)
.cornerRadius(20)
.padding()
//Delete button
Button(action: {
withAnimation { () -> () in // << 2) !!
self.ContentArray.remove(at: i)
}
}){
Text("✕")
.foregroundColor(.white)
.frame(width:40,height:40)
.background(Color.red)
.cornerRadius(100)
}.offset(x:40,y:-40)
}.transition(AnyTransition.scale) // << 3) !!!
}
}
}
}
}
How to animate the removal of a view created with a ForEach loop getting its data from an ObservableObject in SwiftUI
It is not clear which effect do you try to achieve, but on remove you should animate not view internals, but view itself, ie. in parent, because view remove there and as-a-whole.
Something like (just direction where to experiment):
var body: some View {
ZStack {
ForEach(tagModel.tags, id: \.self) { label in
TagView(label: label)
.transition(.move(edge: .leading)) // << here !! (maybe asymmetric needed)
}
.onReceive(timer) { _ in
self.tagModel.addNextTag()
if tagModel.tags.count > 3 {
self.tagModel.removeOldestTag()
}
}
}
.animation(Animation.easeInOut(duration: 1)) // << here !! (parent animates subview removing)
Insert, update and delete animations with ForEach in SwiftUI
Luckily this is actually really easy to do. Simply remove .animation(.spring())
on your Row, and wrap any changes in withAnimation(.spring()) { ... }
.
So the add button will look like this:
private var AddButton: some View {
Button(action: {
withAnimation(.spring()) {
self.items.insert(Item(name: "Jeff"), at: 0)
}
}) {
Text("Add")
}
}
and your Row will look like this:
struct Row: View {
@State var name: String
var body: some View {
HStack {
Text(name)
Spacer()
}
.padding()
.transition(.move(edge: .leading))
}
}
How to add a modifier to any specific buttons inside a ForEach loop for an array of buttons in SwiftUI?
import SwiftUI
struct AnimatedListView: View {
var body: some View {
VStack{
ForEach(0..<5) { num in
//The content of the ForEach goes into its own View
AnimatedButtonView(num: num)
}
}
}
}
struct AnimatedButtonView: View {
//This creates an @State for each element of the ForEach
@State private var rotationDegree = 0.0
//Pass the loops data as a parameter
let num: Int
var body: some View {
Button {
withAnimation {
rotationDegree += 360
}
} label: {
Image(systemName: "person")
Text(num.description)
}
.rotation3DEffect((.degrees(rotationDegree)), axis: (x: 0, y: 1, z: 0))
}
}
struct AnimatedListView_Previews: PreviewProvider {
static var previews: some View {
AnimatedListView()
}
}
SwiftUI - How to animate components corresponding to array elements?
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()
}
}
}
How can I animate individual rows in SwiftUI List?
The key seems to be using the indices from the ForEach loop to set the times at which the animations appear.
Below is the code. The toggle switch just resets the state to show the animation:
struct GuideListView: View {
let data = ["One", "Two", "Three", "Four"]
@State var showListItems = false
@State var animationDelay = 0.5
// definitions of viewRouter, data etc.
var body: some View {
VStack {
// other items, navLink etc.
Toggle("Show List Items", isOn: $showListItems)
List {
ForEach(data.indices) { index in
Button(action: {
// navigation action
}, label: {
Text(data[index])
})
.opacity(showListItems ? 1 : 0)
.animation(Animation.easeOut(duration: 0.6).delay(animationDelay * Double(index)), value: showListItems)
} //: ForEach
} //: List
} //: VStack
}
}
Related Topics
How Constant Is the Firebase Anonymous Id
Using Stringbyreplacingcharactersinrange in Swift
How to Use Sf Rounded Font in Swiftui
Creating a Countableclosedrange<Character>
How to Add Kerning to a Textfield in Swiftui
How to Declare an Inline Function in Swift
Storing Different Types of Value in Array in Swift
How to Create a Window with Transparent Background with Swift on Osx
Unsaferawpointer Assumingmemorybound VS. Bindmemory
In Swiftui, Where Are the Control Events, I.E. Scrollviewdidscroll to Detect the Bottom of List Data
How to Write Inline Assembly in Swift
How to Cache Images Using Urlsession in Swift
Converting a C-Style for Loop That Uses Division for the Step to Swift 3
How to Create an Array of Functions
How to Rotate Only One View Controller to Landscape Orientation in iOS Swift 3
Swift's JSONdecoder with Multiple Date Formats in a JSON String
Difference Between Using Objectidentifier() and '===' Operator