The code you see below creates a CALayer (rectangle shape) and animates it from the left to the right when the user holds down on the screen ('longPressGestureRecognizer'). When they lift their finger, the CALayer stops animating, it gets pushed into an array, and when they hold on the screen again another CALayer is created. You can copy and paste code directly in new project:
//Global Variables
var layer: CALayer?
var holdGesture = UILongPressGestureRecognizer()
let animation = CABasicAnimation(keyPath: "bounds.size.width")
var layerHolder = [CALayer]()
var widthIndex = CGPoint(x: 0, y: 0)
var nextXOffset = CGFloat(0.0)
var checkIfFull = CGFloat()
var colorIndex : Int = 0
let barColors = [
//Red
UIColor(red: 0.969, green: 0.49, blue: 0.443, alpha: 1),
//Orange
UIColor(red: 0.984, green: 0.647, blue: 0.431, alpha: 1),
//Pink
UIColor(red: 0.894, green: 0.592, blue: 0.698, alpha: 1),
//Purple
UIColor(red: 0.851, green: 0.6, blue: 0.957, alpha: 1),
//Yellow
UIColor(red: 0.98, green: 0.875, blue: 0.455, alpha: 1),
//Green
UIColor(red: 0.49, green: 0.792, blue: 0.616, alpha: 1),
//Blue
UIColor(red: 0.553, green: 0.71, blue: 0.906, alpha: 1)]
func setUpView(){
self.view.addGestureRecognizer(holdGesture)
holdGesture.addTarget(self, action:"handleLongPress:")
}
func handleLongPress(sender : UILongPressGestureRecognizer){
if(sender.state == .Began) {
let newLayer = CALayer()
newLayer.frame = CGRect(x: nextXOffset, y: 0, width: 0, height: 10)
newLayer.backgroundColor = barColors[colorIndex % 7].CGColor
print("before \(nextXOffset)")
newLayer.anchorPoint = widthIndex
animation.fromValue = 0
animation.toValue = self.view.bounds.width * 2 - nextXOffset
animation.duration = 5
self.view.layer.addSublayer(newLayer)
print("Long Press Began")
newLayer.addAnimation(animation, forKey: "bounds.size.width")
layer = newLayer
}
else {
print("Long press ended")
if let layer = layer {
print("after \(nextXOffset)")
pauseLayer(layer)
layer.frame = layer.presentationLayer()!.frame
nextXOffset = CGRectGetMaxX(layer.frame)
layerHolder.append(layer)
colorIndex++
}
}
}
My issue is that when the new CALayer is created and animating, it grows from both sides not just growing to the right. I need them to be side by side to each other, one moving to the right where the other one left off. I didn't notice it until i had them interchange colors. After some research, i believe setting an anchor point from where the last CALayer left off is the correct approach. Ive been trying to achieve this but haven't been able to do it. Help me? Please?
UPDATE : I believe if I was to use the nextXOffset as the anchor point, I may be able to do this, but it is of type CGfloat not CGPoint. Help please!
This works, Im so dumb. Keeping the 'widthIndex' at (0,0) allows it to start where the last bar left off. The code works.
Related
I drag my labels to my self.view in storyboard, you can see the white labels, in the gray vc, I am sorry for the tiny distinction to found :
Then I add a gradual layer use code :
let fromColor = UIColor.init(red: 243/255.0, green: 143/255.0, blue: 30/255.0, alpha: 1).cgColor
let centerColor = UIColor.init(red: 237/255.0, green: 90/255.0, blue: 36/255.0, alpha: 1).cgColor
let toColor = UIColor.init(red: 233/255.0, green: 28.0/255.0, blue: 36/255.0, alpha: 1).cgColor
gradul_layer = CAGradientLayer.init()
gradul_layer?.frame = CGRect.init(x: 0, y: 64, width: initFrame.width, height: initFrame.height)
gradul_layer?.colors = [
fromColor,
centerColor,
toColor
]
gradul_layer?.startPoint = CGPoint.init(x: 0.5, y: 0.3)
gradul_layer?.endPoint = CGPoint.init(x: 0.5, y: 0.7)
self.view.layer.addSublayer(gradul_layer!)
And then when I run my app in simulator (or device) can not show the label, but I can capture the View Hierarchy in Xcode, and I can see the label.
You see the picture, left is simulator(the labels did not show), right is the capture View Hierarchy(the labels shows up).
Update
My question is not how to let the label show in my screen, my doubt is why View Hierarchy show, my device or simulator will not.
I currently have a single gradient color background (see below), but I want to gradually change the colors to shades that I will pre-define in an array and have it loop through. How can I achieve this?
var colorTop = UIColor(red: 255.0/255.0, green: 149.0/255.0, blue: 0.0/255.0, alpha: 1.0).cgColor
var colorBottom = UIColor(red: 255.0/255.0, green: 94.0/255.0, blue: 58.0/255.0, alpha: 1.0).cgColor
func configureGradientBackground(colors:CGColor...){
let gradient: CAGradientLayer = CAGradientLayer()
let maxWidth = max(self.view.bounds.size.height,self.view.bounds.size.width)
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 1)
let squareFrame = CGRect(origin: self.view.bounds.origin, size: CGSize(width: maxWidth, height: maxWidth * 0.935))
gradient.frame = squareFrame
gradient.colors = colors
view.layer.insertSublayer(gradient, at: 0)
}
override func viewDidLoad() {
super.viewDidLoad()
configureGradientBackground(colors: colorTop, colorBottom)
}
Really quite straightforward; I can't imagine what difficulty you are having. CAGradientLayer's colors property is itself animatable, so you simply animate the change of colors and, when the animation ends, proceed to the next animation of the change of colors.
In this example I alternate between just two sets of colors, yours and blue-and-green. So my state is a simple Bool. But obviously the example could be extended to any number of states, that is, any number of pairs of colors.
let colorTop = UIColor(red: 255.0/255.0, green: 149.0/255.0, blue: 0.0/255.0, alpha: 1.0).cgColor
let colorBottom = UIColor(red: 255.0/255.0, green: 94.0/255.0, blue: 58.0/255.0, alpha: 1.0).cgColor
var state = false
var grad : CAGradientLayer?
override func viewDidLoad() {
// configure the gradient layer, start the animation
}
func animate() {
let arr = self.state ? [colorTop,colorBottom] : [UIColor.green.cgColor,UIColor.blue.cgColor]
self.state = !self.state
let anim = CABasicAnimation(keyPath: "colors")
anim.duration = 10 // or whatever
anim.fromValue = self.grad!.colors
anim.toValue = arr
anim.delegate = self
self.grad?.add(anim, forKey: nil)
DispatchQueue.main.async {
CATransaction.setDisableActions(true)
self.grad?.colors = arr
}
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
DispatchQueue.main.async {
self.animate()
}
}
You could have a variable in your viewDidLoad that is an int that keeps track of the index of the array that contains all your colors. For example, 0 is your first color, 1 is your second and so on. Then in your infinite while loop (also in the viewDidLoad), you can write:
if tracker == 0 {
// apply first color
} else if tracker == 1{
// apply second color
} ...
Then you can increment the tracker by one at the end and then check your if your tracker is past the size of your array (for example, you only have 11 colors).
tracker += 1
if tracker > 10 {
tracker = 0
}
How to make changing gradient background like in Instagram Log In?
In Android you do it like this:
Is there a similar way to do it in Swift?
If you want to change continuously change view's background color with animation you can set it something like this.
var colors: [UIColor] = [.red, .blue, .green]
func startAnimation(index: Int) {
UIView.animate(withDuration: 2.5, delay: 0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
self.view.backgroundColor = self.colors[index]
}) { (finished) in
var currentIndex = index + 1
if currentIndex == self.colors.count { currentIndex = 0 }
self.startAnimation(index: currentIndex)
}
}
And call this function with index 0.
self.startAnimation(index: 0)
In Swift3
Use CAGradientLayer with Using CABasicAnimation
I create PastelView Library to make alike instgram background animation effect
repo https://github.com/cruisediary/Pastel
Colors
var colors: [UIColor] = [UIColor(red: 156/255, green: 39/255, blue: 176/255, alpha: 1.0),
UIColor(red: 255/255, green: 64/255, blue: 129/255, alpha: 1.0),
UIColor(red: 123/255, green: 31/255, blue: 162/255, alpha: 1.0),
UIColor(red: 32/255, green: 76/255, blue: 255/255, alpha: 1.0),
UIColor(red: 32/255, green: 158/255, blue: 255/255, alpha: 1.0),
UIColor(red: 90/255, green: 120/255, blue: 127/255, alpha: 1.0),
UIColor(red: 58/255, green: 255/255, blue: 217/255, alpha: 1.0)]
Set Gradient
let gradient = CAGradientLayer()
gradient.frame = bounds
gradient.colors = currentGradientSet()
gradient.startPoint = CGPoint(x:0, y:1)
gradient.endPoint = CGPoint(x:1, y:0)
gradient.drawsAsynchronously = true
layer.insertSublayer(gradient, at: 0)
Add Animation to gradient
let animation = CABasicAnimation(keyPath: Animation.keyPath)
animation.duration = animationDuration
animation.toValue = currentGradientSet()
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
animation.delegate = self //this is important
gradient.add(animation, forKey: Animation.key)
After end animation loop another color set looping
extension PastelView: CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if flag {
gradient.colors = currentGradientSet()
animateGradient()
}
}
}
Pastel should help you :)
You can do it pretty easily using CAGradientLayer:
let grad = CAGradientLayer()
grad.colors = [
UIColor.red.cgColor, // top color
UIColor.blue.cgColor // bottom color
]
grad.locations = [
0.0, // start gradating at top of view
1.0 // end gradating at bottom of view
]
grad.frame = view.bounds
view.layer.addSublayer(grad)
You could increase the first item in locations (0.0) to start the gradating further down, or you could decrease 1.0 to complete gradation higher up. It's a bit tough to explain, drop this into a playground and mess around a bit until you get your desired effect.
Example gradient and playground from the code above:
You can do like this in SWIFT 3. You can give more colors in it.
let gradient1 = CAGradientLayer()
gradient1.frame = CGRect(origin: CGPoint.zero , size: CGSize (width: self.view.frame.size.width,height: self.view.frame.size.height))
gradient1.colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
print(gradient1.frame)
view_gradient.layer.masksToBounds = true
view_gradient.layer.addSublayer(gradient1)
Hope this works for you.
let gradient: CAGradientLayer = CAGradientLayer()
let colorTop = UIColor(red: 112.0/255.0, green: 219.0/255.0, blue: 155.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 86.0/255.0, green: 197.0/255.0, blue: 238.0/255.0, alpha: 1.0).CGColor
gradient.colors = [colorTop, colorBottom]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.frame = loginButton.bounds
gradient.cornerRadius = 5
loginButton.layer.addSublayer(gradient)
The resulting gradient runs off goes beyond the button's frame. Why does this happen?
Probably you are setting gradient layer in viewDidLoad() or viewWillAppear(). At that time controller did not calculated views sizes. At time you add this sublayer button size was not calculated yet, so sublayer size is wrong. So you should fix next things:
First of all you should add gradient at viewDidAppear(), at this point all view's sizes are calculated.
Second, you should use layer.insertSublayer(layer, atIndex:index) instead of addSublayer(layer). Because in your case sublayer will hide buttons native layer (title, background...)
You should recalculate your sublayer size in viewDidLayoutSubviews(). Because when your button will change it's size (while rotating for example), sublayer will not change it's frame, so you should change it by yourself.
Add clipsToBounds and cornerRadius to loginbutton. That should fix the problem.
loginButton.clipsToBounds = true
loginButton.layer.cornerRadius = 5;
Is your Login Button's frame correct?It seems correct when I reproduce
let loginButton = UIButton(frame: CGRect(x: 10, y: 50, width: 300, height: 30))
self.view.addSubview(loginButton)
let gradient:CAGradientLayer = CAGradientLayer()
let colorTop = UIColor(red: 112.0/255.0, green: 219.0/255.0, blue: 155.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 86.0/255.0, green: 197.0/255.0, blue: 238.0/255.0, alpha: 1.0).CGColor
gradient.colors = [colorTop, colorBottom]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.frame = loginButton.bounds
gradient.cornerRadius = 5
loginButton.layer.addSublayer(gradient)
And it appear like below
I have done gradient coloring before for changing color of my parent view now I am using same method to change the background color of UITextField but its not working.
This is the method I used for my view:
func setGradientBackgroundOfView()
{
// assigning gradient color to background View
let endingColorOFGradient = UIColor(colorLiteralRed: 133/255, green: 210/255, blue: 230/255, alpha: 1.0).CGColor
let startingColorOfGradient = UIColor(colorLiteralRed: 50/255, green: 189/255, blue: 170/255, alpha: 1.0).CGColor
gradient.startPoint = CGPoint(x: 0.5, y: 0.0)
gradient.endPoint = CGPoint(x: 0.5, y: 1.0)
gradient.colors = [startingColorOfGradient , endingColorOFGradient]
self.view.layer.insertSublayer(gradient, atIndex: 0)
}
And this method for setting layer:
override func viewWillLayoutSubviews() {
gradient.frame = self.view.bounds
}
I am calling my setGradientBackgroundOfView() in viewDidLoad() and it's working perfect but if I change
self.view.layer.insertSublayer(gradient, atIndex: 0)
with
self.textFieldUserName.layer.insertSublayer(gradient, atIndex: 0)
it does not work. What am I missing here?