Mosaic light show CAReplicatorLayer animation

600 Views Asked by At

I'm trying to achieve this mosaic light show effect for my background view with the CAReplicatorLayer object: https://downloops.com/stock-footage/mosaic-light-show-blue-illuminated-pixel-grid-looping-background/

enter image description here

Each tile/CALayer is a single image that was replicated horizontally & vertically. That part I have done.

It seems to me this task is broken into at least 4 separate parts:

  1. Pick a random tile
  2. Select a random range of color offset for the selected tile
  3. Apply that color offset over a specified duration in seconds
  4. If the random color offset exceeds a specific threshold then apply a glow effect with the color offset animation.

But I'm not actually sure this would be the correct algorithm.

My current code was taken from this tutorial: https://www.swiftbysundell.com/articles/ca-gems-using-replicator-layers-in-swift/

Animations are not my strong suite & I don't actually know how to apply continuous/repeating animation on all tiles. Here is my current code:

    @IBOutlet var animationView: UIView!

    func cleanUpAnimationView() {
        self.animationView.layer.removeAllAnimations()
        self.animationView.layer.sublayers?.removeAll()
    }

    /// Start a background animation with a replicated pattern image in tiled formation.
    func setupAnimationView(withPatternImage patternImage: UIImage, animate: Bool = true) {
        // Tutorial: https://www.swiftbysundell.com/articles/ca-gems-using-replicator-layers-in-swift/

        let imageSize = patternImage.size.halve

        self.cleanUpAnimationView()

        // Animate pattern image
        let replicatorLayer = CAReplicatorLayer()
        replicatorLayer.frame.size = self.animationView.frame.size
        replicatorLayer.masksToBounds = true
        self.animationView.layer.addSublayer(replicatorLayer)

        // Give the replicator layer a sublayer to replicate
        let imageLayer = CALayer()
        imageLayer.contents = patternImage.cgImage
        imageLayer.frame.size = imageSize
        replicatorLayer.addSublayer(imageLayer)

        // Tell the replicator layer how many copies (or instances) of the image needs to be rendered. But we won't see more than one since they are, per default, all rendered/stacked on top of each other.
        let instanceCount = self.animationView.frame.width / imageSize.width
        replicatorLayer.instanceCount = Int(ceil(instanceCount))
        // Instance offsets & transforms is needed to move them
        // 'CATransform3D' transform will be used on each instance: shifts them to the right & reduces the red & green color component of each instance's tint color.

        // Shift each instance by the width of the image
        replicatorLayer.instanceTransform = CATransform3DMakeTranslation(imageSize.width, 0, 0)

        // Reduce the red & green color component of each instance, effectively making each copy more & more blue while horizontally repeating the gradient pattern
        let colorOffset = -1 / Float(replicatorLayer.instanceCount)
        replicatorLayer.instanceRedOffset = colorOffset
        replicatorLayer.instanceGreenOffset = colorOffset
        //replicatorLayer.instanceBlueOffset = colorOffset
        //replicatorLayer.instanceColor = UIColor.random.cgColor

        // Extend the original pattern to also repeat vertically using another tint color gradient
        let verticalReplicatorLayer = CAReplicatorLayer()
        verticalReplicatorLayer.frame.size = self.animationView.frame.size
        verticalReplicatorLayer.masksToBounds = true
        verticalReplicatorLayer.instanceBlueOffset = colorOffset
        self.animationView.layer.addSublayer(verticalReplicatorLayer)

        let verticalInstanceCount = self.animationView.frame.height / imageSize.height
        verticalReplicatorLayer.instanceCount = Int(ceil(verticalInstanceCount))

        verticalReplicatorLayer.instanceTransform = CATransform3DMakeTranslation(0, imageSize.height, 0)
        verticalReplicatorLayer.addSublayer(replicatorLayer)

        guard animate else { return }

        // Set both the horizontal & vertical replicators to add a slight delay to all animations applied to the layer they're replicating
        let delay = TimeInterval(0.1)
        replicatorLayer.instanceDelay = delay
        verticalReplicatorLayer.instanceDelay = delay

        // This will make the image layer change color
        let animColor = CABasicAnimation(keyPath: "instanceRedOffset")
        animColor.duration = animationDuration
        animColor.fromValue = verticalReplicatorLayer.instanceRedOffset
        animColor.toValue = -1 / Float(Int.random(replicatorLayer.instanceCount-1))
        animColor.autoreverses = true
        animColor.repeatCount = .infinity
        replicatorLayer.add(animColor, forKey: "colorshift")

        let animColor1 = CABasicAnimation(keyPath: "instanceGreenOffset")
        animColor1.duration = animationDuration
        animColor1.fromValue = verticalReplicatorLayer.instanceGreenOffset
        animColor1.toValue = -1 / Float(Int.random(replicatorLayer.instanceCount-1))
        animColor1.autoreverses = true
        animColor1.repeatCount = .infinity
        replicatorLayer.add(animColor1, forKey: "colorshift1")

        let animColor2 = CABasicAnimation(keyPath: "instanceBlueOffset")
        animColor2.duration = animationDuration
        animColor2.fromValue = verticalReplicatorLayer.instanceBlueOffset
        animColor2.toValue = -1 / Float(Int.random(replicatorLayer.instanceCount-1))
        animColor2.autoreverses = true
        animColor2.repeatCount = .infinity
        replicatorLayer.add(animColor2, forKey: "colorshift2")
    }
1

There are 1 best solutions below

1
Liz On
 let imageSize = patternImage.size.halve 

and

 animColor.toValue = -1 / Float(Int.random(replicatorLayer.instanceCount-1))

both generated errors.

I removed the halve and commented-out the animColor lines and the code runs and animates. I could not get ANY replicator layer to display or animate at all (not even the most basic apple or tutorial code) until I used your code. Thank you so much!