Load Animated gif in UIImageView IOS - ios

I have this gif into my assets file
The name of the assets is loading_apple but when I add the following code I get a nullpointer exception error:
let img = UIImage (named: "loading_apple")
So how can I show this gif ? :(
Hope someone can help me.

I would recommend using FLAnimatedImage https://github.com/Flipboard/FLAnimatedImage

I would recommend breaking out the frames of that gif and use animatedImageNamed:duration: - you can name them all the similar name with a number change at the end. For instance:
loading-1.png
loading-2.png
loading-3.png etc.
Xcode will recognize you want multiple images and will play those through in order.
Look at THIS

instead of storing the gif file, why don't u make use of CAReplicatorLayer refer this link and this link in objective c, i took same code in second like and with some modification,
func spinTheCustomSpinner() -> Void {
let aBar:CALayer = CALayer.init()
aBar.bounds = CGRectMake(0, 0, 8, 25);
aBar.cornerRadius = 4; //(8/2)
aBar.backgroundColor = UIColor.blackColor().CGColor
aBar.position = CGPointMake(150.0, 150.0 + 35)
let replicatorLayer:CAReplicatorLayer = CAReplicatorLayer.init()
replicatorLayer.bounds = CGRectMake(0, 0,300,300)
replicatorLayer.cornerRadius = 10.0
replicatorLayer.backgroundColor = UIColor.whiteColor().CGColor
replicatorLayer.position = CGPointMake(CGRectGetMidX(self.view!.bounds), CGRectGetMidY(self.view!.bounds))
let angle:CGFloat = CGFloat (2.0 * M_PI) / 12.0
let transform:CATransform3D = CATransform3DMakeRotation(angle, 0, 0, 1.0)
replicatorLayer.instanceCount = 12
replicatorLayer.instanceTransform = transform
replicatorLayer .addSublayer(aBar)
self.view!.layer .addSublayer(replicatorLayer)
aBar.opacity = 0.0
let animationFade:CABasicAnimation = CABasicAnimation(keyPath: "opacity")
animationFade.fromValue = NSNumber(float: 1.0)
animationFade.toValue = NSNumber(float: 0.0)
animationFade.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animationFade.repeatCount = HUGE
animationFade.duration = 1.0
let aBarAnimationDuration:Double = 1.0/12.0
replicatorLayer.instanceDelay = aBarAnimationDuration
aBar .addAnimation(animationFade, forKey: "fadeAnimation")
}
if u use above with a view, show this view while loading and hide after loading, this is really cool and handy and there is no headache of storing the gif file and using a image view to load.
and by changing the properties of aBar layer u get different effects.

For this specific gif you may want to use UIActivityIndicatorView and make it animating with startAnimating()

Related

Mosaic light show CAReplicatorLayer animation

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/
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:
Pick a random tile
Select a random range of color offset for the selected tile
Apply that color offset over a specified duration in seconds
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")
}
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!

Changing a CALayer contents before animation finished

I work on a UI component that implements flip-card clock animation. All works fine, but when I change a top CALayer contents to new image, the old image stays visible before changeding. It creates confusion effect. For better explanation I place the gif animation bellow:
This is code with changing a CALayer contents:
firstTopLayer.contents = secondTopLayer.contents
let bottomAnim = CABasicAnimation(keyPath: "transform")
bottomAnim.duration = animDuration/2
bottomAnim.repeatCount = 1
bottomAnim.fromValue = NSValue.init(caTransform3D:
CATransform3DMakeRotation((CGFloat)(M_PI_2), 1, 0, 0))
bottomAnim.toValue = NSValue.init(caTransform3D:
CATransform3DMakeRotation(0, 1, 0, 0))
bottomAnim.isRemovedOnCompletion = true
bottomAnim.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseIn)
firstBottomLayer.add(bottomAnim, forKey: "bottom")
firstBottomLayer.contents = self.bufferContents
For more information I place a link to the repository
I found a solution. Top animation must have this configuration
topAnim.fillMode = kCAFillModeForwards
topAnim.isRemovedOnCompletion = false
and after each start this animation.
firstTopLayer.removeAnimation(forKey: kTopAnimaton)
With this configuration the top layer stays in it last frame animation position

Keeping a background image in the center on animation

