Hallo vrienden, Leo Hier.
Today we will see how can you a cool animation using scale
and translate
in your projects. Knowing how to animate things gives live and vibrancy to your projects. Custom animations can be really hard and not intuitive if you are dealing with Transition Animations. But when you have to do simple animations that doesn't includes multiples ViewControllers everything tend to be easier.
Let's code! But first...
The Painting
This painting is called "Ballet With Magic" from Leonid Afremov . He was born 12 July 1955 in Vitebsk, Belarus - Died August 19th 2019 , Playa Del Carmen, Quintana Roo, Mexico. He is a Russian–Israeli modern impressionistic artist who works mainly with a palette knife and oils. He developed his own unique technique and style which is unmistakable and cannot be confused with other artists.
Afremov is mainly known as being a self-representing artist who promotes and sells his work exclusively over the internet with very little exhibitions and involvement of dealers and galleries.
I choose this painter because his painting style and color are so vivid, looks like it's animated. He died last year
The Problem
Imagine that you have a screen that has a trash bin. When the user tap in other view it should do a moving while scale down animation to the trash bin and vanishes.
The final result should be this (without the right star):
Layout Setup
First of create a new project with storyboard and copy this to the ViewController
class scope :
class ViewController: UIViewController {
var trashBinImageView = UIImageView(image: UIImage(systemName: "trash"))
var scrollView = UIScrollView()
var starImageViewOut = UIImageView(image: UIImage(systemName: "star"))
var isImageViewMoved = false
var imageViewOutCenter: CGPoint?
}
As you can imagine this would be the trash bin from the gif and the star. I set the scroll view just to add a layer of complexity and another target coordinate system for our star view. Talking about the starImageViewOut
the Out part is because this view is out of the target coordinate system, this means that you have to do some conversion to know where the target object is.
Now let's setup the viewDidLoad
:
override func viewDidLoad() {
super.viewDidLoad()
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -200),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
scrollView.isUserInteractionEnabled = true
scrollView.bounces = true
scrollView.alwaysBounceVertical = true
scrollView.alwaysBounceHorizontal = true
trashBinImageView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(trashBinImageView)
NSLayoutConstraint.activate([
trashBinImageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: CGFloat(250)),
trashBinImageView.topAnchor.constraint(equalTo: scrollView.topAnchor),
trashBinImageView.heightAnchor.constraint(equalToConstant: 100),
trashBinImageView.widthAnchor.constraint(equalToConstant: 100)
])
view.addSubview(starImageViewOut)
starImageViewOut.translatesAutoresizingMaskIntoConstraints = false
starImageViewOut.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapStar))) // add the tap gesture to the UIImageView
starImageViewOut.isUserInteractionEnabled = true
NSLayoutConstraint.activate([
starImageViewOut.leadingAnchor.constraint(equalTo: view.leadingAnchor),
starImageViewOut.bottomAnchor.constraint(equalTo: view.bottomAnchor),
starImageViewOut.heightAnchor.constraint(equalToConstant: 100),
starImageViewOut.widthAnchor.constraint(equalToConstant: 100)
])
}
This is just regular layout stuff. You can notice that the scroll view has leading padding to turn things more interesting. This padding is important to show how can we find a view in other coordinates systems.
UITapGestureRecognizer Setup
Now it's time to setup the animation when the user tap in the star.
@objc private func didTapStar() { // Mark 1
UIView.animate(withDuration: 1) { [self] in // Mark 2
if !isImageViewMoved {
let transform = starImageViewOut.transform
let trashBinConvertedFrame = view.convert(trashBinImageView.center, from: scrollView) // Mark 3
//let trashBinConcertedFrame2 = scrollView.convert(trashBinImageView.center, to: view) // Mark 3 this is the same result as above, choose whatever you want
print("Trash Bin Converted CGPoints:", trashBinConvertedFrame, trashBinConcertedFrame2) // Mark 4
print("Trash Bin Original CGPoint:", trashBinImageView.center) // Mark 4
let scaleTransform = transform.scaledBy(x: 0.1, y: 0.1) // Mark 5
starImageViewOut.transform = scaleTransform
starImageViewOut.center = trashBinConvertedFrame
starImageViewOut.alpha = 0.1 // Mark 6
isImageViewMoved = true // Mark 7
} else {
starImageViewOut.transform = .identity
starImageViewOut.center = imageViewOutCenter ?? CGPoint(x: 50, y: 50) // Mark 8
starImageViewOut.alpha = 1
isImageViewMoved = false
}
}
}
Let's discover what exactly this code does:
- Mark 1 - This function is used by a selector so it has to be annotated with
@objc
- Mark 2 - This will start the animation block. Nothing fancy here but always remember that animation blocks are asynchronous code.
- Mark 3 - This is the famous
convert
function. It convert ah CGPoint or a frame from one coordinate system to other coordinate system. Each view in swift has it's own coordinate system, therefore when you try to find the position of a view in another coordinate system, you can use theconvert
function. Our example the trash bin is inUIScrollView
coordinate system but theStarImageViewOut
is in theViewController.view
coordinate system. You can use theconvert(_,to:)
and theconvert(_,from:)
that the result is the same, you will only need to change the parameters and the caller. - Mark 4 - This print shows the difference of getting the
center
property from the view itself and thecenter
from the convert function. - Mark 5 - We are going to scale to 1/10 of the original size to give the user the impression of vanishing item.
- Mark 6 - We are also reducing the alpha to 0.1 just to be able to tap it again to return to the original state. If you set
alpha = 0
this disables the UIImageView tap capabilities. - Mark 7 - Here is the logic if you want to tap again to restore the original position.
- Mark 8 - Just setting the center to the original place.
Now to finish the code we have to set the center to the original place:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
imageViewOutCenter = starImageViewOut.center
}
We will make sure that the trash bin center property is already drawn and correctly set in screen to get the value.
And you should see in the logs something like this:
The original CGPoint gives 300 for X value and 49 to the Y value and this is correct. This is correct inside the UIScrollView coordinate system, this means that if you want to find inside the UIScrollView the trash bin image view center you should use those values. But... the star isn't inside the scroll view so that's why we have to use the convert
method to find where exactly set the new center for the star image view.
Looking to the image above you can see that the X value from the converted system has 30 more points and the Y value has 50 more points. The X value is because scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30)
this line. As the scroll view start 30 points to the view's leading anchor, the X is 300 + 30. And the Y value is because you have top safe are margins that you have to take into account to position the new view.
That's it!
Summary
Today we learn how to use convert function and how it can be handy to do cool animations with different coordinate systems. Now you can amaze your customers with very nice animations and bring life to your applications.
That's all my people, I hope you liked as I enjoyed write this article. If you want to support this blog you can Buy Me a Coffee or just leave a comment saying hello. You can also sponsor posts! Just reach me in LinkedIn or Twitter for details.
Thanks for the reading and... That's all folks.
credits: image
Did you find this article valuable?
Support Leonardo Maia Pugliese by becoming a sponsor. Any amount is appreciated!