2

I want to create an animation which "create a copy of a view, move this copy from a point to another point while decreasing opacity until it totally disappear".

I suppose i need to do it through a CABasicAnimation so i tried something like that :

    CGPoint point = CGPointMake(200, 0);

    // copy layer
    CALayer *layer = [[CALayer alloc] initWithLayer:self.animatedView.layer];
    [self.animatedView.layer addSublayer:layer];

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    animation.fromValue = [layer valueForKey:@"position"];
    animation.toValue = [NSValue valueWithCGPoint:point];
    animation.duration = 2.0f;

    CABasicAnimation *oanimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    oanimation.fromValue = @1;
    oanimation.toValue = @0;
    oanimation.duration = 1.0f;

    // move copy layer
    [layer addAnimation:animation forKey:@"position"];
    [layer addAnimation:oanimation forKey:@"opacity"];

But nothings append and i think it's a normal situation regarding to documentation :

This initializer is used to create shadow copies of layers, for example, for the presentationLayer method. Using this method in any other situation will produce undefined behavior. For example, do not use this method to initialize a new layer with an existing layer’s content.

Someone already did this kind of animation before ?

Thx for your help.

Yaman
  • 3,949
  • 4
  • 38
  • 60
  • Why a copy? And why a layer? Why not just move and fade the original view? – Wain Jun 26 '13 at 18:10
  • Because this is not what i want to do. I'm copying a view from left controller to a right controller (splitted screen) so I want an effect that fake a movement from left to right while remaining on left controller – Yaman Jun 26 '13 at 19:52
  • 1
    Ok, so you want to copy, but you can still do that at view level. `UIView` conforms to `NSCoding` so you can copy it. – Wain Jun 26 '13 at 19:56
  • I said an UIView to generalize but it's actually an UITableViewCell. Could it work with it also ? – Yaman Jun 26 '13 at 19:59
  • 1
    Sure, a standard table view will work easily. A custom subclass will take a little more effort. The main limitation of view copying is the targets and actions of controls will be lost and custom view classes probably won't respect the coding contracts. – Wain Jun 26 '13 at 20:04
  • I don't need targets and actions since it's an temporary copy, just to perform my animation. I'm doing the "real" copy in another way so it's good. I will try this, thx a lot ! – Yaman Jun 26 '13 at 20:09

2 Answers2

1
UIGraphicsBeginImageContext(theView.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[theView.layer renderInContext:context];
UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

UIImageView *imgView = [[UIImageView alloc] initWithImage:screenShot];
imgView.frame = theView.frame;
[self.view addSubview:imgView];

then play with 'imgView' :)

Sriram
  • 11
  • 2
  • I find the answer to have some merit. I'd also add that If you want the rendered image to be Retina (@2x, @3x), you can replace the first line by this: `UIGraphicsBeginImageContextWithOptions(theView.frame.size, false, UIScreen.main.scale)` – Clément Cardonnel Feb 17 '19 at 07:58
0

Solution 1: CALayers

I adapted @Sriram solution in Swift 4.2 and preferred to use a more adapted CALayer instead of an UIImageView.

// We use the screen's scale to support Retina displays
UIGraphicsBeginImageContextWithOptions(viewToCopy.frame.size, false, UIScreen.main.scale)
if let context: CGContext = UIGraphicsGetCurrentContext() {
    // We convert the layer of the viewToCopy into an UIImage
    viewToCopy.layer.render(in: context)
    let screenshot = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    
    // We create a layer that will hold the image. This layer can be animated further down the road.
    let layer = CALayer()
    layer.contents = screenshot?.cgImage // Don't forget .cgImage, otherwise it won't work
    layer.frame = viewToCopy.frame // For example
    view.layer.addSublayer(layer)
}

I guess this one works fine for most cases, but I’ve encountered some issues while doing this with a UIVisualEffectView (the effect broke). So I had to figure out a second solution.

Solution 2: UIView Copy

First, you’re going to need a small extension to UIView to be able to copy it easily. I used this answer but I’ll copy it here for clarity.

// MARK: - UIView + Copy

extension UIView {
    func copyView<T: UIView>() -> T {
        return NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self)) as! T
    }
}

Once you have this, you can copy the view, add it and lay it out into your view hierarchy, and use it just like any other UIView.

let animationCardView = cardView.copyView()
animationCardView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(animationCardView)

NSLayoutConstraint.activate([animationCardView.leadingAnchor.constraint(equalTo: cardView.leadingAnchor),
                                     animationCardView.trailingAnchor.constraint(equalTo: cardView.trailingAnchor),
                                     animationCardView.topAnchor.constraint(equalTo: cardView.topAnchor),
                                     animationCardView.bottomAnchor.constraint(equalTo: cardView.bottomAnchor)])
Community
  • 1
  • 1
Clément Cardonnel
  • 4,232
  • 3
  • 29
  • 36