This is my animation method (PreloaderView.swift)
public func performAction(action: PreloaderAction)
{
let highOpacity = CABasicAnimation.init(keyPath: "opacity")
let middleOpacity = CABasicAnimation.init(keyPath: "opacity")
let lowOpacity = CABasicAnimation.init(keyPath: "opacity")
if action == .PreloaderActionNone {
self.thirdShape.removeAllAnimations()
self.firstShape.opacity = 0.3;
self.secondShape.opacity = 0.5
self.thirdShape.opacity = 1.0
} else if action == .PreloaderActionFailure {
UIView.animateWithDuration(2, animations: {
self.thirdShape.strokeColor = UIColor.redColor().CGColor
}) {(completed : Bool) in
self.thirdShape.strokeColor = self.ringColor.CGColor
}
}
if action == .PreloaderActionStart {
highOpacity.fromValue = 0.0
highOpacity.toValue = 1.0
highOpacity.duration = animationDuration
highOpacity.autoreverses = true
highOpacity.repeatCount = .infinity
self.secondShape.addAnimation(highOpacity, forKey: "highOpacity")
middleOpacity.fromValue = 0.0
middleOpacity.toValue = 1.0
middleOpacity.duration = animationDuration
middleOpacity.autoreverses = true
middleOpacity.repeatCount = .infinity
middleOpacity.beginTime = CACurrentMediaTime() + 0.33
self.firstShape.addAnimation(middleOpacity, forKey: "middleOpacity")
lowOpacity.fromValue = 0.0
lowOpacity.toValue = 1.0
lowOpacity.duration = animationDuration
lowOpacity.autoreverses = true
lowOpacity.beginTime = CACurrentMediaTime() + 0.66
lowOpacity.repeatCount = .infinity
self.thirdShape.addAnimation(lowOpacity, forKey: "lowOpacity")
}
}
This is my playground file
and playground in project navigator
Animations work fine on device, but when I use playground animations not working.
Xcode 7, Swift 2
Add
import XCPlayground
at the top of the Playground, then use
XCPlaygroundPage.currentPage.liveView = view
at the bottom of the Playground, where view is the view you want to render.
You also have to open the "Assistant Editor" (in the "View" menu) to see the current live view.
Also, if you're using asynchronous operations, you have to include
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
in order to use them in a Playground.
Xcode 8, Swift 3
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = view
Related
I would simply like to have a pulsating UIView. For that I have set up this:
let highlightView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .lightBlueCustom
v.alpha = 0.9
return v
}()
let scaleAnimation: CABasicAnimation = {
let v = CABasicAnimation(keyPath: "transform.scale")
v.duration = 0.5
v.repeatCount = .infinity
v.autoreverses = true
v.fromValue = 1.0
v.toValue = 1.4
return v
}()
And this is how I call it:
func showHighlightView(viewToHighlight: UIView, height: CGFloat) {
self.view.addSubview(highlightView)
highlightView.heightAnchor.constraint(equalTo: viewToHighlight.heightAnchor).isActive = true
highlightView.widthAnchor.constraint(equalTo: highlightView.heightAnchor).isActive = true
highlightView.centerXAnchor.constraint(equalTo: viewToHighlight.centerXAnchor).isActive = true
highlightView.centerYAnchor.constraint(equalTo: viewToHighlight.centerYAnchor).isActive = true
highlightView.layer.cornerRadius = height/2
highlightView.layer.add(self.scaleAnimation, forKey: "scale")
self.view.bringSubviewToFront(viewToHighlight)
self.view.bringSubviewToFront(highlightView)
}
func showWishIntro() {
showHighlightView(viewToHighlight: self.addWishButton, height: 60)
}
But this is not working. It shows the highlightView correctly but there is no animation. What am I missing here?
ok so I fixed it... it was just a lucky guess but I am calling showWishIntro now in viewDidLayoutSubviews and not it is working as expected. I have no idea why, so if anyone would care to explain just let me know :D I'm happy that it works.
This code worked perfectly in previous version of Xcode, but now it doesn't show anything. It is only showing activity indicator that says "Running project"
import UIKit
import PlaygroundSupport
let container = UIView()
container.frame.size = CGSize(width: 215.0, height: 215.0)
var view = UIView()
view.frame.size = CGSize(width: 185.0, height: 185.0)
view.center = container.center
view.layer.cornerRadius = view.frame.size.height/2
view.backgroundColor = UIColor.white
container.addSubview(view)
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.fromValue = CGFloat(0.6)
scaleAnimation.toValue = CGFloat(1.15)
scaleAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
scaleAnimation.isRemovedOnCompletion = false
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
opacityAnimation.fromValue = CGFloat(0.0)
opacityAnimation.toValue = CGFloat(0.1)
opacityAnimation.duration = 1
opacityAnimation.isRemovedOnCompletion = false
opacityAnimation.autoreverses = true;
let circleAnimations: CAAnimationGroup = CAAnimationGroup()
circleAnimations.duration = 2
circleAnimations.repeatCount = HUGE;
circleAnimations.animations = [scaleAnimation, opacityAnimation];
view.layer .add(circleAnimations, forKey: "circleAnimations")
PlaygroundPage.current.liveView = container
PlaygroundPage.current.needsIndefiniteExecution = true
What is wrong with it ?
You need to enable the assistant editor to see the results.
View > Assistant Editor > Show Assistant Editor
Also see here:
iOS Playground doesn't show UI preview
Sometimes closing the playground and restarting Xcode resolves the problem
I'd like to create a CASpringAnimation for rotation that ends where it began. Here's some working code that doesn't end where it began (but otherwise is what I'm looking for):
func spring(view: UIView) -> CASpringAnimation{
let animation = CASpringAnimation(keyPath: "transform")
animation.damping = 0.5
animation.initialVelocity = 70
animation.mass = 0.04
animation.stiffness = 12
animation.duration = 1.2
animation.toValue = CATransform3DRotate(CATransform3DMakeAffineTransform(view.transform),
CGFloat(M_PI)/32, // Must be non-zero 😕
0, 0, 1)
return animation
}
And here's how you'd use it:
let animation = spring(view: viewToAnimate)
viewToAnimate.layer.add(animation, forKey: nil)
When I set the angle (beside the comment in CATransform3DRotate) to 0, the animation doesn't work! Any ideas on how to get a spring rotation with the same start and end angles? Thanks for reading.
By adding a CABasicAnimation that animates to 0° over the same time duration, and also by tweaking the spring animation parameters, I've got something that looks pretty good (tested for iOS 10):
#IBAction func springButtonTapped(_ sender: UIButton) { // Attach to the view you'd like animated
let springAnim = spring(view:sender)
sender.layer.add(springAnim, forKey: nil)
let correctionAnim = correct(view:sender)
sender.layer.add(correctionAnim, forKey: nil)
}
func correct(view: UIButton) -> CABasicAnimation{
let animation = CABasicAnimation(keyPath: "transform")
animation.duration = 0.3
animation.toValue = CATransform3DRotate(CATransform3DMakeAffineTransform(view.transform),
0,
0, 0, 1)
return animation
}
func spring(view: UIView) -> CASpringAnimation{
let animation = CASpringAnimation(keyPath: "transform")
animation.damping = 0.0
animation.initialVelocity = 80
animation.mass = 0.04
animation.stiffness = 50
animation.duration = 0.3
animation.toValue = CATransform3DRotate(CATransform3DMakeAffineTransform(view.transform),
CGFloat(M_PI)/64,
0, 0, 1)
return animation
}
What would be the reason for CAKeyframeAnimation objects to not be able to animate simultaneously in a CAAnimationGroup, but it does if being added individually to a CALayer?
A code snippet to describe this:
// The following is a part of CALayer extension function
let quadAnimation = CAKeyframeAnimation(keyPath: "transform")
quadAnimation.duration = 1.0
quadAnimation.repeatCount = 1
quadAnimation.removedOnCompletion = false
quadAnimation.fillMode = kCAFillModeForwards
quadAnimation.beginTime = CACurrentMediaTime()
quadAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.743, 0, 0.833, 0.833)
// Note:
// AGK-based methods come from a geometry manipulation library https://github.com/agens-no/AGGeometryKit
//
var values: [NSValue] = []
let numberOfFrames = 30
for frame in 0..<numberOfFrames {
let p = AGKEaseOutWithBezier(CGFloat(frame) / CGFloat(numberOfFrames))
let quad = AGKQuadInterpolate(quad1, quad2, CGFloat(p))
let innerQuad = AGKQuadMove(quad, -self.position.x, -self.position.y)
let transform = CATransform3DWithAGKQuadFromBounds(innerQuad, CGRect(origin: CGPoint.zero, size: self.boundsSize))
let value = NSValue(CATransform3D: transform)
values += [value]
}
quadAnimation.values = values
let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity")
opacityAnimation.keyTimes = [0, 1]
opacityAnimation.values = [0, 1]
opacityAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.167, 0.167, 0.833, 0.833)
opacityAnimation.duration = 0.5
opacityAnimation.beginTime = CACurrentMediaTime() + 0.5
opacityAnimation.fillMode = kCAFillModeForwards
let animationBlockDelegate = AGKCALayerAnimationBlockDelegate()
animationBlockDelegate.onStart = {
self.quadrilateral = quad2
}
let animationGroup = CAAnimationGroup()
animationGroup.animations = [quadAnimation, opacityAnimation]
animationGroup.duration = quadAnimation.duration + opacityAnimation.duration + 0.5
animationGroup.delegate = animationBlockDelegate
CATransaction.begin()
CATransaction.setCompletionBlock {
// Completion block handler
}
self.addAnimation(animationGroup, forKey: "transformOpacityAnimationKey")
CATransaction.commit()
The opacity will be animated given the right timingFunction and beginTime, but the CA3DTransform values are not animated at all.
All of the above works fine if I added the animation individually to the layer:
self.addAnimation(quadAnimation, forKey:"quadAnimation")
self.addAnimation(opacityAnimation, forKey: "opacityAnimation")
With each animation quadAnimation and opacityAnimation process delegated to AGKCALayerAnimationBlockDelegate()
NO UIVIEW ANIMATIONS, I'M LEARNING CORE ANIMATION
When I run this code, the top image is not on the screen. Then 4 seconds later, it reappears and does as expected. Not sure why. What I want is for the top image to be on the screen when the app launches, and then 4 seconds later for the top image to move up and out of the screen.
#IBOutlet var top: UIImageView!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let openTop = CABasicAnimation(keyPath: "position.y")
openTop.fromValue = self.top.frame.origin.y
openTop.toValue = -self.view.bounds.size.height
openTop.duration = 1.0
openTop.beginTime = CACurrentMediaTime() + 4
self.top.layer.addAnimation(openTop, forKey: nil)
self.top.layer.position.y = -self.view.bounds.size.height
}
Any thoughts?
Here's what I came up with:
let l = top.layer!
let startingPosition = l.position
l.position = CGPointMake( CGRectGetMidX( self.view.bounds ), -l.frame.height * 0.5 )
l.addAnimation({
let anim = CABasicAnimation( keyPath: "position" )
anim.fromValue = NSValue( CGPoint:startingPosition )
anim.beginTime = CACurrentMediaTime() + 4.0
anim.fillMode = kCAFillModeBoth
return anim
}(), forKey: nil)
so you want the image to be on-screen at the start, and then 4 seconds later to fly off the top? You can do this by grouping animations together
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
// keep a track of where we want it to be
let y = self.top.layer.position.y
// move it out of the way - this where it will end up after the animation
self.top.layer.position.y = -self.view.bounds.size.height
// do nothing, other than display the image for 4 seconds
let doNothing = CABasicAnimation(keyPath: "position.y")
doNothing.fromValue = y
doNothing.toValue = y
doNothing.duration = 4.0
// zip it away
let openTop = CABasicAnimation(keyPath: "position.y")
openTop.fromValue = y
openTop.toValue = -self.view.bounds.size.height
openTop.duration = 1.0
openTop.beginTime = doNothing.beginTime + doNothing.duration
// create the group
let group = CAAnimationGroup()
group.duration = 5.01
group.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
group.removedOnCompletion = false
group.fillMode = kCAFillModeForwards
group.animations = [doNothing, openTop]
self.top.layer.addAnimation(group, forKey: nil)
}