7

I have 2 images. One is in the back of the other. I want to fade-out the top image starting from the left corner. How can I do this?

Should I use gradient mask?

Burak
  • 5,706
  • 20
  • 70
  • 110

2 Answers2

11

Here's a technique that will fade out whatever's in a CALayer, from top-left to bottom-right, revealing whatever is underneath the CALayer.

Let's say we're going to fade out the layer of a UIImageView called self.fadeView.

We'll create a CAGradientLayer and use it as self.fadeView.layer.mask. The gradient will go from alpha=0 (transparent) to alpha=1 (opaque) using four stops: 0, 0, 1, 1. Yes, two zeros and then two ones. When we want fadeView to be opaque, we'll set the stop locations to -1, -.5, 0, 1. That way the two alpha=0 stops are completely outside of the layer bounds. When we want fadeView to be transparent, we'll set the stop locations to 0, 1, 1.5, 2. That way the two alpha=1 stops are completely outside of the layer bounds. The CAGradientLayer will automatically animate changes to its stop locations, creating a cross-fade effect.

Here's the code:

#import "ViewController.h"

@implementation ViewController

@synthesize fadeView = _fadeView;

static NSArray *locations(float a, float b, float c, float d)
{
    return [NSArray arrayWithObjects:
        [NSNumber numberWithFloat:a],
        [NSNumber numberWithFloat:b],
        [NSNumber numberWithFloat:c],
        [NSNumber numberWithFloat:d],
        nil];
}

// In my test project, I put a swipe gesture recognizer on fadeView in my XIB
// with direction = Up and connected it to this action.
- (IBAction)fadeIn
{
    [CATransaction begin];
    [CATransaction setValue:[NSNumber numberWithDouble:2.0] forKey:kCATransactionAnimationDuration];
    ((CAGradientLayer *)self.fadeView.layer.mask).locations = locations(-1, -.5, 0, 1);
    [CATransaction commit];
}

// In my test project, I put a swipe gesture recognizer on fadeView in my XIB
// with direction = Down and connected it to this action.
- (IBAction)fadeOut
{
    [CATransaction begin];
    [CATransaction setValue:[NSNumber numberWithDouble:2.0] forKey:kCATransactionAnimationDuration];
    ((CAGradientLayer *)self.fadeView.layer.mask).locations = locations(0, 1, 1.5, 2);
    [CATransaction commit];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    CAGradientLayer *mask = [CAGradientLayer layer];
    mask.frame = self.fadeView.bounds;
    mask.colors = [NSArray arrayWithObjects:
        (__bridge id)[UIColor clearColor].CGColor,
        (__bridge id)[UIColor clearColor].CGColor,
        (__bridge id)[UIColor whiteColor].CGColor,
        (__bridge id)[UIColor whiteColor].CGColor,
        nil];
    mask.startPoint = CGPointZero; // top left corner
    mask.endPoint = CGPointMake(1, 1); // bottom right corner
    self.fadeView.layer.mask = mask;
    [self fadeIn]; // initialize mask.locations
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

@end
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Thanks, but I couldn't make the code work. Do you have a sample project? – Burak Dec 01 '11 at 21:27
  • Sir, I also want to relate the animation with roll or pitch motion of iPad. I am getting the angle from the sensors. So, when I roll the iPad, cross fade should happen like in http://vimeo.com/32931599. Any idea for this? – Burak Dec 02 '11 at 12:48
  • 1
    You can change the direction of the fade by changing `mask.startPoint` and `mask.endPoint`. – rob mayoff Dec 02 '11 at 18:42
  • Yes but for example, if I roll the iPad 1 degree, gradient should be changed for 1 degree. Can I do this? – Burak Dec 03 '11 at 10:09
  • Thanks @robmayoff you are just awesome. Great work ,Work perfectly. – Gurinder Batth Sep 13 '17 at 07:16
2

Define your own CALayer subclass with its own actionForKey animation for the key contents.

@interface CustomLayer: CALayer

@end

@implementation CustomLayer


+ (id<CAAction>)actionForKey:(NSString*)event {

if ([event isEqualToString:@"contents"]) {
    CATransition* basicAnimation = [CATransition animation];
    basicAnimation.type = kCATransitionMoveIn;
    basicAnimation.subtype = kCATransitionFromLeft;
    return basicAnimation;
} else {
    return nil;
}
}

@end

Doing like this, whenever you set the contents property of your CustomLayer object, the transition will be shown:

CustomLayer* layer = [CustomLayer layer];
layer.contents = <FIRST_CGIMAGEREF_HERE>;
...
layer.contents = <SECOND_CGIMAGEREF_HERE>;

As usual, you can wrap the property assignment in a transaction:

[CATransaction begin];
    ....
[CATransaction end];

if you want more control on the duration of the transition, but in any case the transition will be applied.

EDIT:

For the kind of transition you would like to have, you can have a look at this sample on GitHub. Look for the ShutterTransition. It is not exactly what you are looking for, but it could lead you along the right path.

sergio
  • 68,819
  • 11
  • 102
  • 123
  • I think your suggestion is for transition. What I need is something like http://vimeo.com/32931599 – Burak Dec 01 '11 at 19:20