Subview is not resizing properly

I have a view with an image. When the image is clicked there is a modal popup of that image that is "full screen" and is zoomable.


The first time the image is clicked the popup looks great, with the image the proper size. But if the popup is closed and reopened, the sizing is off for some reason. I'm not sure what the problem is.


Code:


import UIKit
class ExampleViewController: UIViewController, UIScrollViewDelegate {
   //popup itself
    @IBOutlet var imageView: UIView!
   //scroll view that enables the zoom feature
    @IBOutlet weak var scrollView: UIScrollView!
  //the image in the popup
    @IBOutlet weak var zoomImageView: UIImageView!
  //dark background (that is clickable) when the popup is shown
    @IBOutlet weak var backgroundButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
     
        self.navigationController?.setNavigationBarHidden(true, animated: false)
     
        imageView.layer.cornerRadius = 10
        imageView.layer.masksToBounds = true
     
        self.scrollView.minimumZoomScale = 1.0
        self.scrollView.maximumZoomScale = 6.0
     
    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
     
        return self.zoomImageView
     
    }
   //keeps the image the proper size and centered if the phone rotates while the popup is open
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        if UIDevice.current.orientation.isLandscape{
            imageView.center = self.view.center
            imageView.frame = view.frame
            scrollView.zoomScale = 1.0
         
        } else if UIDevice.current.orientation.isPortrait{
            imageView.center = self.view.center
            imageView.frame = view.frame
            scrollView.zoomScale = 1.0
        }
    }
   //activates when the image is clicked in the superview
    @IBAction func showImageView(_ sender: Any) {
     
        animateIn()
     
    }
  //activates when the close button is clicked in the popup (or when the backgroundButton is clicked)
    @IBAction func closeImageView(_ sender: Any) {
     
        animateOut()
     
    }

    func animateIn() {
     
        self.scrollView.zoomScale = 1.0
     
        self.view.addSubview(imageView)
        imageView.center = self.view.center
        imageView.frame = self.view.frame
     
        imageView.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
        imageView.alpha = 0
     
        self.backgroundButton.alpha = 0.7
     
        UIView.animate(withDuration: 0.4) {
            self.imageView.alpha = 1
            self.imageView.transform = CGAffineTransform.identity
        }
     
    }

    func animateOut() {
     
        UIView.animate(withDuration: 0.3, animations: {
            self.imageView.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
            self.imageView.alpha = 0
         
            self.backgroundButton.alpha = 0
         
        }) { (success:Bool) in
            self.imageView.removeFromSuperview()
        }
     
    }
}


Any help would be greatly appreciated.


The first part of the answer..

Answered by QuinceyMorris in 227830022

You have source code lines like this:


        imageView.frame = self.view.frame


which are potentially a problem, for two separate reasons:


1. You are at times applying a transform to imageView. Whenever a view has a non-identity transform, its frame property is meaningless, and attempts to resize the view by setting the frame may do the wrong thing. That might explain why it works the first time (imageView has an identity transform when you set the frame at the first animate-in), but fails later (imageView has a non-identity transform left over from the last animate-out), but I'm not sure I'm reading the sequence of events in your code correctly.


Instead, for a transformed view, you should set the center and bounds properties. (The bounds represents the untransformed view size.)


2. You're mixing coordinate systems. The frame of imageView is in the bounds coordinate system of its superview (i.e. self.view), and the frame of self.view is in the bounds coordinate system of its superview (or the window, if it has no actual superview).


Instead, you should transform coordinates to/from the correct coordinate system for each view.


Again, you've posted a lot of code, so I don't know what's going on in detail, and I might be on the wrong track here, but it does look kinda wrong to me.

>if the popup is closed and reopened, the sizing is off for some reason


Might be a case of it adopting the layout specs from it's parent at that point - are you confirming to re-declare/reload it's own positioning?

Accepted Answer

You have source code lines like this:


        imageView.frame = self.view.frame


which are potentially a problem, for two separate reasons:


1. You are at times applying a transform to imageView. Whenever a view has a non-identity transform, its frame property is meaningless, and attempts to resize the view by setting the frame may do the wrong thing. That might explain why it works the first time (imageView has an identity transform when you set the frame at the first animate-in), but fails later (imageView has a non-identity transform left over from the last animate-out), but I'm not sure I'm reading the sequence of events in your code correctly.


Instead, for a transformed view, you should set the center and bounds properties. (The bounds represents the untransformed view size.)


2. You're mixing coordinate systems. The frame of imageView is in the bounds coordinate system of its superview (i.e. self.view), and the frame of self.view is in the bounds coordinate system of its superview (or the window, if it has no actual superview).


Instead, you should transform coordinates to/from the correct coordinate system for each view.


Again, you've posted a lot of code, so I don't know what's going on in detail, and I might be on the wrong track here, but it does look kinda wrong to me.

I believe you do have the proper understanding of my code (sequences, etc.). I was looking into bounds and centering earlier. Wasn't sure that it was the problem. But I'll go ahead and try to implement something regarding bounds. I'll get back to you if it works or doesn't.

Ok, so I replaced


imageView.frame = self.view.frame


in the animateIn() block with


imageView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height)


Although its still giving me the same results. I do believe the problem has to do something with the transform in animateOut(). I'm not exactly sure what the exact issue is, or how to go about solving it.

I commented out all the code that has to do with animation of the popup. And it works!

So, you're first thought was right. All I need to do now is to animate it properly.

Yes, don't use the "frame" property of your imageView at all, either to set or to get. Instead, set the bounds. (Or better still, use autolayout constraints that you set up in IB.)


>> All I need to do now is to animate it properly.


You are already animating it properly. Just don't use the frame for sizing purposes.

Thanks you've helped a lot.

Subview is not resizing properly
 
 
Q