Turn rectangular UIImageView into a circle - ios

My ViewController has an UIImageView (lets name it img) as atribute, and in the beginning, it shows img in full screen, that is, its size is a rectangle similar to the ViewController bounds.
I'd like to do an animation with img, making it become a little circle (not just a rectangle with circular corners) and then move it to some corner of the ViewController.
How can I achieve this? I prefer not to send any code cos Im pretty sure I did it the wrong way (I tried to turn it into a square and then set a cornerRadius (with radius width/2), then making scale and translating position at once, but its a mess and works wrongly, so lets forget it)

Your solution sounds fine to me, but what might be going wrong, is the fact that animating corner radius is not supported by UIView.animateWithDuration as seen in the View Programming Guide for iOS. Therefor, the results are sometimes not whats expected.
You can try out the following code.
func animate() {
//Some properties that needs to be set on UIImageView
self.imageView.clipsToBounds = true
self.imageView.contentMode = .ScaleAspectFill
//Create animations
let cornerRadiusAnim = changeCornerRadiusAnimation()
let squareAnim = makeSquareAnimation()
let boundsAnim = changeBoundsAnimation()
let positionAnim = changePositionAnimation(CGPointMake(300,480))
//Use group for sequenced execution of animations
let animationGroup = CAAnimationGroup()
animationGroup.animations = [cornerRadiusAnim, squareAnim, boundsAnim, positionAnim]
animationGroup.fillMode = kCAFillModeForwards
animationGroup.removedOnCompletion = false
animationGroup.duration = positionAnim.beginTime + positionAnim.duration
imageView.layer.addAnimation(animationGroup, forKey: nil)
}
func makeSquareAnimation() -> CABasicAnimation {
let center = imageView.center
let animation = CABasicAnimation(keyPath:"bounds")
animation.duration = 0.1;
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = NSValue(CGRect: imageView.frame)
animation.toValue = NSValue(CGRect: CGRectMake(center.x,center.y,imageView.frame.width,imageView.frame.width))
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
return animation
}
func changeBoundsAnimation() -> CABasicAnimation {
let animation = CABasicAnimation(keyPath:"transform.scale")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = imageView.layer.mask?.valueForKeyPath("transform.scale")
animation.toValue = 0.1
animation.duration = 0.5
animation.beginTime = 0.1
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
return animation
}
func changeCornerRadiusAnimation() -> CABasicAnimation {
let animation = CABasicAnimation(keyPath:"cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = 0
animation.toValue = imageView.frame.size.width * 0.5
animation.duration = 0.1
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
return animation
}
func changePositionAnimation(newPosition: CGPoint) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = NSValue(CGPoint: imageView.layer.position)
animation.toValue = NSValue(CGPoint: newPosition)
animation.duration = 0.3
animation.beginTime = 0.6
animation.fillMode = kCAFillModeForwards
animation.removedOnCompletion = false
return animation
}

// try like this, i hope it will work for you.
UIView.animateWithDuration(3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: { () -> Void in
img.layer.masksToBounds = true
img.layer.cornerRadius = img.frame.size.height / 2 // here img must be in square shape (height == width)
}) { (finished : Bool) -> Void in
}

option 1 call below method from code
// UIimageView Circle
class func roundImageView(imageView:UIImageView){
imageView.layer.borderWidth = 1.0
imageView.layer.masksToBounds = false
//imageView.layer.borderColor = UIColor.whiteColor().CGColor
imageView.layer.cornerRadius = imageView.frame.size.width/2
imageView.clipsToBounds = true
}
option 2 make user defined run time attributes from storyboard
view.layer.cornerRadius = 5.0f;
view.layer.masksToBounds = NO;

Related

Animating SCNCamera.fStop with property key path doesn't work, only inside implicit SCNTransaction

This code strangely doesn't animate the fStop:
cameraNode.camera!.wantsDepthOfField = true
cameraNode.camera!.focusDistance = 2
let animation = CABasicAnimation(keyPath: "fStop")
animation.toValue = 0.5
animation.duration = 0.3
cameraNode.camera!.addAnimation(animation, forKey: nil)
while this one does:
SCNTransaction.begin()
SCNTransaction.animationDuration = 0.3
cameraNode.camera!.fStop = 0.5
SCNTransaction.commit()
Why?
The animation gets removed on completion so the fStop is reset to its initial value. You just need to set the final value on it when you start the animation.
let animation = CABasicAnimation(keyPath: "fStop")
animation.fromValue = cameraNode.camera!.fStop
animation.toValue = 0.5
animation.duration = 0.3
cameraNode.camera!.addAnimation(animation, forKey: nil)
cameraNode.camera!.fStop = 0.5

Animate shadow properties

