I am trying to get a screenshot of a UIView which is rotated. This is my code
-(void)rotateImage:(int)radian
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationBeginsFromCurrentState:YES];
// The transform matrix
CGAffineTransform transform = CGAffineTransformRotate(self.baseImageView.transform, M_PI/2 * radian);
self.baseImageView.transform = transform;
}
-(NSData *)getData
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO,0.0);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return UIImageJPEGRepresentation(img, 1.0);
}
Screenshot is created successfully, but after that all the viewcontrollers i present is presented from top left and views bounce & misbehave. However if i create screen shot without rotating the image everything is fine. I am stuck with it any help is appreciated.
Note : i already tried modifying UIGraphicsBeginImageContextWithOptions parameters . My project does not use ARC.
It turns out that the animation was not handled properly.I ended up doing this to solve the problem.
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationCurveEaseIn animations:^{
CGAffineTransform transform = CGAffineTransformRotate(self.baseImageView.transform, M_PI/2 * radian);
self.baseImageView.transform = transform;
} completion:nil];
Related
I am using this code to animate view controller with scale transition-
if (self.isPresenting) {
[containerView addSubview:toVC.view];
[toVC.view setAlpha:0];
CGAffineTransform xForm = toVC.view.transform;
toVC.view.transform = CGAffineTransformScale(xForm, 1.0f, 0.8f);
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
[toVC.view setAlpha:1];
toVC.view.transform =
CGAffineTransformScale(xForm, 1.0f, 1.0f);
fromVC.view.transform =
CGAffineTransformScale(fromVC.view.transform, 1.0f, 1.0f);
}
completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
else {
[containerView addSubview:toVC.view];
[containerView addSubview:fromVC.view];
CGAffineTransform xForm = toVC.view.transform;
toVC.view.transform = CGAffineTransformScale(toVC.view.transform, 1.0f, 1.0f);
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
[fromVC.view setAlpha:0];
fromVC.view.transform =
CGAffineTransformScale(xForm, 0.7f, 0.7f);
toVC.view.transform =
CGAffineTransformScale(CGAffineTransformIdentity, 1.0f, 1.0f);
}
completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
Its working perfect when present and dismiss view (on back button click) But i need to do is when user drag view from top to bottom then also it should dismiss the view controller.
any help how can i add this ?
I have a problem with CATransform3D.
When I try to do a Y axis rotation, the UIView breaks into two parts before animation starts.
An image is worth a thousand words...
Here's the code:
CATransform3D _3Dt = CATransform3DMakeRotation(2.0 * M_PI, 0.0, 1.0, 0.0);
[UIView animateWithDuration:2.0
animations:^{
view.layer.transform = _3Dt;
}];
Any idea?
Thanks!
Solved!
The problem was the layer.zPosition
Here's is the final code:
view.layer.zPosition = CGRectGetWidth(view.bounds);
CATransform3D _3Dt = CATransform3DMakeRotation(2.0 * M_PI, 0.0, 1.0, 0.0);
[UIView animateWithDuration:2.0
animations:^{
view.layer.transform = _3Dt;
}];
Thanks!
float scaleFactor = 0.5;
self.bubble.transform = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
[self.bubble setNeedsDisplay];
[UIView animateWithDuration:2.0 animations:^{
self.frame = _rectToMoveTo;
} completion:^(BOOL finished) {
[UIView animateWithDuration:2.0f animations:^{
self.bubble.transform = CGAffineTransformMakeScale(1.0, 1.0);
}];
}];
The above code performs the animation mostly correct. As you can see, the self.bubbles are scaled down to ½ then animated back to normal. The problem is that self.bubble has a drawRect method for drawing a circle. The problem is that this circle gets scaled down to ¼ from the start! When the second animation runs, the subviews of self.bubble get scaled to normal, but the circle scales up to only ½. I've tried using setNeedsDisplay to redraw the circle but it will only work after the animations have completed so it's no good. how do you fix this?
Edit: here is _bubble drawRect method.
CGContextRef c = UIGraphicsGetCurrentContext();
if (!_colour) _colour = [UIColor darkGrayColor];
[_colour setFill];
[[UIColor clearColor] setStroke];
CGContextAddEllipseInRect(c, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height));
CGContextFillPath(c);
[self addSubview:_title];
[self addSubview:_icon];
self.frame is in the superview's coordinate system, and is thus affected by your transform, which you don't want.
Inside drawRect:, you should only use self.bounds, not self.frame.
Also, you should not add subviews in drawRect:.
I asked a similar question recently on how to add a translucent effect to a UIView and got a good response.
However it used a lot of CPU power to process so I used some of the ideas behind the answer but did the filtering using GPUImage which is much more efficient.
It works well for a static screen, but I want to change the background UIImageView's image with an animation. However when I set the UIView to sample the background during the transition, it seems to ignore the transition and show the new Image before the animation has started.
The relevant code is as follows (ask if you need more!):
The superview containing the custom UIView and the UIImageView background:
//The Transition
[contentView setScheduled:YES]; //Starts the sampling every 0.2 seconds
//bg is the UIImageView and contentView is the 'translucent' UIView subclass
[UIView transitionWithView:bg duration:1.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[bg setImage:newImage];
}completion:^(BOOL finished){
[contentView setScheduled:NO];
}];
Then in the UIView subclass:
- (UIImage *)snapshotOfSuperview:(UIView *)superview
{
CGFloat scale = 0.5;
if (([UIScreen mainScreen].scale > 1 || self.contentMode == UIViewContentModeScaleAspectFill))
{
CGFloat blockSize = 12.0f/5;
scale = blockSize/MAX(blockSize * 2, floor(self.blurRadius));
}
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, -self.frame.origin.x, -self.frame.origin.y);
self.hidden=YES; //Don't take a snapshot of the view
[superview.layer renderInContext:context];
self.hidden=NO;
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshot;
}
-(void)updateViewBG{
UIImage *superviewImage = [self snapshotOfSuperview:self.superview];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
GPUImageGaussianBlurFilter* filter = [[GPUImageGaussianBlurFilter alloc] init];
filter.blurSize = 0.8f;
UIImage* newBG = [self applyTint:self.tintColour image:[filter imageByFilteringImage:superviewImage]];
dispatch_async(dispatch_get_main_queue(), ^{
self.layer.contents = (id)newBG.CGImage;
self.layer.contentsScale = newBG.scale;
});
});
}
-(UIImage*)applyTint:(UIColor*)colour image:(UIImage*)inImage{
UIImage *newImage;
if (colour) {
UIGraphicsBeginImageContext(inImage.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect area = CGRectMake(0, 0, inImage.size.width, inImage.size.height);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -area.size.height);
CGContextSaveGState(ctx);
CGContextClipToMask(ctx, area, inImage.CGImage);
[colour set];
CGContextFillRect(ctx, area);
CGContextRestoreGState(ctx);
CGContextSetBlendMode(ctx, kCGBlendModeLighten);
CGContextDrawImage(ctx, area, inImage.CGImage);
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}else{
newImage = inImage;
}
return newImage;
}
-(void)displayLayer:(CALayer *)layer{
[self updateViewBG];
}
How can I get the background to follow the animation?
I think the problem is that as soon as you change the image, displayLayer is called (disregarding the transition being in progress) and thus the new image is used for the translucency.
You should modify updateViewBG so that it does not update the layer content before the transition is finished. E.g., you could add a flag to your class, set it when you start the transition and reset it when it completes. When the flag is set you do not call updateViewBG.
[UIView transitionWithView:bg duration:1.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
self.isTransitionInProgress = YES;
[bg setImage:newImage];
}completion:^(BOOL finished){
[contentView setScheduled:NO];
self.isTransitionInProgress = NO;
[contentView.layer setNeedsDisplay];
}];
-(void)displayLayer:(CALayer *)layer{
if (!self.isTransitionInProgress)
[self updateViewBG];
}
I was wondering if it's possible to do some sort of sway animation in iOS. For example, a sway animation would be like a store sign blowing in the wind. think westerns. I would like to do this kind of animation when a button is pressed. I'm going for a western kind of feel and that would really put it over the top. Thanks in advance if you know how to do something like this.
In order to do this you have to do a couple of things beforehand such as setting the layer anchor point to (0.5,0.0). like the following:
swaybtn.layer.anchorPoint = CGPointMake(0.5, 0.0);
And then make a function that gets called when the button is tapped which starts the backwards sway animation, another which sways forward, and a last one that moves back to the original position. The degrees you specify is how far the sway will go and the second number in the rotationAndPerspectiveTransform is the direction the sway will go.
The functions are as follow:
-(void)sway:(id)sender{
CGFloat degrees = 50.0;
CALayer *layer;
layer = swaybtn.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(moveForward)];
rotationAndPerspectiveTransform.m34 = 1.0 / -400;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, degrees * M_PI / 180.0f, -1.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
[UIView commitAnimations];
}
-(void)moveForward{
CALayer *layer;
layer = swaybtn.layer;
CGFloat degrees = 50.0;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
[UIView beginAnimations:#"swayforward" context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(moveBack)];
rotationAndPerspectiveTransform.m34 = 1.0 / -400;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, degrees * M_PI / 180.0f, 1.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
[UIView commitAnimations];
}
-(void)moveBack{
CALayer *layer;
layer = swaybtn.layer;
CGFloat degrees = 50.0;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
[UIView beginAnimations:#"moveback" context:NULL];
[UIView setAnimationDuration:0.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
rotationAndPerspectiveTransform.m34 = 1.0 / -400;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, degrees * M_PI / 180.0f, 0.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
[UIView commitAnimations];
}
And there you go. You should have an animation that sways an object back and forth just like a hanging sign.