I've attached a tap gesture to a view, at first it seemed to work, but after animation the view the gesture is complete ignored until the view is back at it's original position.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap:)];
[self.cellView addGestureRecognizer:tap];
[tap release];
In here I animate the view to the right
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^
{
[cellView setFrame:CGRectMake(cellViewX, cellView.frame.origin.y, cellView.frame.size.width, cellView.frame.size.height)];
[editView setFrame:CGRectMake(editViewX, editView.frame.origin.y, cellView.frame.size.width, cellView.frame.size.height)];
}
completion:^(BOOL finished)
{
NSLog(#"Animation complete");
}];
The completion handler is triggered, but now the tap gesture is completely being ignored.
Ok, turns out I just made a stupid copy/paste error, I accidentally used the wrong values when setting the frame for the animations
[cellView setFrame:CGRectMake(cellViewX, cellView.frame.origin.y, cellView.frame.size.width, cellView.frame.size.height)];
[editView setFrame:CGRectMake(editViewX, cellView.frame.origin.y, cellView.frame.size.width, cellView.frame.size.height)];
Had to be
[cellView setFrame:CGRectMake(cellViewX, cellView.frame.origin.y, cellView.frame.size.width, cellView.frame.size.height)];
[editView setFrame:CGRectMake(editViewX, editView.frame.origin.y, editView.frame.size.width, editView.frame.size.height)];
So yes, when you copy/past code around midnight, make sure to double check it in the morning! :)
Related
I want to add a heartbeat effect to my view , one way to do is use UIView's animation method as follow :
- (void)heartBeating
{
UIView *panel;
NSTimeInterval during = 0.8;
[UIView animateKeyframesWithDuration:during delay:0.0
options:UIViewKeyframeAnimationOptionCalculationModeLinear|UIViewKeyframeAnimationOptionRepeat
animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.4 animations:^{
panel.transform = CGAffineTransformMakeScale(1.2, 1.2);
}];
[UIView addKeyframeWithRelativeStartTime:0.4 relativeDuration:0.6 animations:^{
panel.transform =CGAffineTransformIdentity ;
}];
} completion:^(BOOL finished) {
}];
}
the problem is : if i want to stop the animation in an action , for example , a Stop butotn tapped , how I can do .
I know I can realize the same effect by create CAKeyFrameAnimation and add it to the view' CALayer .But I want to know how to do with the UIView's animation method. Thanks.
Try
[panel1.layer removeAllAnimations];
Hope this will help you.
Can anyone say how to implement transition segue like in facebook app when i open image to fullscreen?
I fount this example http://joris.kluivers.nl/blog/2013/01/15/custom-view-controller-transitions-using-uistoryboardsegues/
but it blinking during the transition on ios7 and i don't understand how to fix it( Have anyone any ideas?
The blinking is being caused by the temporary image that is created to simulate the transition being removed before the image is unhidden in the new view. I changed the completion block to delay by .25 seconds to allow for the image to be unhidden.
[UIView animateWithDuration:0.4f delay:.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
imageView.frame = dest;
if (self.unwinding) {
[self.destinationViewController dismissViewControllerAnimated:YES completion:nil];
} else {
((UIViewController *)self.destinationViewController).modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.sourceViewController presentViewController:self.destinationViewController animated:YES completion:nil];
}
} completion:^(BOOL completed) {
int64_t delayInSeconds = 1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC/4);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
imageView.hidden = YES;
[imageView removeFromSuperview];
});
}];
My app situation:
I am now having an app displaying a dynamic picture. And I want to capture its current image and save it to disc.
My goal:
I am working on an animated visual effect: add an image view on top, and then animates it from its origin frame into CGRecrZero. Finally, remove this image view from vie hierarchy after animation is done.
Now problem:
First time triggerring the action, it seems work. But when trigger it at the second time, the image view does not animate and stay atop of all subviews.
Codes of the view controller(with ARC):
Firstly, this is the IBAction to trigger the snapshot:
- (IBAction)actionSaveSnapshot:(id)sender
{
... // Save the image file. This works everytime.
NSThread *tempThread = [[NSThread alloc] initWithTarget:self
selector:#selector(threadSimulateScreenCapture:)
object:nil];
if (tempThread)
{
[tempThread start];
tempThread = nil; // Simply release it
}
}
And here is the thread:
- (void)threadSimulateScreenCapture:(id)arg
{#autoreleasepool {
NSImageView __strong *subImageView;
subImageView = [[NSImageView alloc] init];
NSRect subRect = [_imageView frame]; // the dynamic image view which I want to snapshot with
const CGFloat animationTime = 0.4;
[subImageView setWantsLayer:YES];
[subImageView setImageScaling:NSImageScaleAxesIndependently];
[subImageView setImage:[_imageView image]];
dispatch_async(dispatch_get_main_queue(), ^{
[CATransaction begin];
[self.view addSubview:subImageView];
[subImageView setFrame:subRect];
[subImageView setNeedsDisplay:YES];
[CATransaction commit];
});
[NSThread sleepForTimeInterval:0.01];
dispatch_async(dispatch_get_main_queue(), ^{
[CATransaction begin];
[CATransaction setAnimationDuration:animationTime];
[subImageView.layer setFrame:CGRectZero];
[CATransaction commit];
});
[NSThread sleepForTimeInterval:(NSTimeInterval)animationTime]; // wait for end of animation
dispatch_async(dispatch_get_main_queue(), ^{
[subImageView removeFromSuperview];
});
NSLog(#"SubView Count: %#\n%#", self.view.subviews, subImageView); // MARK. described below
subImageView = nil; // release the image
}} // ENDS: thread and aotureleasepool
Every time the program runs to the "MARK" line and print the subview array, it is clear thar the subImageView is not removed from superview then. And every time the thread ends, a "deleted thread with uncommitted CATransaction ...." raised.
What wrong did I do?
I may solved my own problem: At the line with [subImageView setFrame:subRect];, insert another line with: [subImageView.layer setFrame:subRect];
Meanwhile, add another line before the "addSubview:" method: [self.view setWantsLayer:YES];.
It seemed work. But I do not know why. Why I should set both view and layer's frame?
I'm trying to loop over multiple UIViews and perform animation on each, but I'd like to know when all of the animations have finished. What is the best way to call a function when the loop of animations has finished? Alternately, is there a way to wait until all have finished?
I tried using setAnimationDidStopSelector, but it doesn't fire. In this case, I'm clearing some "chips" off a game board by having them fade out then get removed (NumberedChipView is a subclass of UIView). Play cannot continue until the chips are off the board.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelegate: self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop)];
// clear chips
for (HexagonalTile *aTile in tilesToClear) {
NumberedChipView *chipView = [self chipViewForColumn:aTile.column andRow:aTile.row];
[UIView animateWithDuration:0.5 delay:0.3 options:UIViewAnimationCurveLinear animations:^{
chipView.alpha = 0.0;
}
completion:^(BOOL finished){
if (finished) {
[chipView removeFromSuperview];
[self.chipViews removeObject:chipView];
}
}];
}
[UIView commitAnimations];
I also tried CATransaction to no avail:
[CATransaction begin];
[CATransaction setCompletionBlock:^{
[self animationFinished];
}];
// clear chips
for (HexagonalTile *aTile in tilesToClear) {
NumberedChipView *chipView = [self chipViewForColumn:aTile.column andRow:aTile.row];
[UIView animateWithDuration:0.5 delay:0.3 options:UIViewAnimationCurveLinear animations:^{
chipView.alpha = 0.0;
}
completion:^(BOOL finished){
if (finished) {
[chipView removeFromSuperview];
[self.chipViews removeObject:chipView];
//NSLog(#"clearing finished");
}
}];
}
[CATransaction commit];
Update:
I now can get the selector to fire, however it doesn't wait for the loop of animations to finish.
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:)];
Your for loop is firing off all the animations with the same delay and same duration, so they will all start and finish at the same time.
Your completion method is going to be called once for each of your animations.
You could rewrite your code slightly to set a counter to zero before entering the loop, and increment it after each step. When the index is less than the array count, pass a nil completion block. Once the index == the array count, you're on the last item, so pass in a completion block with the code that you want to invoke once the last animation completes.
Note that you can use this index approach to make each animation start at a different time, by increasing the delay amount based on the index value:
delay = .3 + (0.5 * index);
Here's what ended up working for me. I realized that the UIView animateWithDuration methods weren't being associated to the begin/commit section. Rather, the begin/commit sections mark a region of "implicit" animation operations, so I could just set the properties within the animation and it would be implicitly animated.
For example, within the animation below, I simply set the alpha property of the chipView to "0.0" and the chipView (a UIView) is implicitly animated to disappear.
[UIView beginAnimations:#"tilescleared" context:nil];
[UIView setAnimationDelegate: self];
[UIView setAnimationDelay:0.3];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
for (HexagonalTile *aTile in tilesToClear) {
NumberedChipView *chipView = [self chipViewForColumn:aTile.column andRow:aTile.row];
chipView.alpha = 0.0;
}
[UIView commitAnimations];
Then, in my animationDidStop method, I do the "cleanup" and remove and chips from the superview. This animationDidStop method only gets fired when all chipView animations from the loop have finished, which is the tricky thing I was trying to figure out.
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
if ([animationID isEqualToString:#"tilescleared"]
&& [finished boolValue]) {
for (HexagonalTile *aTile in self.animationInfo.tilesToClear) {
NumberedChipView *chipView = [self chipViewForColumn:aTile.column andRow:aTile.row];
[chipView removeFromSuperview];
[self.chipViews removeObject:chipView];
}
}
}
I'm having a little problem. I have an UILabel which have an UILongPressGestureRecognicer. When the UILongPressGestureRecognizer is called my app is supposed to switch to a new view using a flip animation.
This is the code I have used for the GestureRecognizer:
UILongPressGestureRecognizer *labelLongPressRecognizer;
labelLongPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(LoadLabelSettings:)];
labelLongPressRecognizer.numberOfTouchesRequired = 1;
labelLongPressRecognizer.minimumPressDuration = 2.0;
[NewLabel addGestureRecognizer:labelLongPressRecognizer];
and this is the code for the view switching animation:
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:1.0];
[self.view addSubview:LabelSettingsViewController.view];
[UIView commitAnimations];
if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight || self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
LabelSettingsViewController.view.frame = CGRectMake(0, 0, 480, 300);
}
My problem is that when I hold down on my UILabel the switch animation begins, but when I release it repeats the animation again. So basically the animation occur twice and I only want it to take place once.
Any ideas?
Thanks in advance :)
Are you checking the sender state, e.g.,
- (void)LoadLabelSettings:(UILongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded) // or whatever
// then do the flipping stuff
}
Check out the "Overview" of the "UILongPressGestureRecognizer Class Reference", which talks about the long press being continuous and I presume numerous events could be triggered:
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UILongPressGestureRecognizer_Class/Reference/Reference.html