There is a image and a button on Watchkit interface.
Image animation starts and i press the button, animation pause.
Now i need to read which frame is currently displayed.
Is it possible?
As Aleksander said it is not possible to get the right frame name from your animation. However you could divide your animation in parts and call the next part when the previous part is done animating. You could try my framework I just released to ease your work with animations and also have a completion handling.
TimelineKit - Framework
func startAnimationWithRange(range: Range)
{
Timeline.with(identifier: "MyAnimation", delay: 0.0, duration: 2.0, execution: {
// put your animation code in here
// assumed animation time == duration (2.0 seconds)
}, completion: {
// check if your button was clicked and return or call the next animation part
self.startAnimationWithRange(range: nextAnimationRange)
}).start
}
Check the TimelineKit.swift source file for documentation. Feedback is also welcome. :)
Related
I recently faced an issue where all layouts animated. I can't find out what causes this. Any idea about how can I detect the issue source would be welcome.
Controller hierarchy:
UITabBarController
UINavigationControler
UIViewController
UIPageViewController
UICollectionView
GoogleMap
Visible animated stuff: (without being inside animation block)
layout:
labels and views moving across the cell to the destination position (self-sizing cell)
horizontal collection view items comes from corner to their position
layer:
corner radius from 0 to height/2 animated.
view:
isHidden not works sometimes. It is inside UIStackView and when is hide it, it's just push it out from the stack view (visible because of the bug, strange but it's true)
setting title on buttons animated (setTitle:forState: method. And not the flashing animation, some kind of morphing animation)
Where are the layout and styling codes?
first after viewDidLoad and inside datasource didSet observer
What about threads?
I double checked all UI works dispatched in Main queue and Main Thread Checker is on.
- Show me The code !!!
Unfortunately this is an epic production project and changes that caused this issue is UNKNOWN. I wasn't able to reproduce the issue in some demo app to post it. Sorry
I found the issue after about 3 days of investigating and the answer in just a couple of minutes:
In some point of the code there was a animation block:
UIView.animate(withDuration: 1, animations: {
self.view.layoutIfNeeded()
})
I don't know why but this caused all layout animated for the rest of the controller's life (even after animation is done)
So for get around this I tried this:
UIView.animate(withDuration: 1, animations: {
DispatchQueue.main.async {
self.view.layoutIfNeeded()
}
})
Problem solved BUT!!! new issue appeared:
The original animation is not working at all!
So for the final workaround I changed the code a bit:
DispatchQueue.main.async {
UIView.animate(withDuration: 1, animations: {
self.view.layoutIfNeeded()
})
}
And fortunately it works! I knew:
all UI works should be done on main thread
and also animation blocks doesn't capture self, and perform their job on current thread. but strangely done.
I have a very simple animation block which performs the following animation:
(tap the image if the animation doesn't play)
func didTapButton() {
// reset the animation to 0
centerYConstraint.constant = 0
superview!.layoutIfNeeded()
UIView.animate(
withDuration: 1,
animations: {
// animate the view downwards 30 points
self.centerYConstraint.constant = 30
self.superview!.layoutIfNeeded()
})
}
Everything is great when I play the animation by itself. It resets to position 0, then animates 30 points.
The problem is when the user taps the button multiple times quickly (i.e. during the middle of an ongoing animation). Each time the user taps the button, I would expect it to reset to position 0, then animate downwards 30 points. Instead, I get this behavior:
(tap the image if the animation doesn't play)
It's clearly traveling well over 30 points. (Closer to 120 points.)
Why is this happening, and how can I "reset" the animation properly so that it only at most travels 30 points?
Things that I have tried that didn't work:
Using options: UIViewAnimationOptions.beginFromCurrentState. It does the same exact behavior.
Instead of executing the animation directly, do it after a few milliseconds using dispatch_after. Same behavior.
"Canceling" the previous animation by using a 0 second animation that changes the constant to 0, and calls superview!.layoutIfNeeded.
Other notes:
If instead of changing the position, I change the height via a constraint, I get similar odd behavior. E.g. if I set it to go from 30 points to 15 points, when repeatedly pressing the button, the view will clearly grow up to around 120 points.
Even if I don't use constraints, and instead I animate the transform from CGAffineTransform.identity to CGAffineTransform(scaleX: 0.5, y: 0.5), I get the same behavior where it'll grow to 120 points.
If I try a completely different animatable property say backgroundColor = UIColor(white: 0, alpha: 0.5) to backgroundColor = UIColor(white: 0, alpha: 0), then I get correct behavior (i.e. each time I tap the button, the color resets to 0.5 gray at most. It never gets to black.
I can reproduce the behavior you describe. But if I call removeAllAnimations() on the layer of the view that is moving, the problem goes away.
#IBAction func didTapButton(_ sender: Any) {
animatedView.layer.removeAllAnimations()
centerYConstraint.constant = 0
view.layoutIfNeeded()
centerYConstraint.constant = 30
UIView.animate(withDuration: 2) {
self.view.layoutIfNeeded()
}
}
Note, I'm not removing the animation from the superview (because that still manifests the behavior you describe), but rather of the view that is moving.
So far, the only solution I found is to recreate the view rather than trying to reset the existing one.
This gives me the exact behavior I was looking for:
(tap the image if the animation doesn't play)
It's unfortunate that you need to remove the old view and create a new one, but that's the only workaround I have found.
Can someone please help me??
Lets start with the main information: I created an app that has a 2 view and one is called InfoView. This InfoView is located off screen with the following code:
(Code Hide View)
UIView.animateWithDuration(1.0, delay: 0.3, options: [ ], animations: {self.InfoView.center.x -= self.view.bounds.width + 150}, completion: nil)
Its automatically moved when the app starts. I placed the Code Hide View on ViewDidAppear.
When the button Info is pressed the InfoView ease in from left to right to its original coordinates, (As I placed it on the storyboard with x=12 and y=20 and the Views size depends on the screen size). I use the following code to bring it back.
(Code Show View)
UIView.animateWithDuration(1.0, delay: 0.0, options: [ ], animations: {self.InfoView.center.x += self.view.bounds.width + 150}, completion: nil)
The InfoView has a button that when pressed hides the view (Code Hide View) once again so that its off screen.
My problem is that when I tryout the app if the Iphone receives a call (phone call or sms or etc), after the call had ended it would show the InfoView on screen and without pressing the Show View Code to run.
Ive then hidden the view with:
InfoView.Hidden = True
And when I run the Show View code it changes to false and when it hides it turns to true and so on. However now when I receive a call and after the call has ended One can't see the InfoView (Thats good) although If I press the show Info Button the InfoView will appear suddenly on screen and ease out the right side of the screen, making it unreachable (off screen).
My question is there a way to compare the InfoView coordinates so that if its on Screen (And I haven't called it, run the show View) it could be sent back off screen. Like with an if statement. for example like:
if InfoView == CGPoint(12,20).... //(Then Run Hide View)
(I know the if statement is wrong) And I do understand that I got to run that func in the app delegate in the applicationWillResignActive.
Please can someone help me, am I on the right track or can someone give me another solution. (Tell Me If I need to explain it better)
You should compare it by below way. Do not comapre with hardcoded point.
if(InfoView.center.x > self.view.bounds.width)
{
//infoview is hidden
}
else
{
//infoview is visible
}
I'm using the following code to have a label slide onto the screen when a button is pressed, but it's having no effect.
override func viewDidLayoutSubviews() {
summaryLabel.alpha = 0
}
#IBAction func searchButton(sender: AnyObject) {
UIView.animateWithDuration(2) { () -> Void in
self.summaryLabel.center = CGPointMake(self.summaryLabel.center.x - 400, self.summaryLabel.center.y)
self.summaryLabel.alpha = 1
self.summaryLabel.center = CGPointMake(self.summaryLabel.center.x + 400, self.summaryLabel.center.y)
}
//button code continues...
I've tested what's going on by fixing the alpha at 1, but the label just stays where it is and does not move when the button is pressed. What am I missing?
A couple of things:
First of all, your two changes to the view's center cancel each other out. The animation applies the full set of changes that are inside the animation block all in one animation. If the end result is no change, then no change is applied. As Ramy says in his comment, you either need 2 animations, timed so the second one takes place after the first one has completed, or you nee to apply the first change before the animation begins. I would suggest starting with a single change, and a single animation, and get that working first.
Second problem: View controllers use auto layout by default. With auto layout, you can't animate the position of a view directly. It doesn't work reliably. Instead, you have to put a constraint on the view, connect it to an outlet, and the animate a change to the constraint's constant value by changing the constant and calling layoutIfNeeded() inside the animation block. The call to layoutIfNeeded() inside the animation block causes the view's position to be changed, and since it's inside the animation block, the change is applied with animation.
I have a little setup where I have an MPMoviePlayerController setup as a video background. I'm trying to get it to have a smooth transition when it updates/changes. Here's my current code. BTW, newBackground.view.alpha is initially 0.
let oldBackground = currentBackground
self.view.insertSubview(newBackground.view, aboveSubview: oldBackground.view)
UIView.animateWithDuration(1.5 as NSTimeInterval, animations: {
newBackground.view.alpha = 1
}, completion: {
finished in
oldBackground.view.removeFromSuperview()
println("The subview should be removed now")
})
When this executes, the oldBackground.view is immediately removed before the newBackground starts to fade in. The println, however, happens after the animation is completed. I don't understand why the removeFromSuperview happens immediately, but the println happens when I expect it to. If I remove oldBackground.view.removeFromSuperview(), the animation fades in and looks fine (but the view obviously hasn't been removed, it's just sitting behind newBackground).
EDIT: Bizarrely enough, it seems to work as expected in the simulator. Running on my iPhone 6 Plus gives me the issue every time. I've uninstalled and re-run it from Xcode and the problem persists.
If anyone has any advice for me, I would be very happy. Thank you.
I guess when the newBackground.view.alpha = 1 is executed during animation it brings the background to visibility and overlays the oldBackground.
When you put the new subview above the old subview, old subview goes to the back. This happens before the animation code starts. Make sure your new subview's alpha is 0 before inserting it. And if you want a fade out animation you should set your oldBackground alpha to 0.
Like this :
let oldBackground = currentBackground
newBackground.view.alpha = 0
self.view.insertSubview(newBackground.view, aboveSubview: oldBackground.view)
UIView.animateWithDuration(1.5 as NSTimeInterval, animations: {
newBackground.view.alpha = 1
oldBackground.view.alpha = 0
}, completion: {
finished in
oldBackground.view.removeFromSuperview()
println("The subview should be removed now")
})