UIView fill animation in iOS Swift? - ios

I want to fill the color of view from left to right as fill animation. I have been using this code to get the effect but somehow it doesn't show any animation.i am using below code.
UIView.animate(withDuration: 2.0, delay: 0.2, options:.curveLinear, animations: {
view!.backgroundColor = Constant.AppColor.viewBottomFocus
}, completion:nil)
please tell me how can i achieve this?

I don't think you can fill a color with left to right animation with simple UIView blocks. You can ofcourse change width or trailing constraint of the view and it will appear that the view is changing its color. But, I dont think thats the ideal way to do it.
To achieve what you want I guess you have to use gradient layer and animate the locations:
class AnimatingV:UIView {
func animate() {
let layer = CAGradientLayer()
let startLocations = [0, 0]
let endLocations = [1, 2]
layer.colors = [UIColor.red.cgColor, UIColor.white.cgColor]
layer.frame = self.frame
layer.locations = startLocations as [NSNumber]
layer.startPoint = CGPoint(x: 0.0, y: 1.0)
layer.endPoint = CGPoint(x: 1.0, y: 1.0)
self.layer.addSublayer(layer)
let anim = CABasicAnimation(keyPath: "locations")
anim.fromValue = startLocations
anim.toValue = endLocations
anim.duration = 2.0
layer.add(anim, forKey: "loc")
layer.locations = endLocations as [NSNumber]
}
}
let animatingV = AnimatingV(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
animatingV.backgroundColor = UIColor.yellow
animatingV.animate()
PlaygroundPage.current.liveView = animatingV

Create UIView in storyboard and make its subclass of UIProgressView
then use this code to fill view , you can use animated fill as well by calling this function in scheduler
//progressView is view that you have create in storyboard
progressView.progress = .25; // .25 is the fill rate of total view
progressView.trackTintColor = [UIColor whiteColor];
progressView.progressTintColor = [UIColor redColor];

Related

UIImageView is not animating

I am trying to have a view in which 3 dots would animate infinity and here is the code snippet(swift 5) for it
func showAnimatingDotsInImageView() {
let lay = CAReplicatorLayer()
lay.frame = CGRect(x: 0, y: 0, width: 15, height: 7) //yPos == 12
let circle = CALayer()
circle.frame = CGRect(x: 0, y: 0, width: 7, height: 7)
circle.cornerRadius = circle.frame.width / 2
circle.backgroundColor = UIColor.green.cgColor
lay.addSublayer(circle)
lay.instanceCount = 3
lay.instanceTransform = CATransform3DMakeTranslation(10, 0, 0)
let anim = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
anim.fromValue = 1.0
anim.toValue = 0.2
anim.duration = 1
anim.repeatCount = .infinity
circle.add(anim, forKey: nil)
lay.instanceDelay = anim.duration / Double(lay.instanceCount)
test1.layer.addSublayer(lay)
}
In the above code test1 is a UIImageView.
The main issue is the dots are shown but it's static and doesn't animate.
I tried your code and it is animating just fine.
Any chance you are calling this from a thread which is different than the Main thread? Like for example something on the background is happening, which you want to triggers the loading?
Try adding:
DispatchQueue.main.async { [weak self] in
self?.showAnimatingDotsInImageView()
}

Gradient Color Swift

Here is what I am trying to do:
The screenshot is taken from Iphone:
This is my code:
#IBOutlet weak var viewBottomBorder: UIView!
let gradient = CAGradientLayer()
gradient.startPoint = CGPoint(x: 0.5, y: 0.0)
gradient.endPoint = CGPoint(x: 0.5, y: 1.0)
let whiteColor = UIColor.black
gradient.colors = [whiteColor.withAlphaComponent(0.0).cgColor, whiteColor.withAlphaComponent(1.0), whiteColor.withAlphaComponent(1.0).cgColor]
gradient.locations = [NSNumber(value: 0.0),NSNumber(value: 0.2),NSNumber(value: 1.0)]
gradient.frame = viewBottomBorder.bounds
viewBottomBorder.layer.mask = gradient
Question: How to show same text in white color with gradient?
Can someone please explain to me how to solve this , i've tried to solve this issue but no results yet.
Any help would be greatly appreciated.
Thanks in advance.
You need to start your gradient at the top, because it's darker at the top. So x should be have max alpha. Then as y increases reduce the alpha.
Try this:
let gradient = CAGradientLayer()
gradient.startPoint = CGPoint(x: 1.0, y: 0.0)
gradient.endPoint = CGPoint(x: 0.0, y: 1.0)
let whiteColor = UIColor.black
gradient.colors = [whiteColor.withAlphaComponent(1.0).cgColor, whiteColor.withAlphaComponent(1.0), whiteColor.withAlphaComponent(0.0).cgColor]
gradient.locations = [NSNumber(value: 1.0),NSNumber(value: 0.7),NSNumber(value: 0.0)]
gradient.frame = viewBottomBorder.bounds
viewBottomBorder.layer.mask = gradient
A little alternative:
func setupGradient(on view: UIView, withHeight height: CGFloat) {
// this is view that holds gradient
let gradientHolderView = UIView()
gradientHolderView.backgroundColor = .clear
gradientHolderView.translatesAutoresizingMaskIntoConstraints = false
// here you set layout programmatically (you can create this view in storyoard as well and attach gradient to it)
// make sure to position this view under your text
view.addSubview(gradientHolderView) // alternative: view.insertSubview(gradientHolderView, belowSubview: YourTextLaber)
gradientHolderView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
gradientHolderView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
gradientHolderView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
gradientHolderView.heightAnchor.constraint(equalToConstant: height).isActive = true
// this is needed to kinda refresh layout a little so later we can get bounds from view. I'm not sure exactly why but without this if you create view programmatically it is needed
gradientHolderView.setNeedsLayout()
gradientHolderView.layoutIfNeeded()
// here you create gradient as layer and add it as a sublayer on your view (modify colors as you like)
let gradientLayer = CAGradientLayer()
gradientLayer.frame = gradientHolderView.bounds
gradientLayer.colors = [UIColor.clear.cgColor, UIColor(white: 0, alpha: 0.6).cgColor]
gradientHolderView.layer.addSublayer(gradientLayer)
}
Use above code to apply Gradient to your view
var gradientBackground : CAGradientLayer?
func applyGradientToBackground() {
if self.gradientBackground != nil {
self.gradientBackground?.removeFromSuperlayer()
}
self.gradientBackground = CAGradientLayer()
self.gradientBackground?.frame = self.your_view_name.bounds
let cor1 = UIColor.black.withAlphaComponent(1).cgColor
let cor2 = UIColor.white.cgColor
let arrayColors = [cor1, cor2]
self.gradientBackground?.colors = arrayColors
self.your_view_name.layer.insertSublayer(self.gradientBackground!, at: 0)
}

CALayer animation for width

I have one view in which need to add one layer. I successfully added the layer but now i want to do animation for it's width means layer start filling from "started" label and end at "awaiting..." label. I try to add animation with CABasicAnimation it start from beginning but it not properly fill. Please check video like for more understanding.
func startProgressAnimation()
{
self.ivStarted.isHighlighted = true
let layer = CALayer()
layer.backgroundColor = #colorLiteral(red: 0.4980392157, green: 0.8352941176, blue: 0.1921568627, alpha: 1)
layer.masksToBounds = true
layer.frame = CGRect.init(x: self.viewStartedAwaited.frame.origin.x, y: self.viewStartedAwaited.frame.origin.y, width: 0.0, height: self.viewStartedAwaited.frame.height)
self.ivStarted.layer.addSublayer(layer)
CATransaction.begin()
let boundsAnim:CABasicAnimation = CABasicAnimation(keyPath: "bounds.size.width")
boundsAnim.fromValue = NSNumber.init(value: 0.0)
boundsAnim.byValue = NSNumber.init(value: Float(self.viewStartedAwaited.frame.size.width / 2.0))
boundsAnim.toValue = NSNumber.init(value: Float(self.viewStartedAwaited.frame.size.width))
let anim = CAAnimationGroup()
anim.animations = [boundsAnim]
anim.isRemovedOnCompletion = false
anim.duration = 5
anim.fillMode = kCAFillModeBoth
CATransaction.setCompletionBlock{ [weak self] in
self?.ivAwaited.isHighlighted = true
}
layer.add(anim, forKey: "bounds.size.width")
CATransaction.commit()
}
Here is the video what i achieve with this code.
https://streamable.com/h6tpp
Not Proper
Proper image
Just set your
layer.anchorPoint = CGPoint(x: 0, y: 0)
After this it will work according to your requirement.
The default anchorpoint of (0.5,0.5) means that the growing or shrinking of width happens evenly on both sides. Anchorpoint of (0,0) means the layer is essentially pinned to the top left, so whatever new width it gets, the change happens on the right. That is, it matters not whether your animation key path is 'bounds' or 'bounds.size.width'. The anchor point is what determines where the change happens

CABasicAnimation not scaling around center

I want to perform opacity And Scale effect at same time my animation work perfect but it's position is not proper. i want to perform animation on center.
This is my code.
btn.backgroundColor = UIColor.yellowColor()
let stroke = UIColor(red:236.0/255, green:0.0/255, blue:140.0/255, alpha:0.8)
let pathFrame = CGRectMake(24, 13, btn.bounds.size.height/2, btn.bounds.size.height/2)
let circleShape1 = CAShapeLayer()
circleShape1.path = UIBezierPath(roundedRect: pathFrame, cornerRadius: btn.bounds.size.height/2).CGPath
circleShape1.position = CGPoint(x: 2, y: 2)
circleShape1.fillColor = stroke.CGColor
circleShape1.opacity = 0
btn.layer.addSublayer(circleShape1)
circleShape1.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.fromValue = NSValue(CATransform3D: CATransform3DIdentity)
scaleAnimation.toValue = NSValue(CATransform3D: CATransform3DMakeScale(2.0, 2.0, 1))
let alphaAnimation = CABasicAnimation(keyPath: "opacity")
alphaAnimation.fromValue = 1
alphaAnimation.toValue = 0
CATransaction.begin()
let animation = CAAnimationGroup()
animation.animations = [scaleAnimation, alphaAnimation]
animation.duration = 1.5
animation.repeatCount = .infinity
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
circleShape1.addAnimation(animation, forKey:"Ripple")
CATransaction.commit()
The problem is that you are not grappling with frames correctly. You say:
let circleShape1 = CAShapeLayer()
But you have forgotten to give circleShape1 a frame! Thus, its size is zero, and very weird things happen when you animate it. Your job in the very next line should be to assign circleShape1 a frame. Example:
circleShape1.frame = pathFrame
That may or may not be the correct frame; it probably isn't. But you need to figure that out.
Then, you need to fix the frame of your Bezier path in terms of the shape layer's bounds:
circleShape1.path = UIBezierPath(roundedRect: circleShape1.bounds // ...
I have never worked with sublayers so I would make it with a subview instead, makes the code a lot easier:
btn.backgroundColor = UIColor.yellow
let circleShape1 = UIView()
circleShape1.frame.size = CGSize(width: btn.frame.height / 2, height: btn.frame.height / 2)
circleShape1.center = CGPoint(x: btn.frame.width / 2, y: btn.frame.height / 2)
circleShape1.layer.cornerRadius = btn.frame.height / 4
circleShape1.backgroundColor = UIColor(red:236.0/255, green:0.0/255, blue:140.0/255, alpha:0.8)
circleShape1.alpha = 1
btn.addSubview(circleShape1)
UIView.animate(withDuration: 1,
delay: 0,
options: [.repeat, .curveLinear],
animations: {
circleShape1.transform = CGAffineTransform(scaleX: 5, y: 5)
circleShape1.alpha = 0.4
}, completion: nil)
You need to create path frame with {0,0} position, and set correct frame to the Layer:
let pathFrame = CGRectMake(0, 0, btn.bounds.size.height/2, btn.bounds.size.height/2)
....
circleShape1.frame = CGRect(x: 0, y: 0, width: pathFrame.width, height: pathFrame.height)
circleShape1.position = CGPoint(x: 2, y: 2)
If you want to create path with {13,24} position you need to change width and height in layer. Your shape should be in center of layer.

Animating Background Color Change - Side to Side

Let's say I have a rectangle that is horizontally long. It's blue. I want it to be red. However, I want the color change to start on one side and end on the other.
I can use a key frame animation to make the whole view gradually change from red to blue. Is there a way to gradually change it from left-right/right-left??
UIView.animateKeyframesWithDuration(2.0 /*Total*/, delay: 0.0, options: UIViewKeyframeAnimationOptions.CalculationModeLinear, animations: {
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 1/1, animations:{
self.view.backgroundColor = UIColor.redColor()
self.view.layoutIfNeeded()
})
},
completion: { finished in
if (!finished) { return }
})
Take a look at CAGradientLayer. You can animate its locations, colors or endPoint and startPoint
Here is a quick snippet you can paste into playground and see how it can work. In this case I'm animating the gradients color locations.
import UIKit
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
let view = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 400))
let startLocations = [0, 0]
let endLocations = [1, 2]
let layer = CAGradientLayer()
layer.colors = [UIColor.redColor().CGColor, UIColor.blueColor().CGColor]
layer.frame = view.frame
layer.locations = startLocations
layer.startPoint = CGPoint(x: 0.0, y: 1.0)
layer.endPoint = CGPoint(x: 1.0, y: 1.0)
view.layer.addSublayer(layer)
let anim = CABasicAnimation(keyPath: "locations")
anim.fromValue = startLocations
anim.toValue = endLocations
anim.duration = 2.0
layer.addAnimation(anim, forKey: "loc")
layer.locations = endLocations
XCPlaygroundPage.currentPage.liveView = view
Swift 3 version:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
let view = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 400))
let startLocations = [0, 0]
let endLocations = [1, 2]
let layer = CAGradientLayer()
layer.colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
layer.frame = view.frame
layer.locations = startLocations as [NSNumber]?
layer.startPoint = CGPoint(x: 0.0, y: 1.0)
layer.endPoint = CGPoint(x: 1.0, y: 1.0)
view.layer.addSublayer(layer)
let anim = CABasicAnimation(keyPath: "locations")
anim.fromValue = startLocations
anim.toValue = endLocations
anim.duration = 2.0
layer.add(anim, forKey: "loc")
layer.locations = endLocations as [NSNumber]?
PlaygroundPage.current.liveView = view

Resources