I'm developing a screen which has just one UIImageView
inside UIScrollView
. UIScrollView
enables users to pin and zoom image. I got help from below post. It uses storyboard and AutoLayout. Basically, It has 8 constraints which are top, bottom,leading and trailing (scrollview to superview and imageView to scrollView). Example post sets image inside storyboard so that It doesn't needed to give any specific height or width.
https://www.raywenderlich.com/560-uiscrollview-tutorial-getting-started
My problem is that when I use the same code but do everything programatically, I can see the image (which is extra zoomed) and I can scroll with one finger but when I want to pinch it (zoom out, zoom in) I got
'CALayer position contains NaN: [nan nan]'
error. I think It is because scrollview doesn't set contentSize or contentOffset but whether I set that attributes,I got the same error.
override func viewDidLoad() {
self.scrollView.delegate = self
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = photo
imageView.contentMode = .scaleAspectFit
scrollView.addSubview(imageView)
self.view.addSubview(scrollView)
self.imageView.setContentHuggingPriority(UILayoutPriority.init(251), for: NSLayoutConstraint.Axis.vertical)
self.imageView.setContentHuggingPriority(UILayoutPriority.init(251), for: NSLayoutConstraint.Axis.horizontal)
scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: self.scrollView.topAnchor, constant: 0)
imageViewTopConstraint.isActive = true
imageViewBottomConstraint = imageView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor, constant: 0)
imageViewBottomConstraint.isActive = true
imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: self.scrollView.leadingAnchor, constant: 0)
imageViewLeadingConstraint.isActive = true
imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: self.scrollView.trailingAnchor, constant: 0)
imageViewTrailingConstraint.isActive = true
scrollView.contentSize = imageView.bounds.size
}
fileprivate func updateMinZoomScaleForSize(_ size: CGSize) {
let widthScale = size.width / imageView.bounds.width
let heightScale = size.height / imageView.bounds.height
let minScale = min(widthScale, heightScale)
scrollView.minimumZoomScale = minScale
scrollView.zoomScale = minScale
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
updateMinZoomScaleForSize(view.bounds.size)
}
fileprivate func updateConstraintsForSize(_ size: CGSize) {
let yOffset = max(0, (size.height - imageView.frame.height) / 2)
imageViewTopConstraint.constant = yOffset
imageViewBottomConstraint.constant = yOffset
let xOffset = max(0, (size.width - imageView.frame.width) / 2)
imageViewLeadingConstraint.constant = xOffset
imageViewTrailingConstraint.constant = xOffset
view.layoutIfNeeded()
}
}
extension ZoomedPhotoViewController: UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
updateConstraintsForSize(view.bounds.size)
}
}
I make it work by moving updateMinZoomScaleForSize(view.bounds.size)
function from ViewDidLayoutSubViews()
to ViewDidLoad()
and called self.view.layoutIfNeeded()
before calling ZoomScale function. Therefore my final and working ViewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
self.scrollView.delegate = self
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = photo
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
scrollView.addSubview(imageView)
//scrollView.contentSize = CGSize(width: imageView.image!.size.width, height: imageView.image!.size.height)
self.view.addSubview(scrollView)
self.imageView.setContentHuggingPriority(UILayoutPriority.init(251), for: NSLayoutConstraint.Axis.vertical)
self.imageView.setContentHuggingPriority(UILayoutPriority.init(251), for: NSLayoutConstraint.Axis.horizontal)
scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: self.scrollView.topAnchor, constant: 0)
imageViewTopConstraint.isActive = true
imageViewBottomConstraint = imageView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor, constant: 0)
imageViewBottomConstraint.isActive = true
imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: self.scrollView.leadingAnchor, constant: 0)
imageViewLeadingConstraint.isActive = true
imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: self.scrollView.trailingAnchor, constant: 0)
imageViewTrailingConstraint.isActive = true
self.view.layoutIfNeeded()
updateMinZoomScaleForSize(view.bounds.size)
}