Animate UIImageView height and width changes

50 views Asked by At

My goal is to animate UIImageView transition from top left corner to the center of the view. It also has to change it width and height to screen width and change corner radius to zero.

How to modify following code so UIImageView changes its width and height:

    private func imageAnimates() {
        UIView.animate(
            withDuration: 0.5,
            delay: 0.0,
            options: .curveLinear
        ) {
            self.userImage.layer.cornerRadius = 0
            self.userImage.center = CGPoint(x: self.view.center.x, y: self.view.center.y)
        } completion: { finished in
            print("Image animated")
        }
    }

UPD: Following an answer by vaibhav sharma, I updated my code to:

        UIView.animate(
            withDuration: 0.5,
            delay: 0.0,
            options: .curveLinear
        ) {
            self.userImage.alpha = 1.0
            self.userImage.layer.cornerRadius = 0
            self.userImage.frame = CGRect(
                 x: (screenWidth - finalWidth) / 2,
                 y: (self.view.frame.size.height - finalHeight) / 2,
                 width: finalWidth,
                 height: finalHeight
             )
        } completion: { finished in
            print("Image animated")
        } 

But the result I'm getting is not the one I expected. Here's a screen recording: https://sendvid.com/i1fxbf0y. It seems like the image now goes from tiny picture in the top left corner to its original frame and position. It's not even moving to the center.

2

There are 2 answers

1
DonMag On BEST ANSWER

You need to start out *simple -- work on only this image view animation until you get it working right. Then add it to your complex controller.

You cannot "mix-and-match" constraints and direct frame setting. UIKit will automatically reset the frame to match the constraints.

Instead, use only frame setting for your userImage view ... you can use auto-layout constraints for everything else.

So, take a look at this - tap anywhere to show-and-grow or shrink-and-hide the image view:

class ProfileViewController: UIViewController {
    
    private lazy var userImage: UIImageView = {
        
        //let imageView = UIImageView(image: UIImage(named: me.login))
        
        // I don't have your "me.login" so let's use SF Symbol
        let imageView = UIImageView()
        if let img = UIImage(systemName: "person.fill") {
            imageView.image = img
        }
        
        imageView.layer.cornerRadius = 45
        imageView.clipsToBounds = true

        // we're going to set the frame directly, so
        //  use true (it's the default so we don't really need to set it)
        imageView.translatesAutoresizingMaskIntoConstraints = true
        
        imageView.alpha = 0.0
        
        // while we're designing this,
        //  use a backgroundColor so we can see the framing
        imageView.backgroundColor = .red
        
        return imageView
    }()
    
    // these will be update in viewDidLayoutSubviews()
    //  when we know the view frame and safeAreaInsets
    private var userImageStartFrame: CGRect = .zero
    private var userImageFinalFrame: CGRect = .zero
    private var screenWidth: CGFloat = 0.0

    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // we'll adjust the origin in viewDidLayoutSubviews()
        userImageStartFrame = CGRect(x: 0.0, y: 0.0, width: 90.0, height: 90.0)
        
        view.addSubview(userImage)

        // do NOT set constraints on userImage !!!

    }

    override func viewDidLayoutSubviews() {
        
        super.viewDidLayoutSubviews()

        // viewDidLayoutSubviews() will be called many times, so
        //  only set the userImage view frames when the view width changes
        if screenWidth != view.frame.width {
            
            screenWidth = view.frame.width
            
            // get the safe area rect
            let r: CGRect = self.view.bounds.inset(by:self.view.safeAreaInsets)

            userImageStartFrame.origin = r.origin

            // let's make the large-center frame 90% of the safe area width
            let finalWidth: CGFloat = r.width * 0.9
 
            userImageFinalFrame = CGRect(
                x: r.origin.x + ((r.width - finalWidth) * 0.5),
                y: r.origin.y + ((r.height - finalWidth) * 0.5),
                width: finalWidth, height: finalWidth
            )
            
            userImage.frame = userImageStartFrame

        }

    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        UIView.animate(
            withDuration: 0.5,
            delay: 0.0,
            options: .curveLinear
        ) {
            //self.backgroundBlur.alpha = 0.5
            //self.blurCloseButton.alpha = 1.0

            if self.userImage.alpha == 0.0 {
                // userImage is currently transparent (alpha: 0.0)
                //  so we want to show it
                self.userImage.alpha = 1.0
                self.userImage.layer.cornerRadius = 0
                self.userImage.frame = self.userImageFinalFrame
            } else {
                self.userImage.alpha = 0.0
                self.userImage.layer.cornerRadius = 45
                self.userImage.frame = self.userImageStartFrame
            }
            
        } completion: { finished in
            print("Background blur appeared")
        }

    }
    
}
3
SwiftCoderVaibhav On

As per my understanding, might be i am wrong, please try

   private func imageAnimates() {
    // Calculate the final width and height to match the screen width
    let screenWidth = self.view.frame.size.width
    let finalWidth = screenWidth
    let finalHeight = screenWidth * (self.userImage.frame.size.height / self.userImage.frame.size.width)
    
    UIView.animate(
        withDuration: 0.5,
        delay: 0.0,
        options: .curveLinear
    ) {
        self.userImage.alpha = 1.0
        self.userImage.layer.cornerRadius = 0
        self.userImage.frame = CGRect(
            x: (self.view.frame.size.width - finalWidth) / 2,
            y: (self.view.frame.size.height - finalHeight) / 2,
            width: finalWidth,
            height: finalHeight
        )
    } completion: { finished in
        print("Image animated")
    }
}
  • Please look on below points:
    1. Calculate the finalWidth and finalHeight based on the screen width while maintaining the aspect ratio of the original image.

    2. Set the frame property of the UIImageView to the calculated finalWidth and finalHeight, and center it on the screen.