Holy Swift

Holy Swift

Using convert function to make Animations in Swift

Using convert function to make Animations in Swift

Subscribe to my newsletter and never miss my upcoming articles

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:

  1. Mark 1 - This function is used by a selector so it has to be annotated with @objc
  2. Mark 2 - This will start the animation block. Nothing fancy here but always remember that animation blocks are asynchronous code.
  3. 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 the convert function. Our example the trash bin is in UIScrollView coordinate system but the StarImageViewOut is in the ViewController.view coordinate system. You can use the convert(_,to:) and the convert(_,from:) that the result is the same, you will only need to change the parameters and the caller.
  4. Mark 4 - This print shows the difference of getting the center property from the view itself and the center from the convert function.
  5. Mark 5 - We are going to scale to 1/10 of the original size to give the user the impression of vanishing item.
  6. 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.
  7. Mark 7 - Here is the logic if you want to tap again to restore the original position.
  8. 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:

Screenshot 2021-09-23 at 08.16.48.png

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

Interested in reading more such articles from Leonardo Maia Pugliese?

Support the author by donating an amount of your choice.

 
Share this