Custom animation for loading data - ios

Here I have been trying so long to create a custom animated progress bar.
I'm trying to move a view from screen's one end to second end with resizing.
view's frmae is (0, 100, 10, 4)
What I want is...
At start of animation view should be at its initial position, lets say at (0, 100)
in the middle of the animation view will be at the middle of the screen and its width will be increased to 100px.
at the end of the animation view will be at screen's second end. and frame of the view will be at, lets say (400, 100) and width off the view will be again back to 10px
The whole progress should be reversed also.
UIButton Action method
var mainView = UIView()
#IBAction func btnTapped(_ sender: UIButton) {
let rect = CGRect(x: 0, y: 300, width: 10, height: 4)
mainView = UIView(frame: rect)
mainView.backgroundColor = UIColor.black
self.view.addSubview(mainView)
animate()
}
Animation Method
func animate() {
let theAnimationTranslationX = CABasicAnimation(keyPath: "transform.translation.x")
theAnimationTranslationX.fromValue = mainView.bounds.minX
theAnimationTranslationX.toValue = 400
let scaleAnimate:CABasicAnimation = CABasicAnimation(keyPath: "transform.scale.x")
scaleAnimate.fromValue = NSValue(caTransform3D: CATransform3DMakeScale(1, 1, 1))
scaleAnimate.toValue = NSValue(caTransform3D: CATransform3DMakeScale(1.2, 0, 0))
let group = CAAnimationGroup()
group.autoreverses = true
group.repeatCount = .greatestFiniteMagnitude
group.animations = [theAnimationTranslationX]//, scaleAnimate]
group.duration = 2
group.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
mainView.layer.add(group, forKey: "animation")
}
I'm not getting the desired animation effect.
Is there any other way to do so or is it possible with any other way please suggest me.
Thanks for you time and any help will be appreciated.
EDIT:
The final animation should look like...

Related

Synchronously animate CALayer property and UIView with UIViewPropertyAnimator

I'm using a UIViewPropertyAnimator to animate the position of a view. But, I also want to animate CALayer properties like borderColor along with it. Currently, it doesn't animate and instantly changes at the start, like this:
Here's my code:
class ViewController: UIViewController {
var animator: UIViewPropertyAnimator?
let squareView = UIView(frame: CGRect(x: 0, y: 40, width: 80, height: 80))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(squareView)
squareView.backgroundColor = UIColor.blue
squareView.layer.borderColor = UIColor.green.cgColor
squareView.layer.borderWidth = 6
animator = UIViewPropertyAnimator(duration: 2, curve: .easeOut, animations: {
self.squareView.frame.origin.x = 100
self.squareView.layer.borderColor = UIColor.red.cgColor
})
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.animator?.startAnimation()
}
}
}
I looked at this question, How to synchronously animate a UIView and a CALayer, and the answer suggested using "an explicit animation, like a basic animation." It said,
If the timing function and the duration of both animations are the same then they should stay aligned.
However, if I use CABasicAnimation, I lose all the benefits of UIViewPropertyAnimator, like timing and stopping in the middle. I will also need to keep track of both. Is there any way to animate CALayer properties with UIViewPropertyAnimator?
CABasicAnimation has timing as well as keyframe animation to stop in the middle. But, to replicate your animation above:
squareView.layer.transform = CATransform3DMakeTranslation(100, 0, 0)
let fromValue = squareView.transform
let toValue = 100
let translateAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.transform))
translateAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
translateAnimation.fromValue = fromValue
translateAnimation.toValue = toValue
translateAnimation.valueFunction = CAValueFunction(name: .translateX)
let fromColor = squareView.layer.borderColor
let toColor = UIColor.red.cgColor
let borderColorAnimation = CABasicAnimation(keyPath: "borderColor")
borderColorAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
borderColorAnimation.fromValue = fromColor
borderColorAnimation.toValue = toColor
let groupAnimation = CAAnimationGroup()
groupAnimation.animations = [translateAnimation, borderColorAnimation]
groupAnimation.duration = 5
squareView.layer.add(groupAnimation, forKey: nil)

Why is my table view shadow scrolling with my table view?