I want to animate shadow properties for a view. Initially I tried:
func animateIn(delay: TimeInterval = 0) {
UIView.animate(withDuration: 0.2, delay: delay, options: .allowUserInteraction, animations: {
self.shadowView.layer.shadowColor = UIColor.black.cgColor
self.shadowView.layer.shadowOffset = CGSize(width: 15, height: 15)
self.shadowView.layer.shadowRadius = 20
self.shadowView.layer.shadowOpacity = 0.2
self.layoutIfNeeded()
}, completion: nil)
}
which doesnt work given that UIView.animate doesnt work with layers.
I tested the following post (How to animate layer shadowOpacity?), but it only specifies how to animate the shadow Opacity. Is there any way to animate all my shadow properties like I would with an animation blocl?
You are very close, you just create multiple CABasicAnimations for all the property changes and then you can use CAAnimationGroup
So let say you have 2 CABasicAnimation animations like:
let shadownOpacityAnimation = CABasicAnimation(keyPath: "shadowOpacity")
...
let shadowRadiusAnimation = CABasicAnimation(keyPath: "shadowRadius")
...
Then you can add them using CAAnimationGroup
let group = CAAnimationGroup()
group.duration = 0.2
group.repeatCount = 1
group.autoreverses = false
group.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
group.animations = [shadownOpacityAnimation, shadowRadiusAnimation]
layer.add(group, forKey: "allAnimations")
You can read more about it in this answer:
How can I create an CABasicAnimation for multiple properties?

Swift Change circular border color with animation

I created a circular button using swift, what I am trying to do is changing the border color with animation
I used the following code :
let color = CABasicAnimation(keyPath: "borderColor")
color.fromValue = UIColor.black.cgColor
color.toValue = UIColor.red.cgColor
color.duration = 1
color.repeatCount = 1
sender.layer.add(color, forKey: "color and width")
The border color is changing but it is not given the desire effect, it change the border color all at once. What I would like to have as effect is like your drawing over the old color, to keep it simple ==> like a progress bar where u see the old color like fade away and be replaced by the new color.
Is there is any way to do that?
Thanks for helping.
Updating code
var storkeLayer = CAShapeLayer()
#IBOutlet weak var Mybut: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
storkeLayer.fillColor = UIColor.clear.cgColor
storkeLayer.strokeColor = UIColor.black.cgColor
storkeLayer.lineWidth = 2
// Create a rounded rect path using button's bounds.
storkeLayer.path = CGPath.init(roundedRect: Mybut.bounds, cornerWidth: Mybut.frame.width / 2 , cornerHeight: Mybut.frame.height / 2, transform: nil)
// Add layer to the button
Mybut.layer.addSublayer(storkeLayer)
}
#IBAction func bytton(_ sender: UIButton) {
storkeLayer.fillColor = UIColor.clear.cgColor
storkeLayer.strokeColor = UIColor.red.cgColor
storkeLayer.lineWidth = 2
// Create a rounded rect path using button's bounds.
storkeLayer.path = CGPath.init(roundedRect: Mybut.bounds, cornerWidth: Mybut.frame.width / 2 , cornerHeight: Mybut.frame.height / 2, transform: nil)
// Add layer to the button
sender.layer.addSublayer(storkeLayer)
// Create animation layer and add it to the stroke layer.
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = CGFloat(0.0)
animation.toValue = CGFloat(1.0)
animation.duration = 1
animation.fillMode = kCAFillModeForwards
// animation.fillMode = kCAFillModeBackwards
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
storkeLayer.add(animation, forKey: "circleAnimation")
}
You can stroke the button's path using the following code snippet.
#IBAction func buttonTapped(_ sender: UIButton) {
let storkeLayer = CAShapeLayer()
storkeLayer.fillColor = UIColor.clear.cgColor
storkeLayer.strokeColor = UIColor.red.cgColor
storkeLayer.lineWidth = 2
// Create a rounded rect path using button's bounds.
storkeLayer.path = CGPath.init(roundedRect: sender.bounds, cornerWidth: 5, cornerHeight: 5, transform: nil) // same path like the empty one ...
// Add layer to the button
sender.layer.addSublayer(storkeLayer)
// Create animation layer and add it to the stroke layer.
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = CGFloat(0.0)
animation.toValue = CGFloat(1.0)
animation.duration = 1
animation.fillMode = kCAFillModeForwards
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
storkeLayer.add(animation, forKey: "circleAnimation")
}
This snippet creates something like this:

CAKeyframeAnimation of "transform" is not animated alongside "opacity" in a CAAnimationGroup

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()

Spinning objects in iOS

How do I make an object, for example an image, rotate forever?
UIView.animateWithDuration(2.0, animations: ({
self.Cube.transform = CGAffineTransformMakeRotation(320)
}))
Does anyone have a solution, would really appreciate some help.
Try this function to rotate a layer forever.
func rotateLayer(layer:CALayer,duration:CFTimeInterval){
let animation = CABasicAnimation(keyPath: "transform.rotation");
animation.fromValue = 0;
animation.duration = duration;
animation.toValue = CGFloat(M_PI*2);
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear);
animation.repeatCount = Float.infinity;
layer.addAnimation(animation, forKey: "rotation");
}
usage (the duration is time it takes the layer to rotate 360 degress):
rotateLayer(subView.layer, duration: 2);

Resources