animating UIView transitions like expand dot to circle
Asked Answered
R

3

9

In my iPhone app i need to implement a different type of transition.

that was

next view open from the current view,

it stats like a dot, and the dot expands slowly like a circle with in the circle next view is to be displayed partially with in the part of the circle , finally the circle expands totally the next view appears completely.

I search lots of transitions like CATransitions, and some animations on cocoa controller, but i didn't find this type of transition, can any one help me please.

enter image description hereenter image description hereenter image description hereenter image description here

Revolution answered 2/9, 2013 at 4:56 Comment(0)
R
1

in my case I did it that way:

set a CAShapeLayer instance as the layer's mask property of your custom view subclass

@interface MyCustomView ()
@property (nonatomic, strong) CircleShapeLayer *circleShapeLayer;
@end

@implementation MyCustomView

- (id) initWithFrame: (CGRect) frame {
    self = [super initWithFrame: CGRectZero];
    if (self) {
        self.layer.mask = self.shapeLayer;
        [self.layer.mask setValue: @(0) forKeyPath: @"transform.scale"];
    }
    return self;
}

zoom this mask layer to fullsize. code of your view:

- (void) zoom {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: @"transform.scale"];
    animation.fromValue = [self.layer.mask valueForKeyPath: @"transform.scale"];
    animation.toValue = @(1);
    animation.duration = 2.0;
    animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut];
    animation.delegate = self;
    // Important: change the actual layer property before installing the animation.
    [self.layer.mask setValue: animation.toValue forKeyPath: animation.keyPath];
    // Now install the explicit animation, overriding the implicit animation.
    [self.layer.mask addAnimation: animation forKey: animation.keyPath];
    return;
}

- (CAShapeLayer *) circleShapeLayer {
    if (!_ circleShapeLayer) {
        _circleShapeLayer = [SGMaskLayer layer];
        _circleShapeLayer.delegate = _shapeLayer;
        _circleShapeLayer.frame = self.bounds;
        _circleShapeLayer.needsDisplayOnBoundsChange = YES;
    }

    return _circleShapeLayer;
}

@end

the code of the mask layer:

@interface CircleShapeLayer : CAShapeLayer
@end

@implementation CircleShapeLayer

- (void) drawLayer: (CALayer *) layer inContext: (CGContextRef) ctx {
    UIGraphicsPushContext(ctx);
    UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect: self.bounds];
    self.path = circlePath.CGPath;
    UIGraphicsPopContext();
}

@end

from the documentation:

The layer’s alpha channel determines how much of the layer’s content and background shows through. Fully or partially opaque pixels allow the underlying content to show through but fully transparent pixels block that content.

The default value of this property is nil nil. When configuring a mask, remember to set the size and position of the mask layer to ensure it is aligned properly with the layer it masks.

so I drew a circle with UIBezierPath to achieve the round mask. at the beginning I set the mask's scale factor to 0 so nothing of the view's layer is visible. then the scale factor is set to 1 (filling the layer's bounds) animated which gives a nice animation of a circle increasing it's radius from the center.

you might need one more animation shifting the center point of your view. both animations can be wrapped in a CAAnimationGroup.

Reluctivity answered 2/9, 2013 at 10:13 Comment(2)
i think it may help, but be some clarity pleaseRevolution
@hacker2007 Can you please tell me some more details about "myView" , "self.shapeLayer" and "self.layer"?Unleash
H
2

Well I think I can offer you a workaround. Instead of pushing to the next view like a dot . I suggest you add a simple dot animation in the ViewWillAppear of the view in which you have to get pushed. Now the push method would remain the same like

[self.navigationController pushViewController:NewView animated:YES];

But in the ViewWillAppear the code would be such that the dot would expand to a circle and reveal the New View beneath it. Hope you understand the logic I am trying to explain here. Any issue do let me know .

Herries answered 2/9, 2013 at 5:3 Comment(1)
is there any way to get round animationRevolution
R
1

in my case I did it that way:

set a CAShapeLayer instance as the layer's mask property of your custom view subclass

@interface MyCustomView ()
@property (nonatomic, strong) CircleShapeLayer *circleShapeLayer;
@end

@implementation MyCustomView

- (id) initWithFrame: (CGRect) frame {
    self = [super initWithFrame: CGRectZero];
    if (self) {
        self.layer.mask = self.shapeLayer;
        [self.layer.mask setValue: @(0) forKeyPath: @"transform.scale"];
    }
    return self;
}

zoom this mask layer to fullsize. code of your view:

- (void) zoom {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: @"transform.scale"];
    animation.fromValue = [self.layer.mask valueForKeyPath: @"transform.scale"];
    animation.toValue = @(1);
    animation.duration = 2.0;
    animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut];
    animation.delegate = self;
    // Important: change the actual layer property before installing the animation.
    [self.layer.mask setValue: animation.toValue forKeyPath: animation.keyPath];
    // Now install the explicit animation, overriding the implicit animation.
    [self.layer.mask addAnimation: animation forKey: animation.keyPath];
    return;
}

