Border Layer animation not Scaling from Center - ios

I am struggling with the strange issue like when I am adding scale transformation to any layer, it doesn't animated from the center. It does from origin.
func addSquareBorderLayer() {
borderLayer = CAShapeLayer()
let rect = CGRect(x: spotlight!.frame.origin.x, y: spotlight!.frame.origin.y, width: spotlight!.frame.width, height: spotlight!.frame.height)
borderLayer?.path = UIBezierPath(roundedRect: rect, cornerRadius: spotlight!.cornerRadius).cgPath
borderLayer?.strokeColor = UIColor(red: 91/255, green: 186/255, blue: 162/255, alpha: 1).cgColor //ColorRGB(91, 186, 162)
borderLayer?.lineWidth = spotlight!.cornerRadius
borderLayer?.fillColor = UIColor.clear.cgColor
layer.addSublayer(borderLayer!)
animatePulsatingLayer(pulsatingLayer: layer)
}
private func animatePulsatingLayer(pulsatingLayer : CALayer) {
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.duration = 0.5
animation.repeatCount = .greatestFiniteMagnitude
animation.autoreverses = true
animation.fromValue = 1
animation.toValue = 1.05
pulsatingLayer.add(animation, forKey: "pulsing")
}
Can anybody please help?
Refernce to what's happening: https://www.dropbox.com/s/0hqdrrv310du7vz/mo.mov?dl=0

It's a bit tricky. You need to set the frame for the layer as the rect you are setting currently for path. And set path origin to zero so that the circle(or any shape) is drawn at the same position as frame's origin. This will allow the animation to be applied at the anchorPoint (0.5, 0.5).
Here is the complete example,
var borderLayer: CAShapeLayer!
func addSquareBorderLayer() {
let size: CGFloat = 100
borderLayer = CAShapeLayer()
let rect = CGRect(origin: .zero, size: CGSize(width: size, height: size))
borderLayer?.path = UIBezierPath(roundedRect: rect, cornerRadius: 50).cgPath
borderLayer?.strokeColor = UIColor(red: 91/255, green: 186/255, blue: 162/255, alpha: 1).cgColor
borderLayer?.lineWidth = 20
borderLayer?.fillColor = UIColor.clear.cgColor
borderLayer?.frame = CGRect(origin: CGPoint(x: 100, y: 200), size: rect.size)
self.view.layer.addSublayer(borderLayer!)
animatePulsatingLayer(pulsatingLayer: borderLayer!)
}
private func animatePulsatingLayer(pulsatingLayer : CALayer) {
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.duration = 0.3
animation.repeatCount = .greatestFiniteMagnitude
animation.autoreverses = true
animation.fromValue = 1
animation.toValue = 1.2
pulsatingLayer.add(animation, forKey: "pulsing")
}
Result

Related

image disappears after keyframe animation

I have an animation where a line is drawn and then with that another image moves. The animation works good but as the animation completes the image disappears from the view. The image should stay after completion of the animation. My code for the animation:
private func addPlaneAnimation() {
let image = UIImageView(frame: CGRect(x: -30, y: view.height*0.8, width: 30, height: 30))
image.image = R.image.plane()
view.addSubview(image)
let path = UIBezierPath()
path.move(to: image.center)
path.addLine(to: CGPoint(x: (view.width*0.85), y: (view.height*0.40)))
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
shapeLayer.strokeColor = R.color.Purple()?.cgColor
shapeLayer.lineWidth = 3
shapeLayer.path = path.cgPath
view.layer.addSublayer(shapeLayer)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.duration = 4
animation.beginTime = CACurrentMediaTime() + 0.2
animation.isRemovedOnCompletion = false
let pathAnimation = CAKeyframeAnimation(keyPath: "position")
pathAnimation.path = path.cgPath
pathAnimation.duration = 4
pathAnimation.autoreverses = false
pathAnimation.isRemovedOnCompletion = false
shapeLayer.add(animation, forKey: "MyAnimation")
image.layer.add(pathAnimation, forKey: "position")
view.bringSubviewToFront(cardBarcode)
}
Can someone please help me here?
Just add two more properties.
For strokeEnd animation
animation.fillMode = CAMediaTimingFillMode.backwards
and for pathAnimation
pathAnimation.fillMode = CAMediaTimingFillMode.forwards

I am trying to add gradient as a sublayer in view but its not resizing it equal to the view

