0

I am new to Swift and I'm trying to reproduce the behavior most applications have when you zoom in an image (like Twitter, Facebook etc...). Basically, you tap on the image, it opens fullscreen and then you can zoom in and move freely around the zoomed in image. However, I'm having trouble with the last two parts.

My main ViewController has an instance of a custom UIImageView called TappableImage, which inherits from UIImageView. I use this class as a wrapper to handle touch and transitions, code is below:

import UIKit

class TappableImage: UIImageView {
    private let windowBounds: CGRect = UIScreen.main.bounds
    private let imageV: UIImageView = UIImageView()
    private var startingFrame: CGRect = .zero
    private var backdrop: ImageBackdrop!

    private var isOpen: Bool = false

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }

    private func setup() {
        image = UIImage(named: "kaws") // horizontal
//        image = UIImage(named: "portrait") // vertical
//        image = UIImage(named: "ksg") // square
        isUserInteractionEnabled = true

        startingFrame = frame

        imageV.image = self.image
        imageV.frame = startingFrame
        imageV.contentMode = .scaleAspectFill
        imageV.isUserInteractionEnabled = true

        backdrop = ImageBackdrop(frame: windowBounds, with: imageV)

        let openTapGesture = UITapGestureRecognizer(target: self, action: #selector(onTap))
        openTapGesture.numberOfTapsRequired = 1
        addGestureRecognizer(openTapGesture)
    }

    @objc private func onTap(_ sender: UITapGestureRecognizer) {
        guard let window = UIApplication.shared.windows.first else {
            print("no window...")
            return
        }

        window.addSubview(backdrop)
        UIView.animate(withDuration: 0.25, animations: {
            self.backdrop.alpha = 1

            let h = (self.windowBounds.width / self.startingFrame.width) * self.startingFrame.height
            let y = self.windowBounds.height / 2 - h / 2
            self.imageV.frame = CGRect(x: 0, y: y, width: self.windowBounds.width, height: h)

        }) { (_) in
            self.isOpen = true
        }
    }
}

When a user tap the image, I bring a custom UIScrollView to the front in which I added my TappableImage. The UIScrollView is a basic implementation just handling pinching, code is below:

import UIKit

class ImageBackdrop: UIScrollView, UIScrollViewDelegate {
    private var imageView: UIImageView!

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    convenience init(frame: CGRect, with imgv: UIImageView) {
        self.init(frame: frame)

        imageView = imgv
        setupScrollView()
        addSubview(imageView)
    }

    private func setupScrollView() -> Void {
        delegate = self

        backgroundColor = .black
        alpha = 0

        minimumZoomScale = 1.0
        maximumZoomScale = 5.0
        contentSize = imageView.bounds.size
        autoresizingMask = [.flexibleWidth, .flexibleHeight]
    }

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

My issue is that when I zoom in, the image is shifted down and I cannot move freely around with one finger. If I try to move to the bottom of the image, I am "slingshotted" back to the top, like when you arrive at the end of a list, you can find a video of this behavior here.

My guess is that the UIImageView center or size is incorrect but I cannot find a way to fix it.

tom
  • 745
  • 5
  • 20

0 Answers0