0

I am embedding a UIImageView inside UIContentView to make it zoomable and panable (reference. The problem is that the image is not centered correctly. It looks like the entire image content is shifted out of top left corner: enter image description here

While the printed content Offset is near zero: content offset is: (0.0, -20.0)

Here is my implementation:

import UIKit

class ZoomableImageView: UIScrollView {

    private struct Constants {
        static let minimumZoomScale: CGFloat = 0.5;
        static let  maximumZoomScale: CGFloat = 6.0;
    }


    // public so that delegate can access
    public let imageView = UIImageView()

    // gw: must be called to complete a setting
    public func setImage(image: UIImage) {
        imageView.image = image
        imageView.bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)

        self.contentSize = image.size


        let scaleFitZoomScale: CGFloat = min(
        self.frame.width / image.size.width ,
        self.frame.height / image.size.height
        )


        // reset scale and offset on each resetting of image
        self.zoomScale = scaleFitZoomScale
    }


    // MARK - constructor
    init() {
        // gw: there is no super.init(), you have to use this constructor as hack
        super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0)) // gw: relies on autolayout constraint later

        minimumZoomScale = Constants.minimumZoomScale
        maximumZoomScale = Constants.maximumZoomScale

        addSubview(imageView)


    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }



}

ViewController:

class ViewController: UIViewController, UIScrollViewDelegate {


    let zoomableImageView  = ZoomableImageView()


    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(zoomableImageView)
        zoomableImageView.delegate = self

    }

    override func viewDidLayoutSubviews() {

        zoomableImageView.frame = view.frame
        let image = UIImage(imageLiteralResourceName: "kelly")
        zoomableImageView.setImage(image: image)

    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        // print("scale factor is: \(scrollView.zoomScale)")
        return zoomableImageView.imageView
    }

    func scrollViewDidZoom(_ scrollView: UIScrollView) {
         print("scale factor is: \(scrollView.zoomScale)")
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print("content offset is: \(scrollView.contentOffset)")
    }



}

However, the centering of image works perfectly fine if I don't wrap the UIImageView and UIScrollView under the custom class:

class ViewController: UIViewController, UIScrollViewDelegate {


    let imageView: UIImageView = {
        let image = UIImage(imageLiteralResourceName: "kelly")
        let imageView = UIImageView(image: image)
        imageView.bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
        return imageView
    } ()

    let scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        return scrollView
    } ()
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        view.addSubview(scrollView)
        scrollView.addSubview(imageView)


        scrollView.delegate = self
        self.scrollView.minimumZoomScale = 0.5;
        self.scrollView.maximumZoomScale = 6.0;

    }

    override func viewDidLayoutSubviews() {
        scrollView.frame = view.frame

        if let size = imageView.image?.size {
            scrollView.contentSize = size
        }



    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }


}

Did I missed something in the first implementation?

modeller
  • 3,770
  • 3
  • 25
  • 49
  • Not entirely clear what you want... If the image's actual size is smaller than the scrollView frame (which, by your code, is the full main view frame), do you want the image to initially be shown centered horizontally and vertically? If the image is larger than the frame, do you want it centered, but already scaled so the full image is visible? Or centered, with the sides / top&bottom extending outside the frame of the scrollView? – DonMag Jan 24 '19 at 15:15
  • I am trying to get the image top left corner match with the scroll view top left corner. This is automatically achieved with 2nd impl, but the 1st impl behaves different (image shifted out of top-left corner) – modeller Jan 24 '19 at 15:25
  • 1
    OK - I think it's simply that, in your `ZoomableImageView` class, you are setting **`bounds`** when it should be **`frame`** -- so change `imageView.bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)` to `imageView.frame = ...` – DonMag Jan 24 '19 at 15:33
  • @DonMag works like charm. Damn UI programming is hard – modeller Jan 24 '19 at 15:36
  • I added this as an answer so anyone who may come across it in the future will see the "fix". – DonMag Jan 24 '19 at 15:39

1 Answers1

2

In your ZoomableImageView class, you are setting bounds when it should be frame

So change:

imageView.bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) 

to

imageView.frame =  CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
DonMag
  • 69,424
  • 5
  • 50
  • 86