let cellview: UIView!
let gradient = CAGradientLayer()
let topColor = UIColor(red: 0.435, green: 0, blue: 0.635, alpha: 1.0)
let bottomColor = UIColor(red: 0.255 , green: 0.043, blue: 0.373, alpha: 1.0)
gradient.colors = [topColor.cgColor, bottomColor.cgColor]
gradient.startPoint = CGPoint(x:0, y:0)
gradient.endPoint = CGPoint(x:0.3, y:1)
cellview = UIView(frame: CGRect(x: 0, y: 0, width: 192, height: 160))
gradient.frame = cellview.bounds
gradient.masksToBounds = true
cellview.layer.addSublayer(gradient)
I wrote the above code according to the code gradient should cover entire view. But this is how the gradient appearing.
Following is the screen shot for app
Here's a function i use to create gradients, you may find it useful
func createGradientBackground(gradientColor: UIColor, darkStylePreferred: Bool, viewToUse: UIView) {
let gradient = CAGradientLayer(layer: viewToUse.layer)
gradient.frame = viewToUse.bounds
gradient.locations = [0, 1]
viewToUse.layer.insertSublayer(gradient, at: 0)
let animation = CABasicAnimation(keyPath: "locations")
animation.toValue = [0, 0.11, 1]
animation.fromValue = [0, 0.9, 1]
if darkStylePreferred {
gradient.colors = [UIColor.black.cgColor, gradientColor.cgColor, UIColor.black.cgColor]
} else {
gradient.colors = [UIColor.white.cgColor, gradientColor.cgColor, UIColor.white.cgColor]
}
animation.autoreverses = true
animation.repeatCount = Float.infinity
animation.speed = 0.015
animation.isRemovedOnCompletion = false
gradient.add(animation, forKey: nil)
}
I figured it out, just answering my self so it might help someone later on.
All I had to do is set the gradient in func viewDidLayoutSubviews(). And this solved my issue

How do I add layer border with blurred colour?

I am using the following code:
bottomBorder.backgroundColor = UIColor.viewShadowGray().cgColor
bottomBorder.frame = CGRect(x: 0, y: view.frame.size.height - 1, width: view.frame.size.width, height: 1)
In extended UIColor file:
class func viewShadowGray() -> UIColor
{
return UIColor(red: 177.0/255.0, green: 177.0/255.0, blue: 179.0/255.0, alpha: 0.7)
}
changing the alpha value only makes the colour light, doesn't make it look blurred.
and getting :
I am expecting to obtain something as this:
How do I proceed rectifying this here?
edited , since you tried the shadow opacity thing in your new updated question i suggest using the implemented shadow properties for UIlayer
view.layer.masksToBounds = NO
view.layer.shadowOffset = CGSizeMake(-15, 20)
view.layer.shadowRadius = 5
view.layer.shadowOpacity = 0.5
view.layer.shadowColor = UIColor.black.cgColor
i would also recommend reading this article it would help you with that big time here
and check out this solution
extension UIView {
// OUTPUT 1
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize(width: -1, height: 1)
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
// OUTPUT 2
func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize, radius: CGFloat = 1, scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = color.cgColor
layer.shadowOpacity = opacity
layer.shadowOffset = offSet
layer.shadowRadius = radius
layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
}
original answer here
using them like this
view.layer.dropShadow()

How do i call functions of a class - Swift

