0

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")
    }
Krekin
  • 1,516
  • 1
  • 13
  • 24

1 Answers1

0
 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!

Asperi
  • 228,894
  • 20
  • 464
  • 690
Liz
  • 1
  • Ah yes, the 'halve' is a convenience var extension of CGSize that just halves the size. 'animColor' ... don't remember what that does, but is some kind of custom logic regarding color. Glad someone got something useful out of this :) – Krekin Jan 09 '20 at 07:35