Search code examples
iosswiftuiimageviewautolayoutscrollview

Resize height of image to fit screen (keep aspect ratio) and make the rest horizontally scrollable programmatically


I want to fit the height of my image to the height of the screen, but keep the aspect ratio and make the rest horizontally scrollable. You can see the code example below. I managed to fit the height of the image to the height of the screen while keeping the aspect ratio like this: Adding a view to a scroll view that will stretch to fill available width.

My last Problem is that the scrollView is not scrolling over the whole horizontal distance. Just the distance the scrollview used to scroll with the smaller image size, so I also tried to reset the scrollview after setting the contentView like this How do I auto size a UIScrollView to fit its content but unfortunately it is not working. So i cannot see the edges of my image, however if i pull the scrollview to one side and keep my "finger" on i can see the image. but as soon as i release it bounces back to the middle part of the image.

I also took a look at this: https://developer.apple.com/library/archive/technotes/tn2154/_index.html but i couldn't find a solution to my problem.

I also already tried to calculate the ratio change of the image after fitting to the screen manually and than set the contentSize of the scrollview, but that didn't work either (like the answer in this question: How to position UI elements on background image relative to the image and here: How to resize UIImageView based on UIImage's size/ratio in Swift 3?)

Here's the code:

import UIKit

class ViewController: UIViewController, UIScrollViewDelegate {
     var scrollView: UIScrollView = {
            let scrollView = UIScrollView()
            scrollView.translatesAutoresizingMaskIntoConstraints = false
            scrollView.backgroundColor = .red
            return scrollView
        }()

        var contentView: UIView = {
            let contentView = UIView()
            contentView.translatesAutoresizingMaskIntoConstraints = false
            return contentView
        }()

        var imageView: UIImageView = {
            let imageView = UIImageView()
            let image = UIImage(named: "Erlebnispark.jpg")
            imageView.image = image
            imageView.backgroundColor = .red
            imageView.translatesAutoresizingMaskIntoConstraints = false
            imageView.contentMode = .scaleAspectFill
            return imageView
        }()

        override func viewDidLoad() {
            super.viewDidLoad()
            self.view.addSubview(scrollView)
            scrollView.addSubview(contentView)
            contentView.addSubview(imageView)
            scrollView.delegate = self
            setupLayout()
        }

        func setupLayout() {
            [
                scrollView.topAnchor.constraint(equalTo: self.view.topAnchor),
                scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
                scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
                scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),

                contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
                contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
                contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
                contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
                //contentView.widthAnchor.constraint(equalTo: scrollView.contentLayoutGuide.widthAnchor),

                contentView.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor),
               // contentView.widthAnchor.constraint(equalTo: scrollView.contentLayoutGuide.widthAnchor),

                imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
                imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
                imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
                imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
               // imageView.heightAnchor.constraint(equalTo: contentView.heightAnchor),

                ].forEach {$0.isActive = true}
        }
}

Maybe someone can help me.


Solution

  • Just a couple changes...

    var imageView: UIImageView = {
        let imageView = UIImageView()
        let image = UIImage(named: "Erlebnispark.jpg")
        imageView.image = image
        imageView.backgroundColor = .red
        imageView.translatesAutoresizingMaskIntoConstraints = false
    
        // use scaleToFill
        //imageView.contentMode = .scaleAspectFill
        imageView.contentMode = .scaleToFill
    
        return imageView
    }()
    

    First line in setupLayout():

    func setupLayout() {
        // add this line
        guard let img = imageView.image else { return }
    

    Last line in constraints array:

            // add this line
            imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: img.size.width / img.size.height),
    
            ].forEach {$0.isActive = true}
    

    Your completed constraints now tell the image view's width to be its own height with a multiplier of img.size.width / img.size.height

    Everything else is good to go.