Im still trying to learn this portion of things. I've looked around and read a few questions about it but truthfully I dont understand any of it.
I've got a circle class that creates and draws a circle
class CircleView: UIView {
var circleLayer: CAShapeLayer!
var isAnimating = false
override init(frame: CGRect) {
let fColor = UIColor.clear.cgColor
super.init(frame: frame)
self.backgroundColor = UIColor.clear
// Use UIBezierPath as an easy way to create the CGPath for the layer.
// The path should be the entire circle.
let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
// Setup the CAShapeLayer with the path, colors, and line width
circleLayer = CAShapeLayer()
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.strokeColor = UIColor.init(rgbColorCodeRed: 230, green: 226, blue: 218, alpha: 1).cgColor
circleLayer.lineWidth = 9.0;
// Don't draw the circle initially
circleLayer.strokeEnd = 0.0
// Add the circleLayer to the view's layer's sublayers
layer.addSublayer(circleLayer)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setCircleClockwise(){
let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
self.circleLayer.removeFromSuperlayer()
self.circleLayer = formatCirle(circlePath: circlePath)
self.layer.addSublayer(self.circleLayer)
}
func setCircleCounterClockwise(){
let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: false)
self.circleLayer.removeFromSuperlayer()
self.circleLayer = formatCirle(circlePath: circlePath)
self.layer.addSublayer(self.circleLayer)
}
func formatCirle(circlePath: UIBezierPath) -> CAShapeLayer{
let circleShape = CAShapeLayer()
circleShape.path = circlePath.cgPath
circleShape.fillColor = UIColor.clear.cgColor
circleShape.strokeColor = UIColor.init(rgbColorCodeRed: 230, green: 226, blue: 218, alpha: 1).cgColor
circleShape.lineWidth = 9.0;
circleShape.strokeEnd = 0.0
return circleShape
}
func animate(duration: TimeInterval){
self.isAnimating = true
self.animateCircleFull(duration: 1)
}
func endAnimate(){
self.isAnimating = false
}
func animateCircleFull(duration: TimeInterval) {
if self.isAnimating{
CATransaction.begin()
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = duration
animation.fromValue = 0
animation.toValue = 1
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
circleLayer.strokeEnd = 1.0
CATransaction.setCompletionBlock {
self.setCircleCounterClockwise()
self.animateCircleEmpty(duration: duration)
}
// Do the actual animation
circleLayer.add(animation, forKey: "animateCircle")
CATransaction.commit()
}
}
func animateCircleEmpty(duration: TimeInterval){
if self.isAnimating{
CATransaction.begin()
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = duration
animation.fromValue = 1
animation.toValue = 0
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
circleLayer.strokeEnd = 0
CATransaction.setCompletionBlock {
self.setCircleClockwise()
self.animateCircleFull(duration: duration)
}
// Do the actual animation
circleLayer.add(animation, forKey: "animateCircle")
CATransaction.commit()
}
}
Which is being called from my viewController with the below function. It all works fine but what i cant work out is how do i go about calling the endAnimation function on the same circle?
func addCircleView() {
let diceRoll = CGFloat(Int(arc4random_uniform(7))*50)
var circleWidth = CGFloat(100)
var circleHeight = circleWidth
var bgColor: UIColor = UIColor.init(rgbColorCodeRed: 230, green: 226, blue: 218, alpha: 1)
// Create a new CircleView
let circleView = CircleView(frame: CGRect(x: self.view.frame.width/2, y: self.view.frame.height-110, width: circleWidth, height: circleHeight))
//let test = CircleView(frame: CGRect(x: diceRoll, y: 0, width: circleWidth, height: circleHeight))
cv = circleView
view.addSubview(circleView)
// Animate the drawing of the circle over the course of 1 second
circleView.animate(duration: 1)
let imageName = "ButtonBackground.png"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: self.view.frame.width/2, y: self.view.frame.height-110, width: 100, height: 100)
view.addSubview(imageView)
view.bringSubview(toFront: circleView)
}
For calling endAnimation function on the same circle you have to declare property in your ViewController class.
import UIKit
class ViewController: UIViewController {
var circleView = CircleView()
override func viewDidLoad() {
super.viewDidLoad()
self.addCircleView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func addCircleView() {
let diceRoll = CGFloat(Int(arc4random_uniform(7))*50)
var circleWidth = CGFloat(100)
var circleHeight = circleWidth
var bgColor: UIColor = UIColor.init(rgbColorCodeRed: 230, green: 226, blue: 218, alpha: 1)
// Create a new CircleView
self.circleView = CircleView(frame: CGRect(x: self.view.frame.width/2, y: self.view.frame.height-110, width: circleWidth, height: circleHeight))
//let test = CircleView(frame: CGRect(x: diceRoll, y: 0, width: circleWidth, height: circleHeight))
cv = circleView
view.addSubview(circleView)
// Animate the drawing of the circle over the course of 1 second
circleView.animate(duration: 1)
let imageName = "ButtonBackground.png"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: self.view.frame.width/2, y: self.view.frame.height-110, width: 100, height: 100)
view.addSubview(imageView)
view.bringSubview(toFront: circleView)
//now you are able to call endAnimation function
self.circleView.endAnimate()
}
}

Why am I unable to animate my UIView's shadow out?

I want to animate out a shadow on my UIView, so that it starts visible, and by the end of the animation is invisible. I can do the opposite fine, but if I try to fade it out, the animation never occurs, in fact I never see a lick of shadow.
My code:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let foo = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
foo.backgroundColor = UIColor.greenColor()
applyShadowToView(foo)
view.addSubview(foo)
foo.layer.shadowOpacity = 1.0
let shadowAnimation = CABasicAnimation(keyPath: "shadowOpacity")
shadowAnimation.fromValue = 1.0
shadowAnimation.toValue = 0.0
shadowAnimation.duration = 0.5
shadowAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
foo.layer.addAnimation(shadowAnimation, forKey: "shadowOpacity")
foo.layer.shadowOpacity = 0.0
}
private func applyShadowToView(view: UIView) {
view.clipsToBounds = false
view.layer.shadowColor = UIColor(white: 0.4, alpha: 1.0).CGColor
view.layer.shadowOffset = CGSize(width: -5.0, height: 0.0)
view.layer.shadowRadius = 5.0
view.layer.shadowOpacity = 0.0
view.layer.shadowPath = UIBezierPath(rect: view.bounds).CGPath
view.layer.shouldRasterize = true
view.layer.rasterizationScale = UIScreen.mainScreen().scale
}
What am I doing wrong here exactly?

Resources