I added shadow to my table view but unfortunately when I scroll through the table view the shadow also moves with the table. The code for adding shadow is as follows:
func addShadow(to myView: UIView){
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: uiView.frame.width, height: uiView.frame.height * 1.1))
uiView.layer.shadowColor = UIColor.black.cgColor
uiView.layer.shadowOffset = CGSize(width: 0.2, height: 0)
uiView.layer.shadowOpacity = 0.5
uiView.layer.shadowRadius = 5.0
uiView.layer.masksToBounds = false
uiView.layer.shadowPath = shadowPath.cgPath
}
Can you please explain to me why is this happening and how to make the shadow stick to its designated location?
Thank you in advance.
If you are adding shadow on tableView, it will scroll along with tableview data. To prevent that you have to add UIView first. Add tableview on that view. Add shadow for the UIView you have taken. It will stick to designated location.
This is my solution in Swift 3 with an UIView and a CAGradientLayer inside.
override func viewWillAppear(_ animated: Bool) {
addShadow(myView: myTab)
}
func addShadow(myView: UIView){
let shadowView = UIView()
shadowView.center = CGPoint(x: myView.frame.minX,y:myView.frame.minY - 15)
shadowView.frame.size = CGSize(width: myView.frame.width, height: 15)
let gradient = CAGradientLayer()
gradient.frame.size = shadowView.frame.size
let stopColor = UIColor.clear.cgColor
let startColor = UIColor.black.withAlphaComponent(0.8).cgColor
gradient.colors = [stopColor,startColor]
gradient.locations = [0.0,1.0]
shadowView.layer.addSublayer(gradient)
view.addSubview(shadowView)
}

Swift CATransition Between Two Views - Slide over effect previous screen turns white

I am trying to animate between two full screen views (same view controller) in swift using CATransition. Looking to replicate (or get close to) navigation view controller push transition. Running into issue where view2 (the new view to present) is sliding over correctly, but view1 (the view underneath) disappears. Basically the view that is being animated is sliding over a white screen, but I want it to slider over the previous screen.
var pushAnimationEffect = CATransition()
override func viewDidLoad() {
super.viewDidLoad()
pageToUIView[i] = UIView()
view1.frame = CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height)
view1.center = CGPointMake(screenSize.width/2, screenSize.height/2)
view2.frame = CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height)
view2.center = CGPointMake(screenSize.width/2, screenSize.height/2)
view1.layer.addAnimation(pushAnimationEffect, forKey: nil)
view2.layer.addAnimation(pushAnimationEffect, forKey: nil)
self.view.addSubview(view1)
pushAnimationEffect.delegate = self
pushAnimationEffect.duration = 0.4
pushAnimationEffect.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
}
func nextPage() {
pushAnimationEffect.type = kCATransitionMoveIn
pushAnimationEffect.subtype = kCATransitionFromRight
self.view.addSubview(view2)
view1.removeFromSuperview()
}
func prevPage() {
pushAnimationEffect.type = kCATransitionMoveIn
pushAnimationEffect.subtype = kCATransitionFromLeft
self.view.addSubview(view1)
view2.removeFromSuperview()
}
Simplified the code a bit so its easier to read.
Is there a better way to do this? Is there something wrong with the code?
You need to do the view remove on after the animation completes. Use the animationDidStop:finished: delegate method.

Animate UIImageView Diagonally