- (CAShapeLayer *) circleShapeLayer {
    if (!_ circleShapeLayer) {
        _circleShapeLayer = [SGMaskLayer layer];
        _circleShapeLayer.delegate = _shapeLayer;
        _circleShapeLayer.frame = self.bounds;
        _circleShapeLayer.needsDisplayOnBoundsChange = YES;
    }

    return _circleShapeLayer;
}

@end

the code of the mask layer:

@interface CircleShapeLayer : CAShapeLayer
@end

@implementation CircleShapeLayer

- (void) drawLayer: (CALayer *) layer inContext: (CGContextRef) ctx {
    UIGraphicsPushContext(ctx);
    UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect: self.bounds];
    self.path = circlePath.CGPath;
    UIGraphicsPopContext();
}

@end

from the documentation:

The layer’s alpha channel determines how much of the layer’s content and background shows through. Fully or partially opaque pixels allow the underlying content to show through but fully transparent pixels block that content.

The default value of this property is nil nil. When configuring a mask, remember to set the size and position of the mask layer to ensure it is aligned properly with the layer it masks.

so I drew a circle with UIBezierPath to achieve the round mask. at the beginning I set the mask's scale factor to 0 so nothing of the view's layer is visible. then the scale factor is set to 1 (filling the layer's bounds) animated which gives a nice animation of a circle increasing it's radius from the center.

you might need one more animation shifting the center point of your view. both animations can be wrapped in a CAAnimationGroup.

Reluctivity answered 2/9, 2013 at 10:13 Comment(2)
i think it may help, but be some clarity pleaseRevolution
@hacker2007 Can you please tell me some more details about "myView" , "self.shapeLayer" and "self.layer"?Unleash
R
1

Open in first view:

//Delegate method for annotation did select - (void)tapOnAnnotation:(RMAnnotation *)annotation onMap:(RMMapView *)map; { //To get the touch point of the GMS marker to set them as circle transiton pos CGPoint markerPoint = annotation.position; x = markerPoint.x; y = markerPoint.y;

    circleSize = 10;
    radiusChange = 0;

    //Populate Same Values to next view to close
    VenueScreen.x = x;
    VenueScreen.y = y;

    VenueScreen.view.hidden = YES;
    timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(openVenueScreen) userInfo:nil repeats:YES];
    VenueScreen.view.frame = CGRectMake(0, 0, 320, 480);
    [self.view addSubview:VenueScreen.view];

}



//Circular transition to open Next view
-(void)openVenueScreen
{
    VenueScreen.view.hidden = NO;
    VenueScreen.view.userInteractionEnabled = NO;
    VenueScreen.view.alpha = 0.9;
    self.view.userInteractionEnabled = NO;

    int rChange = 0; // Its doing proper masking while changing this value
    int radius = circleSize-rChange;

    CAShapeLayer *circleShapeLayer = [CAShapeLayer layer];

    // Make a circular shape
    circleShapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(x+radiusChange, y+radiusChange, 2.0*radius, 2.0*radius)
                                                       cornerRadius:radius].CGPath;
    radiusChange = radiusChange-10;

    // Configure the apperence of the circle
    [[VenueScreen.view layer] setMask:circleShapeLayer];

    //Start Transition
    circleSize = circleSize+10;

    if (circleSize > 480)
    {
        [[VenueScreen.view layer] setMask:nil];

        [timer invalidate]; //Stop titmer

        VenueScreen.view.userInteractionEnabled = YES;
        self.view.userInteractionEnabled = YES;
        VenueScreen.view.alpha = 1;

        //Populate to next view to close
        VenueScreen.radiusChange = radiusChange;
    }

}

Close in Next View:

//Close button Action
-(IBAction)DismissVenueScreen:(id)sender;
{
    timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(dismissVenueScreen) userInfo:nil repeats:YES];
    NSLog(@"close button clciked");
}

//Circular trasition to Close window
-(void)dismissVenueScreen
{
    int rChange = 0; // Its doing proper masking while changing this value
    int radius = circleSize-rChange;

    CAShapeLayer *circleShapeLayer = [CAShapeLayer layer];
    // Make a circular shape
    circleShapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(x+radiusChange,y+radiusChange, 2.0*radius, 2.0*radius)
                                                       cornerRadius:radius].CGPath;

    // Configure the apperence of the circle
    [[self.view layer] setMask:circleShapeLayer];
    self.view.layer.masksToBounds = YES;

    radiusChange = radiusChange+10;
    circleSize = circleSize-10;

    if (circleSize <= 0)
    {
        [timer invalidate]; //Stop titmer
        [[self.view layer] setMask:nil];
        [self.view removeFromSuperview];
        circleSize = 480;
    }

}
Revolution answered 5/9, 2013 at 9:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.