SwiftUI synchronizing multiple SwiftUI animations with delays
The fix for the out of sync views is to put them all in one view and that view syncs everything simultaneously. It took me some time to work out the pattern, but here it is:
struct MyRepeatingItemView: View {
@State var isTranslated = false
var body: some View {
ZStack {
Rectangle()
.fill(Color.red)
.frame(width: 256, height: 256)
.opacity(isTranslated ? 0 : 0.25)
.scaleEffect(isTranslated ? 1 : 0.75)
Rectangle()
.fill(Color.red)
.frame(width: 256, height: 256)
.opacity(0.25)
.scaleEffect(isTranslated ? 0.75 : 0.5)
Rectangle()
.fill(Color.red)
.frame(width: 256, height: 256)
.opacity(0.25)
.scaleEffect(isTranslated ? 0.5 : 0.25)
Rectangle()
.fill(Color.red)
.frame(width: 256, height: 256)
.opacity(0.25)
.scaleEffect(isTranslated ? 0.25 : 0)
}
.onAppear {
withAnimation(Animation.linear(duration: 1).repeatForever(autoreverses: false)) {
isTranslated.toggle()
}
}
}
}
struct MyRepeatingContainerView: View {
var body: some View {
ZStack {
Color.blue
.frame(width: 256, height: 2)
Color.blue
.frame(width: 2, height: 256)
MyRepeatingItemView()
}
}
}
Swift iOS multiple successive multiple animations with same UIView?
i think the error is that you mix here 2 things:
UIView.animate
-> animate controls and propertiesUIImageView.startAnimating
-> start a loop of images in an UIImageView
but they don't do the same they are very independent. but UIView animation is normally for an other use case. only one thing is that they maybe have the same duration like your UIImageView animation, but you don't set the duration of your UIImageView. maybe when you set the animation duration of your image view to the duration of UIView animation then it is done on the same time range.
myImageView.animationDuration = 2;
and for the second loop
myImageView.animationDuration = 4;
Other Solutions
the thing is you need to know when an image loop ist completed. but there is no event for this (i did not found any)
there are some solutions on StackOverflow for this:
1 performSelector afterDelay
Set a timer to fire after the duration is done. for this you need also to add an animation duration to your image view:
myImageView.animationDuration = 0.7;
solution from here: how to do imageView.startAnimating() with completion in swift?:
When the button is pressed you can call a function with a delay
self.performSelector("afterAnimation", withObject: nil, afterDelay: imageView1.animationDuration)
Then stop the animation and add the last image of imageArray to the imageView in the afterAnimation function
func afterAnimation() {
imageView1.stopAnimating()
imageView1.image = imageArray.last
}
2 Timer
this is similar to performSelector afterDelay
but with NSTimer.
you find a description here UIImageView startAnimating: How do you get it to stop on the last image in the series? :
1) Set the animation images property and start the animation (as in
your code in the question)2) Schedule an NSTimer to go off in 'animationDuration' seconds time
3) When the timer goes off, [...]
add to point 3: then start the next animation
3 CATransaction
solution from here: Cocoa animationImages finish detection (you need to convert it to swift 3 syntax but it can be a starting point
Very old question, but I needed a fix now, this works for me:
[CATransaction begin];
[CATransaction setCompletionBlock:^{
DLog(@"Animation did finish.");
}];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.window.bounds];
imageView.animationDuration = 0.3 * 4.0;
imageView.animationImages = @[[UIImage AHZImageNamed:@"Default2"],
[UIImage AHZImageNamed:@"Default3"],
[UIImage AHZImageNamed:@"Default4"],
[UIImage AHZImageNamed:@"Default5"]];
imageView.animationRepeatCount = 1;
[self.window addSubview:imageView];
[imageView startAnimating];
[CATransaction commit];
4 Offtopic: Manual do the Image Animation
thats a little offtopic, because here they do the image animation manually. for your use case you just change the logic which image from index is visible. count forward until last image, then backwards until first image. and then stop the loop. not nice solution but in this solution is added a image transition animation:
Solution from here: Adding Image Transition Animation in Swift
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
let images = [
UIImage(named: "brooklyn-bridge.jpg")!,
UIImage(named: "grand-central-terminal.jpg")!,
UIImage(named: "new-york-city.jpg"),
UIImage(named: "one-world-trade-center.jpg")!,
UIImage(named: "rain.jpg")!,
UIImage(named: "wall-street.jpg")!]
var index = 0
let animationDuration: NSTimeInterval = 0.25
let switchingInterval: NSTimeInterval = 3
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = images[index++]
animateImageView()
}
func animateImageView() {
CATransaction.begin()
CATransaction.setAnimationDuration(animationDuration)
CATransaction.setCompletionBlock {
let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(self.switchingInterval * NSTimeInterval(NSEC_PER_SEC)))
dispatch_after(delay, dispatch_get_main_queue()) {
self.animateImageView()
}
}
let transition = CATransition()
transition.type = kCATransitionFade
/*
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromRight
*/
imageView.layer.addAnimation(transition, forKey: kCATransition)
imageView.image = images[index]
CATransaction.commit()
index = index < images.count - 1 ? index + 1 : 0
}
}
Implement it as a custom image view would be better.
Animations higher in the SwiftUI view hierarchy overriding nested animations
You need to link your internal animation to specific state variable, then it will be independent.
Tested with Xcode 12 / iOS 14
var body: some View {
Text("Example")
.opacity(opacity)
.animation(Animation.easeInOut(duration: 2).repeatForever(autoreverses: true),
value: opacity) // << here !!
.onAppear() {
self.opacity = 1
}
}
How to get different components to have different animations?
Transition works when specific view appeared, but in your code ZStack loads views and then appears as a whole.
Here is a fix:
HStack() {
ZStack() {
if animate { // << here !!
Rectangle()
.frame(height: 50)
.transition(.slide)
Image(systemName: "figure.mixed.cardio")
.foregroundColor(.white)
.transition(.scale)
}
}
}
SwiftUI - animation determined by multiple properties doesn't animate, or updates weirdly
The problem is in the value
of animation(_:value:)
. From the documentation:
A value to monitor for changes.
You're only passing in the first property toggle1IsOn
, so when the other ones are changed, the animation doesn't react. Instead, you should pass in the condition that causes the green to be shown:
value: toggle1IsOn && toggle2IsOn && toggle3IsOn
As long as value
conforms to Equatable
it's fine. You can also move it into a separate property, shouldFlash
, so you don't repeat code.
struct ContentView: View {
@State var toggle1IsOn = false
@State var toggle2IsOn = false
@State var toggle3IsOn = false
var body: some View {
/// accumulate into 1 constant
let shouldFlash = toggle1IsOn && toggle2IsOn && toggle3IsOn
VStack {
Toggle("Toggle 1", isOn: $toggle1IsOn)
Toggle("Toggle 2", isOn: $toggle2IsOn)
Toggle("Toggle 3", isOn: $toggle3IsOn)
Rectangle()
.fill(
shouldFlash
? Color.green
: Color.gray
)
.animation(
shouldFlash
? .default.repeatForever(autoreverses: true)
: .default,
value: shouldFlash /// here!
)
}
}
}
Result:
SwiftUI multiple vertical lists row animations not working
You can enable the animations on the List view:
List(visitManager.inProgress) { $call in
CallView(call: $call)
}
.animation(.default)
Or wrap the changes in a withAnimation block:
.onTapGesture { withAnimation { call.state = .accepted } }
As for the animation between the columns, you can get something like that with .matchedGeometryEffect
. afaik it will always look a bit crumbly between List, to make it look good you need to use a VStack
(but then loose all the comfort of the List view). For example:
import SwiftUI
extension Call {
enum State: Equatable, CaseIterable {
case inProgress
case accepted
case rejected
}
}
class CallManager: ObservableObject {
@Published var calls: [Call]
init(visits: [Call] = []) { calls = visits }
}
struct Call: Identifiable & Equatable & Hashable {
let id = UUID()
var state: State
}
struct ContentView: View {
@Namespace var items
@StateObject var visitManager = CallManager(visits: [
Call(state: .inProgress),
Call(state: .accepted),
Call(state: .accepted),
Call(state: .inProgress),
Call(state: .inProgress),
Call(state: .rejected),
])
var body: some View {
HStack(alignment: .top) {
ForEach(Call.State.allCases, id: \.self) { state in
VStack {
List(visitManager.calls.filter { $0.state == state }) { call in
CallView(call: call)
.id(call.id)
.matchedGeometryEffect(id: call.id, in: items)
.onTapGesture {
if let idx = visitManager.calls.firstIndex(where: { $0.id == call.id }) {
withAnimation {
visitManager.calls[idx].state = .rejected
}
}
}
}
}
}
}
}
}
struct CallView: View & Identifiable {
var call: Call
var id: UUID { call.id }
var body: some View {
Text(call.id.uuidString.prefix(15))
.foregroundColor(call.state.color)
}
}
extension Call.State {
var color: Color {
switch self {
case .inProgress: return .blue
case .rejected: return .red
case .accepted: return .green
}
}
}
A single component's animation is messing up the whole view — SwiftUI
You need to remove the animation(nil)
in the AvatarComponent
You also need to change all .frame(width: 352)
to .frame(width: UIScreen.main.bounds.size.width)
Related Topics
3 Component Dynamic Multi UIpickerview Swift
What Do Detached and Assigncurrentcontext Meaning
Add a File Generated by Run Script into The Test Target Compilation List in Xcode
Inmemory Realm Threading in Swift
Swift: Trunc a Floating Number to Show It in a Label
Swiftui New App Lifecycle How to Connect The Facebook Sdk
How to Provide Default Implementation of an Objective-C Protocol in a Swift Protocol Extension
Force Refresh on Another Viewcontroller Component with Swift
How We Can Read and Write to Same Observableobject in Swiftui
Communicate Data Between Watchos & Today Extension Widget
I Opened My App in Xcode 10 and Now I Have Errors in 9.4.1: Sdkapplicationdelegate (Facebookcore)
No Trailing Closures Support for Methods with Default Parameter Values
Can Not Get Correct Position of View in Swiftui
How to Detect Day Change in Swift
Urlcomponents Queryitems Losing Percent Encoding When Mutated