I've been struggling with this for a long and I just can't find out what's wrong here. I actually do have an idea - I think that somehow the view I am adding and trying to animate is not yet included to the view hierarchy and that's why core animation isn't wasting cycles to animate. I did some other animations in the project successfully but I am running them in the ViewController's viewDidLoad method. Here is my code:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.opaque = NO;
self.backgroundColor = [UIColor colorWithWhite: 1.0 alpha: 0.7];
[self addTrack];
}
return self;
}
- (void)addTrack
{
//init the track here
UIView *track = [[UIView alloc] initWithFrame: CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
track.backgroundColor = [UIColor whiteColor];
[self addSubview: track];
//transform the track for its initial scale
float scaleFactorX = 0.01;
track.layer.anchorPoint = CGPointMake(0, 0);
track.layer.position = CGPointMake(0, 0);
track.transform = CGAffineTransformMakeScale(scaleFactorX, 1.0);
//animate the track here
[UIView animateWithDuration:8 delay:0.3 options:UIViewAnimationOptionCurveEaseOut
animations:^{
NSLog(#"animation is running.");
track.transform = CGAffineTransformIdentity;
}
completion:^(BOOL finished) {
NSLog(#"animation finished.");
}];
}
So the result is that the "track" is scaling to its original size immediately. I also tried to call addTrack inside the didMoveToSuperview method so I can make sure that the containing view is added to the view hierarchy but with the same result. I tried other animations inside that class like adding alpha animation to the containing view but with no success again ..
Try calling your animations after loading the view so that you can see it.
-(void)viewDidAppear:(BOOL)animated
{
[self addTrack];
}
You shouldn't have put animations in the init method to start with. Try calling them immediately after your view was actually displayed (added on a superview) and see what happens then.
Related
I have 2 animations with one calling the other, but only the later is actually happening. I am not sure with the issue is. I am new to iOS. I am trying to animate it up then down but only the down is happening.
Also, if I change the call from [self animateUp] to [self animateDown], no animation happens.
-(void)viewDidLoad
{
[super viewDidLoad];
[self.containerView addSubview:self.logoView = ({
UIImageView *imageView = [UIImageView new];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = [UIImage imageNamed:#"thisImage"];
imageView;
})];
[self animateUp];
}
- (void)animateUp
{
[UIView animateWithDuration:1 animations:^{
CGRect newFrame = CGRectMake(self.logoView.frame.origin.x, self.logoView.frame.origin.y - 40, self.logoView.frame.size.width, self.logoView.frame.size.height);
self.logoView.frame = newFrame;
} completion:^(BOOL finished) {
[self animateDown];
}];
}
- (void)animateDown
{
[UIView animateWithDuration:1 animations:^{
CGRect newFrame = CGRectMake(self.logoView.frame.origin.x, self.logoView.frame.origin.y + 40, self.logoView.frame.size.width, self.logoView.frame.size.height);
self.logoView.frame = newFrame;
} completion:nil ];
}
The call to animateUp is occurring before the ImageView has an initial frame set. You can defer this to viewDidAppear or viewDidLayoutSubviews and should see the animations occur correctly.
-(void)viewDidLoad
{
[super viewDidLoad];
[self.containerView addSubview:self.logoView = ({
UIImageView *imageView = [UIImageView new];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = [UIImage imageNamed:#"thisImage"];
imageView;
})];
}
- (void) viewDidLayoutSubviews
{
[self animateUp]; // at this point the ImageView has its frame set
}
Please be aware that viewDidAppear and viewDidLayoutSubviews could get called multiple times in the lifetime of your UIViewController, so you'll want to accomodate for this should you only want the animation to occur once.
My problem is...
I have the object of parent view controller and i wanted to animate second view controller over that.
So i added a subview called backgroundview over the view of parentvc.view and then the view which needs to be drawn over the backgroundView.
But after animation completes for a second, i can see the views the way i want them to be but then it is replaced by a complete black screen.
I think my topmost view is redrawn so how do i rectify this issue.
Code :-
- (void)viewDidLoad {
[super viewDidLoad];
//_colorCaptureOptions = [NSArray arrayWithObjects: #"Camera", #"Color Slider / Color Wheel", nil];
mExtractionQueue = [[NSOperationQueue alloc] init];
[mExtractionQueue setMaxConcurrentOperationCount:1];
CGRect screenRect = [[UIScreen mainScreen] bounds];
//CGRect screenRect = self.view.frame;
self.backgroundView = [[UIView alloc] initWithFrame:screenRect];
self.backgroundView.backgroundColor = [UIColor clearColor];
[self.parentVC addChildViewController:self];
[self.parentVC.view addSubview:self.backgroundView];
//[self didMoveToParentViewController:self.parentVC];
UITapGestureRecognizer *guideTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleScrimSelected)];
[self.backgroundView addGestureRecognizer:guideTap];
[self.backgroundView addSubview:self.view];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat viewHeight = 150;
self.backgroundView.alpha = 0.0;
self.view.alpha = 1;
self.view.frame = CGRectMake(0, screenRect.size.height, screenRect.size.width, viewHeight);
[UIView animateWithDuration:0.2
delay:0
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
self.backgroundView.alpha = 1.0;
self.view.frame = CGRectMake(0, screenRect.size.height - viewHeight, screenRect.size.width, viewHeight);
}
completion:^(BOOL _finished) {
}];
}
}
Well, for clarity you'd better to provide few screenshots.
For all the rest:
Possibly, your background view is simply black that leads to black screen. Or it even is [UIColor clearColor]
better not use childViewController, it breaks MVC
better not change frame inside animation directly
If you want present another controller with animation, use this UIViewControllerTransitioningDelegate and this UIViewControllerAnimatedTransitioning in your objects, so do not reinvent transitions. Refer to this custom controller transition tutorial
Hope this may help you
EDIT:
[UIColor clearColor] removes colour entirely, so that means you will have no color at all.
The best solution for you now is rewrite those controllers, split up one from another, get rid of container for viewControllers, change animation to custom and than problem will disappear as a magic. If you solve you problem, do not forget to mark question as resolved, please
Im looking for a way to copy the "blink" animation, that is played when pressing home+lock.
Does anyone know if this animation is available somehow?
On an iOS device you take a screenshot when you press home + lock and the screen flashes white. Do you mean this effect? If so, try this:
Add a UIView with a white background color to your view hierarchy such that it covers the whole screen. Then, start an animation that fades the opacity of this view to zero. On completion, remove the view from its superview:
[UIView animateWithDuration: 0.5
animations: ^{
whiteView.alpha = 0.0;
}
completion: ^(BOOL finished) {
[whiteView removeFromSuperview];
}
];
Try:
[UIView animateWithDuration:1 animations:^{
self.view.backgroundColor = [UIColor blackColor];
for (UIView *view2 in self.view.subviews) {
view2.backgroundColor = [UIColor blackColor];
}
}];
[UIView animateWithDuration:1 animations:^{
self.view.backgroundColor = [UIColor whiteColor];
for (UIView *view2 in self.view.subviews) {
view2.backgroundColor = [UIColor whiteColor];
}
}];
I have found very easy way to imitate exactly the Screenshot Flash Apple uses. I wish everyone try it at least once.
UIView * flashView = [[UIView alloc] initWithFrame:self.view.frame];
flashView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:flashView];
[UIView animateWithDuration:1 delay:0.3 options:0 animations:^{
flashView.alpha = 0;
} completion:^(BOOL finished)
{
[flashView removeFromSuperview];
}];
}
These examples gave me inspiration, but did not give me the desired effect. This is my attempt, looks pretty close to the real deal.
func mimicScreenShotFlash() {
let aView = UIView(frame: self.view.frame)
aView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(aView)
UIView.animateWithDuration(1.3, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
aView.alpha = 0.0
}, completion: { (done) -> Void in
aView.removeFromSuperview()
})
}
Two problems with the current solutions:
putting views over other views can (in my experience with UICollectionView and other crap) trigger autolayout. Autolayout is bad. Autolayout makes the machine do a burst of work and can have side effects other than just calculating for no reason other than to burn CPU and battery. So, you don't want to give it reasons to autolayout, for instance, adding a subview to a view that really, really cares about keeping itself laid out nicely.
your view doesn't cover the whole screen necessarily, so if you want to flash the whole screen, you're better off using a UIWindow ... this should insulate from any view controllers that are touchy about having subviews added
This is my implementation, a category on UIView. I've included methods to take a screenshot before flashing the window, and saving it to the camera roll afterwards. Note that UIWindow seems to want to do its own animations when being added, it will normally fade in over maybe a third of a second. There may be a better way of telling it not to do this.
// stupid blocks
typedef void (^QCompletion)(BOOL complete);
#interface UIView (QViewAnimation)
+ (UIImage *)screenshot; // whole screen
+ (void)flashScreen:(QCompletion)complete;
- (UIImage *)snapshot; // this view only
- (void)takeScreenshotAndFlashScreen;
#end
#implementation UIView (QViewAnimation)
+ (UIImage *)screenshot;
{
NSArray *windows = [[UIApplication sharedApplication] windows];
UIWindow *window = nil;
if (windows.count) {
window = windows[0];
return [window snapshot];
} else {
NSLog(#"Screenshot failed.");
return nil;
}
}
- (UIImage *)snapshot;
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0);
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
+ (void)flashScreen:(QCompletion)complete;
{
UIScreen *screen = [UIScreen mainScreen];
CGRect bounds = screen.bounds;
UIWindow * flash = [[UIWindow alloc] initWithFrame:bounds];
flash.alpha = 1;
flash.backgroundColor = [UIColor whiteColor];
[UIView setAnimationsEnabled:NO];
[flash makeKeyAndVisible];
[UIView setAnimationsEnabled:YES];
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationCurveEaseOut
animations:^{
flash.alpha = 0;
}
completion: ^(BOOL finished)
{
flash.hidden = YES;
[flash autorelease];
if (complete) {
complete(YES);
}
}];
}
- (void)takeScreenshotAndFlashScreen;
{
UIImage *image = [UIView screenshot];
[UIView flashScreen:^(BOOL complete){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0),
^{
UIImageWriteToSavedPhotosAlbum(image,self,#selector(imageDidFinishSaving:withError:context:),nil);
});
}];
}
- (void)imageDidFinishSaving:(UIImage *)image
withError:(NSError *)error
context:(void *)context;
{
dispatch_async(dispatch_get_main_queue(),^{
// send an alert that the image saved
});
}
#end
I have a UIView which is located offscreen and I'm animating the frame so that the view slides in offscreen from the bottom and is visible. I'd like to simultaneously animate the alpha property of a UILabel on the view as well so it fades in. Unfortunately it appears I can't do the alpha animation because the view is offscreen and doesn't appear to take hold. It looks something like this:
nextCell.titleLabel.alpha = 0;
[UIView animateWithDuration:collapsedAnimationDuration animations:^{
CGRect newFrame = lastCell.frame;
newFrame.origin = CGPointMake(lastCell.frame.origin.x , lastCell.frame.origin.y + THREAD_CELL_HEIGHT);
nextCell.frame = newFrame;
nextCell.titleLabel.alpha = 1;
}];
Is it not possible to start animating the alpha of the subview because it's offscreen? If I position the view on screen and then try the animation it looks great but that's not the effect I'm going for. Thanks for your help.
Is this code executed in cellForRowAtIndexPath? If so, try moving it to tableView:willDisplayCell:forRowAtIndexPath:. The table view resets various properties of the cell before displaying it.
From the AppDelegate didFinishLaunching method:
self.myView = [[MyView alloc] initWithFrame:CGRectMake(320, 480, 400, 400)];
self.myView.titleLabel.text = #"test text";
self.myView.titleLabel.alpha = 0;
[UIView animateWithDuration:10.0 animations:^{
CGRect newFrame = self.myView.frame;
newFrame.origin = CGPointMake(0 , 0);
self.myView.frame = newFrame;
self.myView.titleLabel.alpha = 1;
}];
[self.viewController.view addSubview:self.myView];
MyView is just this:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self addSubview:self.titleLabel];
}
return self;
}
- (UILabel *)titleLabel
{
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
}
return _titleLabel;
}
I did no important changes to the code you presented and it worked fine. So assuming you're not doing what Tim mentioned (it won't work if you're doing it), we need more details to help you out.
I'm trying to have the iPad splash screen fade out to reveal my app's main interface over 2 secs. The main interface is my main view controller and view, inside a navigation controller.
So I did the following:
UINavigationController with my root view controller.
root view controller has its interface all laid out and, as a last step, a CALayer with the same png used for the splash screen covering the interface.
The idea is that once the real splash screen is gone, there's still the CALayer. And then I fade the CAlayer out to reveal the interface. It kind of works: the fade happens, but no matter what duration I set for the animation, it still happens too fast.
Here's the code: (logoLayer is an ivar and mainCanvas is a container view inside self.view, in which I insert most subviews -- for other screen-dim-type effects.)
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
self.view.layer.backgroundColor = [UIColor clearColor].CGColor;
[self setLayerContent:[[NSBundle mainBundle] pathForResource:#"interfaceBackground" ofType:#"png"]];
[self layoutNavigationButtonWithResource:#"button1" glowing:YES forAction:#"biblioteca"];
[self layoutNavigationButtonWithResource:#"button2"glowing:YES forAction:#"portfolio"];
NSString *logoPath = [[NSBundle mainBundle] pathForResource:#"Default-Portrait~ipad" ofType:#"png"];
UIImage *image = [UIImage imageWithContentsOfFile:logoPath];
logoLayer = [[CALayer layer] retain];
logoLayer.contents = (id)image.CGImage;
logoLayer.bounds = CGRectMake(0, 0, image.size.width, image.size.height);
logoLayer.anchorPoint = CGPointMake(0, 0);
logoLayer.opacity = 1;
[mainCanvas.layer addSublayer:logoLayer];
}
return self;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:#selector(fadeOutLogo) withObject:nil afterDelay:0.1];
}
- (void)fadeOutLogo
{
CABasicAnimation *a = [CABasicAnimation animation];
a.duration = 10;
[logoLayer addAnimation:a forKey:#"opacity"];
[logoLayer setOpacity:0];
}
Notice I even delayed the call to the animation code just in case. And I also ended up with a value of 10sec. which is absurd for a fade. Still... the fade happens in about 0.2 secs.
Any thoughts?
That CABasicAnimation isn’t actually doing anything, because you’re not giving it a fromValue or toValue; when you’re setting the layer’s opacity, it’s only animating because setting properties on Core Animation layers triggers an implicit animation, whose default duration is about a quarter of a second. What you want to do is this:
- (void)fadeOutLogo
{
[CATransaction begin];
[CATransaction setAnimationDuration:2];
[logoLayer setOpacity:0];
[CATransaction commit];
}
Alternatively, in viewWillAppear: you could instantiate a UIImageView with Default-Portrait~ipad.png, add it to self.view, use the UIView animateWithDuration:animations:completion: class method to animate the value of alpha to 0 over a period of 2 seconds, and then remove and release the UIImageView in the completion block. The following code demonstrates the idea, using a UIView instead of a UIImageView:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear: animated];
UIView __block *fadeView = [[UIView alloc]
initWithFrame: CGRectMake(0, 0, 320, 460)];
fadeView.backgroundColor = [UIColor blackColor];
[self.view addSubview: fadeView];
[UIView animateWithDuration: 2 animations:^{
fadeView.alpha = 0;
} completion:^(BOOL finished) {
fadeView = nil;
}];
}