-2

My hierarchy is scrollView > UiView(named it contentView) > imageView.

I have the sv pinned to the l,r,t,b of the parent view, the contentView pinned to the t,b of the sv and the width of the parent view, and the imageView pinned to the l,r,t,b of the contentView.

When using the iPad the image takes up the entire screen. When I swipe up and down the scrollView scrolls with no issues. When I swipe left or right I get a crash:

* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]'

Why can I scroll up and down with no issues but get a crash when trying to scroll left or right?

MyVC: UIViewController, UIScrollViewDelegate {

lazy var scrollView: UIScrollView = {
    let scrollView = UIScrollView()
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.backgroundColor = .white
    return scrollView
}()

lazy var containerView: UIView = {
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

lazy var imageView: UIImageView = {
    let imageView = UIImageView()
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.contentMode = .scaleAspectFill
    return imageView
}()

override func viewDidLoad() {
    super.viewDidLoad()


    scrollView.delegate = self
    scrollView.minimumZoomScale = 1.0
    scrollView.maximumZoomScale = 6.0

    setAnchors()
}

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

func setAnchors() {

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

    scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
    scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
    scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true

    containerView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
    containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
    containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

    imageView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
    imageView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
    imageView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
    imageView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true

    scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: view.frame.height)
}

}

Update:

I tried using these constraints and it still crashed:

containerView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • Because the horizontal content view constraint is what’s wrong. You are doing the whole content view and content size thing incorrectly. – matt Sep 19 '19 at 01:35
  • `containerView` missing constraint `leadingAnchor ` – Nullable Sep 19 '19 at 01:40
  • @Nullable I updated the answer to show I tried setting the containerView's leading and trailing and it still crashed. Thanks though :) – Lance Samaria Sep 19 '19 at 02:05
  • 1
    Those are still wrong. You must pin to the scroll view ‘s content layout guide on all four sides. No width constraint. Do not try to set the content size manually. Just make the container view horizontal constraints exactly like the vertical constraints which are correct. – matt Sep 19 '19 at 02:11
  • I thought the scrollView gets its w/h from its content's w/h. If it's not set manually then how? – Lance Samaria Sep 19 '19 at 02:13
  • why `scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: view.frame.height)`? `scrollView.contentSize.width` always 0 – Nullable Sep 19 '19 at 02:31
  • @Nullable i commented that line out and it still crashed – Lance Samaria Sep 19 '19 at 02:40
  • @matt https://stackoverflow.com/a/48217064/4833705 – Lance Samaria Sep 19 '19 at 03:39
  • @matt I tried your your answer in the comments, it worked. If you add it I'll choose it because even though the other answer works, it causes a problem with the image. – Lance Samaria Sep 19 '19 at 03:44
  • No thanks. This is standard stuff. I’ve explained it here many times already. It doesn’t need to be said again. – matt Sep 19 '19 at 03:47
  • ok cool thanks for the help! – Lance Samaria Sep 19 '19 at 03:48

1 Answers1

0

I updated some of your code

I add a local picture 00 to imageView

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        scrollView.delegate = self
        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = 6.0

        imageView.image = UIImage.init(named: "00")
        setAnchors()
    }

    lazy var scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.backgroundColor = .white
        return scrollView
    }()

    lazy var containerView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFill
        return imageView
    }()

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

    func setAnchors() {

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

        scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
        scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true

        containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
        containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        containerView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
        containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

        imageView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
        imageView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
        imageView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
        imageView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true

        scrollView.contentSize = CGSize(width: view.bounds.size.width, height: view.frame.height)
    }

This is the gif of the iPad simulator.

enter image description here

I have tested it with iPhone and it works. Hope it helps you

Nullable
  • 761
  • 5
  • 17
  • my code works fine on the iPhone, its the iPad where it crashed. Test your code on there and see what happens. – Lance Samaria Sep 19 '19 at 02:44
  • Sorry, I only have an iPad simulator, the simulator test is working. – Nullable Sep 19 '19 at 02:48
  • I tried it and it crashed here **(imageView.image?.size.width)!** it's nil – Lance Samaria Sep 19 '19 at 02:53
  • Need to set `imageView.image` before `setAnchors()` – Nullable Sep 19 '19 at 02:58
  • it works and i'm going to accept the answer but it caused another problem -the image is stretched beyond the screen. When I scroll it scrolls limited and the scroll doesn't work correctly. I changed the imageView to .scaleAspectFit – Lance Samaria Sep 19 '19 at 03:05
  • Yes, I wrote in the comments. Setting `contentSize` depends on the effect you want. If you want the scrolling range to be the width and height of the image, it is likely to exceed the screen. If you want to limit the screen width, you can reset `contentSize` – Nullable Sep 19 '19 at 03:10
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/199661/discussion-between-lance-samaria-and-nullable). – Lance Samaria Sep 19 '19 at 03:12