I'd like to be able to animate my UIImageView background in a diagonal fashion. Meaning the background would animate and scroll from the top left of the screen towards the bottom right of the screen. Ideally, this would be a continuous animation.
I have the directions figured out, however, what I have seems to take a copy of the background & animate it over a static background. This causes for a weird effect.
See gif:
Here is the code:
var imageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
self.setup();
}
func setup() {
self.imageView.frame = self.view.bounds
self.imageView.image = UIImage(named: "myimage.jpg")
self.view.addSubview(self.imageView)
self.scroll()
}
func scroll() {
var theImage = UIImage(named: "myimage.jpg")!
var pattern = UIColor(patternImage: theImage)
var layer = CALayer()
layer.backgroundColor = pattern.CGColor
layer.transform = CATransform3DMakeScale(1,-1,1)
layer.anchorPoint = CGPointMake(0, 1)
var viewSize = self.imageView.bounds.size
layer.frame = CGRectMake(0,0, theImage.size.width + viewSize.width, viewSize.height)
self.imageView.layer.addSublayer(layer)
var startPoint = CGPointZero
var endPoint = CGPointMake(theImage.size.width, theImage.size.height + 15)
var animation = CABasicAnimation(keyPath: "position")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = NSValue(CGPoint:startPoint)
animation.toValue = NSValue(CGPoint:endPoint)
animation.repeatCount = Float.infinity
animation.duration = 1.0
layer.addAnimation(animation, forKey: "position")
}
Is anyone able to figure out how to make the whole background scroll? Do i just need a larger image, and scroll that? If that is the case, what does the contentMode of the imageView need to be set at?
Thanks
---- Update
I've reworked the code a bit, and have a working version of what I wanted. I'm curious for some feedback, as I'm still not 100% comfortable with animations in general (but i'm learning! :) )
-- I removed the whole background image in general, as DuncanC suggested it wasn't serving a good purpose. Now, i'm just making an image layer, trying to make if sufficiently big, and looping the animation. Thoughts?
override func viewDidLoad() {
super.viewDidLoad()
self.scroll()
}
func scroll() {
var theImage = UIImage(named: "myimage.jpg")!
var pattern = UIColor(patternImage: theImage)
var layer = CALayer()
layer.backgroundColor = pattern.CGColor
layer.transform = CATransform3DMakeScale(1,-1,1)
layer.anchorPoint = CGPointMake(0, 1)
// i'm making the view large here so the animation can keep running smoothly without
// showing what is behind our image
// is there a better way to do this?
var viewSize = CGSizeMake(self.view.bounds.size.height * 10, self.view.bounds.size.width * 10)
layer.frame = CGRectMake(0,0, theImage.size.width + viewSize.width, viewSize.height)
self.view.layer.addSublayer(layer)
var startPoint = CGPointMake(-theImage.size.width, -theImage.size.height)
var endPoint = CGPointMake(0, 0)
var animation = CABasicAnimation(keyPath: "position")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = NSValue(CGPoint:startPoint)
animation.toValue = NSValue(CGPoint:endPoint)
animation.repeatCount = Float.infinity
animation.duration = 3.0
layer.addAnimation(animation, forKey: "position")
}
**Result:*
Your code doesn't make a lot of sense. You have an image view that contains the image myimage.jpg, and then you create a CALayer that contains that image as a pattern color. You add the layer to your image view and animate it's position.
Just use a UIView animation and animate the center of your image view directly. If you're using AutoLayout then you'll need to add horizontal and vertical position constraints, wire them up as IBOutlets, and then in your animation block change the constraints and call layoutIfNeeded() on the image view's superview.

Image overlaps previous view when tapping/swiping back

I have a UIScrollView which covers one of my views entirely. I have added a background image to this same view which scrolls at a slightly different rate to the actual UIScrollView. This works absolutely fine unless I use the back swipe gesture or tap the 'Back' button. What happens is the image covers the view for about 0.5 seconds before disappearing, and it looks pretty bad.
This is what I mean:
As you can see, that is mid way through the gesture, and rather than being able to see the previous view, you just see the part of the image that is off to the left. It doesn't happen on the first page of the UIScrollView so I guess it's because the image is overlapping the previous view.
Here is my code:
override func viewDidLoad() {
let pagesScrollViewSize = scrollView.frame.size
scrollView.contentSize = CGSize(width: pagesScrollViewSize.width * CGFloat(images.count), height: pagesScrollViewSize.height)
backgroundImageView.frame = CGRect(x: 0, y: 0, width: 2484, height: 736)
backgroundImageView.image = UIImage(named: "SF.png")
var visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .Light)) as UIVisualEffectView
visualEffectView.frame = backgroundImageView.bounds
backgroundImageView.addSubview(visualEffectView)
view.addSubview(backgroundImageView)
view.sendSubviewToBack(backgroundImageView)
scrollView.backgroundColor = UIColor.clearColor()
}
func scrollViewDidScroll(scrollView: UIScrollView) {
loadVisiblePages()
var factor = scrollView.contentOffset.x / (scrollView.contentSize.width - 414);
if factor < 0 {
factor = 0
}
if factor > 1 {
factor = 1
}
var frame: CGRect = backgroundImageView.frame
frame.origin.x = factor * (414 - backgroundImageView.frame.size.width)
backgroundImageView.frame = frame
}
Anyone have any suggestions?
You have to add the following in your viewDidLoad function:
self.view.clipsToBounds = true or scrollView.clipsToBounds = true if you just want to clip the subviews in your UIScrollView.
Setting this value to true causes subviews to be clipped to the bounds of the receiver. If set to false, subviews whose frames extend beyond the visible bounds of the receiver are not clipped. The default value is false.
From Apple' doc : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html#//apple_ref/occ/instp/UIView/clipsToBounds

Resources