I have animation setup to resize the image to about 1.3 times it's original size. The animations and everything are working without a problem but the image is moving towards the top left. Which means that the position of the image is not centering upon resize. How do i solve this problem
These are the animations I setup
var borderWidth:CABasicAnimation = CABasicAnimation(keyPath: "borderWidth")
borderWidth.fromValue = 0
borderWidth.toValue = 5
borderWidth.repeatCount = Float.infinity
sender.layer.borderWidth = 0
var increaseButtonHeight:CABasicAnimation = CABasicAnimation(keyPath: "bounds.size.height")
increaseButtonHeight.fromValue = sender.frame.size.height
increaseButtonHeight.toValue = sender.frame.size.height * 1.3
var increaseButtonWidth: CABasicAnimation = CABasicAnimation(keyPath: "bounds.size.width")
increaseButtonWidth.fromValue = sender.frame.size.width
increaseButtonWidth.toValue = sender.frame.size.width * 1.3
var boom:CAAnimationGroup = CAAnimationGroup()
boom.animations = [borderWidth,increaseButtonWidth, increaseButtonHeight]
boom.repeatCount = Float.infinity
boom.duration = 0.5
boom.autoreverses = true
sender.layer.addAnimation(boom, forKey: "boom")
Do I need to setup a new animation for centering the button continuously as the animation happens?
Please help
Nikhil
Set the property contentsGravity of the layer to kCAGravityCenter

Save CoreAnimation sequence frame by frame

How can I save each frame of a CoreAnimation based animation (as image files)?
Here's my little playground scene. The animation lasts 2.4 seconds.
let stage = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 300, height: 300))
stage.backgroundColor = UIColor.blueColor();
var dot = UIView(frame: CGRectMake(0, 0, 10, 10))
dot.backgroundColor = UIColor.redColor()
dot.center = stage.center
UIView.animateWithDuration(2.4, animations: { () -> Void in
dot.center.y = dot.center.y + 50
})
I know how to save a static UIView frame as PDF but I am not sure how to hook into the animation sequence while it happens and capture/save the view frame by frame.
Option 1
As far as I see it I need to hook into the animation block and save the stage view for each frame (at a given frame rate?). How can I do this? Is there some sort of callback I can use?
Option 2
While looking for a solution I came across another option that looks even more promising (based on this question and this awesome blog post). Using a CBAnimation, setting a timingFunction and then setting the timeOffset to the progress states I want to render.
Here's an example for the frame at 50% progress (using a different example here).
var drawAnimation = CABasicAnimation(keyPath: "strokeEnd")
…
drawAnimation.fromValue = NSNumber(float: 0.0)
drawAnimation.toValue = NSNumber(float: 1.0)
drawAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
circle.addAnimation(drawAnimation, forKey: "drawCircleAnimation")
circle.speed = 0.0;
circle.timeOffset = 0.5
The problem is that I cannot capture this timeOffset based animation state. When I render the view I only get the final state instead of the frozen animation frame at 0.5.
Any help to resolve this is appreciated.

Adding image to particle emitter and stopping after a duration in swift/ios

I've been trying to learn and understand the emitter functions of CAEmitter, but I'm currently a little bit stuck. I want to add an image for the emitter and make it stop after a duration.
I've got a view that I'm using to emit some particles, and I want them to only appear emit when the view appears for around 10 seconds, then stop. I also am unsure how to attach a UI image with a png, instead of using CGrect.
Thanks for any help and advice!
import UIKit
class ParticleView: UIView {
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(15,8), false, 1)
let con = UIGraphicsGetCurrentContext()
CGContextAddRect(con, CGRectMake(0, 0, 15, 8))
CGContextSetFillColorWithColor(con, UIColor.whiteColor().CGColor)
CGContextFillPath(con)
let im = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// make a cell with that image
var cell = CAEmitterCell()
cell.birthRate = 10
cell.color = UIColor(red:0.5, green:0.5, blue:0.5, alpha:1.0).CGColor
cell.redRange = 1
cell.blueRange = 1
cell.greenRange = 1
cell.lifetime = 5
cell.alphaSpeed = -1/cell.lifetime
cell.velocity = -100
cell.spinRange = 10.0
cell.scale = 1.0;
cell.scaleRange = 0.2;
cell.emissionRange = CGFloat(M_PI)/5.0
cell.contents = im.CGImage
var emit = CAEmitterLayer()
emit.emitterSize = CGSize(width: 100, height: 0)
emit.emitterPosition = CGPointMake(30,100)
emit.emitterShape = kCAEmitterLayerLine
emit.emitterMode = kCAEmitterLayerLine
emit.emitterCells = [cell]
self.layer.addSublayer(emit)
}
}
I have found a nice workaround to stop CAEmitter:
Create 2 identical view controllers with the same layout
Implement a Start and Stop button on both (to begin and end the CAEmitter)
Connect the Stop button of each view controller to each other with a “Show Detail (e.g. Replace) Segue and deselect "Animates"
When you hit “Stop” button it will make a seamless transition to the identical VC without emitter particles!! But what is really happening is that you are just switching to a replica view controller. This is not an elegant solution but it is the only reliable way that I have found to stop a CAEmitter (segue to a different VC) all of the other solutions are "buggy"
visual of how the VCs are set up

Resources