jwhubert91 jwhubert91 - 1 year ago 113
iOS Question

Viewcontroller background image with horizontal animation

I'm trying to create an animated background image for one of my view controllers where the image scrolls horizontally from left to right and then repeats when it has reached the end of the image horizontally. This effect is extremely common in games with SpriteKit but there isn't clear information anywhere on how to do it in a standard view controller with UIKit. Extra points if you can tell me how to set the image's top bounds to the bottom of the navigation bar rather than the top of the view controller :)

Here is the image:

Answer Source

Use two copies of the image. Start with one image fully occupying the screen. The other is to its left. Animate to the right, so that the second copy pushes the first copy off the screen. When the animation reaches the end, so that just one copy is fully occupying the screen, jump back to the start and repeat. The jump will not be visible because the images are identical and the jump is performed when they are in identical positions.

The nice thing is that the behavior I just described is the default for a repeating CABasicAnimation; it jumps back to the start automatically at the end of every repetition. Thus I was able to write a proof-of-concept very quickly (Swift 3, iOS 10):

    let im = UIImage(named:"stuff")!
    // create double image
    let r = UIGraphicsImageRenderer(size:
    let im2 = r.image { _ in
        im.draw(at: .zero)
        im.draw(at: CGPoint(x: im.size.width, y: 0))
    // create layer to hold double image
    let imlay = CALayer()
    imlay.frame.size = im2.size
    imlay.contents = im2.cgImage
    // put it inside a single-image-sized container
    let scroll = CALayer()
    scroll.frame = CGRect(origin: .zero, size: im.size)
    scroll.masksToBounds = true
    scroll.bounds.origin = CGPoint(x:im.size.width, y:0)
    // put it in the interface
    // animate
    let anim = CABasicAnimation(keyPath: "bounds.origin")
    anim.toValue = NSValue(cgPoint: .zero)
    anim.duration = 10
    anim.repeatCount = .infinity
    scroll.add(anim, forKey: nil)