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
Related
The following code effectively animates and scales a view along a BezierPath but I feel like I'm cheating since I'm mixing UIKit and CAKeyframeAnimation (Core Animation) animations.
Is there a better way to scale a view while animating it along the BezierPath?
func animateIt(){
let myView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
myView.backgroundColor = .blue
myView.layer.cornerRadius = myView.frame.height / 2
view.addSubview(myView)
let startPoint = CGPoint(x: 250, y: 500)
let apexPoint = CGPoint(x: 100, y: 50)
let endPoint = CGPoint(x: 50, y: 350)
let durationTime = 1.5
let path = UIBezierPath()
path.move(to: startPoint)
path.addQuadCurve(to: endPoint, controlPoint: apexPoint)
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = path.cgPath
animation.duration = durationTime
myView.layer.add(animation, forKey: "bezier")
myView.center = endPoint
UIView.animate(withDuration: durationTime, animations: {
myView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5);
})
}
I tried using a second CAKeyframeAnimation animation instead of the UIKit animation and used the array of key values but no matter what I do, I cannot get a uniform scale, it scales at the beginning but not at the end.
let scaleAnimation = CAKeyframeAnimation(keyPath: "scale")
scaleAnimation.values = [1.0, 0.9, 0.5,]
myView.layer.add(scaleAnimation, forKey: "scale")
Thanks!
I am Attempting to use FBShimmer cocoa pod on iOS but not finding Foundation framework.PodFile shown here
I ended up just using a UIView extension to achieve the same effect. Much less code and no external dependency.
func startShimmering() {
let light = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).cgColor
let dark = UIColor.white.cgColor
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [dark, light, dark]
gradient.frame = CGRect(x: -self.bounds.size.width, y: 0, width: 3*self.bounds.size.width, height: self.bounds.size.height)
gradient.startPoint = CGPoint(x: 0.0, y: 0.4)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.locations = [0.4, 0.5, 0.6]
self.layer.mask = gradient
let animation: CABasicAnimation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [0.0, 0.1, 0.2]
animation.toValue = [0.8, 0.9, 1.0]
animation.duration = 3.0
animation.repeatCount = HUGE
gradient.add(animation, forKey: "shimmer")
}
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];
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.
I have a large text and want to add an effect that is similar to the slide to unlock one of the latest iOS and that will go from top to bottom with the text sliding from bottom to top.
Update with swift shimmering label, view etc...
import UIKit
import QuartzCore
class ViewController: UIViewController {
#IBOutlet var testLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.testLabel.startShimmering()
}
}
extension UIView {
func startShimmering() {
// let light = UIColor.init(white: 0, alpha: 0.1).cgColor
let light = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor
let dark = UIColor.black.cgColor
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [dark, light, dark]
gradient.frame = CGRect(x: -self.bounds.size.width, y: 0, width: 3*self.bounds.size.width, height: self.bounds.size.height)
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.525)
gradient.locations = [0.4, 0.5, 0.6]
self.layer.mask = gradient
let animation: CABasicAnimation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [0.0, 0.1, 0.2]
animation.toValue = [0.8, 0.9, 1.0]
animation.duration = 1.5
animation.repeatCount = HUGE
gradient.add(animation, forKey: "shimmer")
}
func stopShimmering() {
self.layer.mask = nil
}
}
I suggest you use Grant Pauls's Shimmer control, because it does just that.
https://github.com/facebook/Shimmer