Emre Önder
Emre Önder

Reputation: 2537

'CALayer position contains NaN: [nan nan]' on UIScrollView

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)
}

}

Upvotes: 0

Views: 1572

Answers (1)

Emre Önder
Emre Önder

Reputation: 2537

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)

}

Upvotes: 0

Related Questions