2

I'm not sure how to create this animation. Would you somehow split the 1 jpg file evenly in 3 pieces and animate that? Or would you have to make multiple copies of the jpg and do something with them?

Any help would be awesome!

JEL
  • 1,540
  • 4
  • 23
  • 51
  • When you say “there is an image shifting”, are you referring to the spatial displacement (the frame moves around on the screen) or the change from one image to the next (from the cartoony image to the photo)? – rob mayoff Jan 12 '17 at 02:09
  • If you can split the image correctly, you can use [this to animate it](http://stackoverflow.com/a/8545212/1219956) – Fonix Jan 12 '17 at 02:10
  • @robmayoff Sorry about the video, that is my hand recording the video. What I mean is the `the change from one image to the next (from the cartoony image to the photo)` – JEL Jan 12 '17 at 02:11
  • @Fonix Do you think it's just that: 1) split the stitched image into three pieces 2) Animate between them using `animationImages ` – JEL Jan 12 '17 at 02:15
  • Should do the trick, the problem is, if the stitched images have variable amount of frames and/or different sizes, im not sure how you will know where to split the image. if the image always stays the same it will be fine though – Fonix Jan 12 '17 at 02:17
  • @Fonix Ok, when you say 'split' the images, do you mean split 1 jpg file into 3 jpg files? Then create 3 `UIImages` from each of these separate files? – JEL Jan 12 '17 at 02:20
  • ye split the 1 jpg with the 3 frames in it, to just 3 jpgs with 1 frame each, then you can create just 1 `UIImageView` with all 3 frames in, and make it cycle between them using the answer i linked `[UIImage animatedImagesWithImages:animationFrames duration:10];` – Fonix Jan 12 '17 at 02:22
  • @Fonix Correct me if I'm wrong, but I don't believe that api allows you to customize the transition animation. For example there is no fade – JEL Jan 12 '17 at 02:40
  • You need to write custom methods for this. Especially since you want the fade. What you could take a look at is how they do the "card flip" animation, and instead of flipping, you change that animation to fade type. Then handle the image sequence as needed. – GeneCode Jan 12 '17 at 02:43
  • oh i see, i think you will have to manually animate the image change then, so just load up 1 image into the `UIImageView`, then use [this method](http://stackoverflow.com/a/9773674/1219956) to animate to the next, you will have to come up with a way of cycling through the images yourself, prob just keep an array of the frames and index through them and cycle back when you hit the end – Fonix Jan 12 '17 at 02:44
  • @Fonix Kk, that makes sense, thanks! So there seems to be this way and Rob's answer below, I will see how I can make any of these work perfectly (hopefully not to hard). – JEL Jan 12 '17 at 02:54
  • @GeneCode Will look into it - thanks! – JEL Jan 12 '17 at 02:54

1 Answers1

2

UPDATE

Since you want a crossfade, it's probably easiest to do this by splitting the image into separate cel images:

import UIKit
import PlaygroundSupport

extension UIImage {
    func subImage(inUnitRect unitRect: CGRect) -> UIImage? {
        guard imageOrientation == .up, let cgImage = self.cgImage else { return nil }
        let cgImageWidth = CGFloat(cgImage.width)
        let cgImageHeight = CGFloat(cgImage.height)
        let scaledRect = CGRect(x: unitRect.origin.x * cgImageWidth, y: unitRect.origin.y * cgImageHeight, width: unitRect.size.width * cgImageWidth, height: unitRect.size.height * cgImageHeight)
        guard let croppedCgImage = cgImage.cropping(to: scaledRect) else { return nil }
        return UIImage(cgImage: croppedCgImage, scale: scale, orientation: .up)
    }
}

let image = #imageLiteral(resourceName: "image.png")

let celCount: CGFloat = 3
let cels = stride(from: 0, to: celCount, by: 1).map({ (i) -> UIImage in
    image.subImage(inUnitRect: CGRect(x: i / celCount, y: 0, width: 1/3, height: 1))!
})

Then we can use a keyframe animation to crossfade the layer contents:

let imageView = UIImageView(image: cels[0])

PlaygroundPage.current.liveView = imageView

let animation = CAKeyframeAnimation(keyPath: "contents")
var values = [CGImage]()
var keyTimes = [Double]()
for (i, cel) in cels.enumerated() {
    keyTimes.append(Double(i) / Double(cels.count))
    values.append(cel.cgImage!)
    // The 0.9 means 90% of the time will be spent *outside* of crossfade.
    keyTimes.append((Double(i) + 0.9) / Double(cels.count))
    values.append(cel.cgImage!)
}
values.append(cels[0].cgImage!)
keyTimes.append(1.0)
animation.keyTimes = keyTimes.map({ NSNumber(value: $0) })
animation.values = values
animation.repeatCount = .infinity
animation.duration = 5
imageView.layer.add(animation, forKey: animation.keyPath)

Result:

animation with crossfade

ORIGINAL

There are multiple ways you can do this. One is by setting or animating the contentsRect property of the image view's layer.

In your image, there are three cels, and each occupies exactly 1/3 of the image. The contentsRect is in the unit coordinate space, which makes computation easy. The contentsRect for cel i is CGRect(x: i/3, y: 0, width: 1/3, height: 0).

You want discrete jumps between cels, instead of smooth sliding transitions, so you need to use a keyframe animation with a kCAAnimationDiscrete calculationMode.

import UIKit
import PlaygroundSupport

let image = #imageLiteral(resourceName: "image.png")
let celSize = CGSize(width: image.size.width / 3, height: image.size.height)
let imageView = UIImageView()
imageView.frame = CGRect(origin: .zero, size: celSize)
imageView.image = image

PlaygroundPage.current.liveView = imageView

let animation = CAKeyframeAnimation(keyPath: "contentsRect")
animation.duration = 1.5
animation.calculationMode = kCAAnimationDiscrete
animation.repeatCount = .infinity
animation.values = [
    CGRect(x: 0, y: 0, width: 1/3.0, height: 1),
    CGRect(x: 1/3.0, y: 0, width: 1/3.0, height: 1),
    CGRect(x: 2/3.0, y: 0, width: 1/3.0, height: 1)
] as [CGRect]
imageView.layer.add(animation, forKey: animation.keyPath!)

Result:

animation demo

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Thanks! Wow that's really interesting easier to do than creating 3 `jpg` from 1 `jpg` and animating between them. I think the only thing that is not the same is the transition - is there a way to make the transition the same (it seems like there is a flash of white with a fade)? Prob easier to see here https://vid.me/vWVw Asking because I am not sure how I would add this using your code – JEL Jan 12 '17 at 02:34
  • You could do the flash by putting a solid white view behind the image view, and animating the image view's opacity with another keyframe animation. The opacity animation would be a bit more code than what's in my answer, but not rocket science. – rob mayoff Jan 12 '17 at 02:36
  • Actually I don't think there's a flash of white. I think there's just a crossfade between cels. – rob mayoff Jan 12 '17 at 02:37
  • Hm ok, so that seems easier if it's a crossfade. Would this be using `Core Animation` as well for the fading? – JEL Jan 12 '17 at 02:39
  • To follow up, since this uses `Core Animation`, would the cross fade also have to be done with `Core Animation` - possibly combine these two animations, the shifting and fade into a group? – JEL Jan 12 '17 at 03:10
  • Nice! Last questions: It seems that just a crossfade might not give that white flash (I could be wrong), do you believe there needs to be something extra to get that effect? Also, in terms of speed, to make it slower like the video I guess it's just changing the `duration` value? (Not so good with `Core Animation` so that's why I'm asking). Will accept your answer, just last thing please (just trying to match video exactly) – JEL Jan 12 '17 at 03:33
  • The duration is in seconds, so you can set it however you like. You might need to tune the 0.9 also. As for a flash, you would need to add a white view and animate opacity as I described in my other comment – rob mayoff Jan 12 '17 at 03:37
  • @rob mayoff, could use `PlaygroundPage` in `viewController` ? – aircraft Jan 12 '17 at 03:44
  • @robmayoff Alright, got it thanks! So there are 3 pieces to this: 1) Make 3 `UIImage` from 1 `jpg`. 2) Animate `contents ` with Keyframe 3) Animate `UIView` white and opacity to get flash (Please correct me if I am wrong) – JEL Jan 12 '17 at 03:54
  • @robmayoff Hey Rob! I have a question about centering a label in a view. I thought you would be the best person to answer it, please take a look here: https://stackoverflow.com/questions/55114054/center-label-based-on-view – JEL Mar 12 '19 at 22:36