Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
242 views
in Technique[技术] by (71.8m points)

swift - How to use automatically/dynamically set scrollview to fit the content view

Surprisingly, this is harder than I thought. I followed this tutorial which seems rather straightforward but I am programmatically creating my view instead of using storyboard. Just to be clear, the content I add to the content view is static i.e. it's not growing or increasing.

Here are the definitions of scroll view and content view:

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

lazy var scrollView : UIScrollView = {
    let scrollView = UIScrollView(frame: .zero)
    scrollView.backgroundColor = .white
    scrollView.frame = self.view.bounds
    scrollView.bounces = true
    scrollView.autoresizingMask = .flexibleHeight
    scrollView.contentSize = CGSize(width: self.view.frame.width, height: contentView.frame.height)
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    return scrollView
}()

In view did load add the scroll view and set its constraints in the view controller:

view.addSubview(scrollView)
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

Then I add content view and the constraints:

scrollView.addSubview(contentView)
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true

contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
let constraint = contentView.heightAnchor.constraint(equalTo: view.heightAnchor)
constraint.priority = UILayoutPriority(250)
constraint.isActive = true

The problem is that I cannot scroll all the way to the bottom.

question from:https://stackoverflow.com/questions/65872987/how-to-use-automatically-dynamically-set-scrollview-to-fit-the-content-view

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Initial points to note

Delete

    scrollView.frame = self.view.bounds

It is pointless to give scrollView a frame, as you are going to give it a frame later through the use of constraints.

Delete

    scrollView.autoresizingMask = .flexibleHeight

You are using constraints, not the autoresizing mask, to give the scroll view its frame and later resizing behavior.

Delete

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

Once the scroll view is under the influence of constraints, you must use constraints, not contentSize, to give it a content size that determines scroll behavior.

Adding the content view

With those preliminaries out of the way, let's talk about how you add the content view to the scroll view:

scrollView.addSubview(contentView)
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true

That is not exactly wrong, but it is very outmoded. You should pin the content view to the scroll view's content layout guide; that is what it is for. So, wherever you have equalTo: scrollView, change it to equalTo: scrollView.contentLayoutGuide.

Scrolling

Okay! Now everything is assembled and we are ready to talk about scrolling. What makes the scroll view scrollable, in this configuration, is that the content view is bigger than the scroll view itself. Well, so far, that's not true; in fact, the content view has no size at all. So we must proceed to give it some size.

Your attempt to do that is rather feeble. Let's extract the key lines where you give the content view height and width constraints:

contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
contentView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true

(Delete the other two lines, as they are not really doing anything useful now.) So now is the content view any bigger than the scroll view? Well, maybe, but if so, only by a tiny bit, because the content view is merely the size of the main view, and the scroll view is either that same size or a tiny bit smaller.

Since we are just demonstrating, it would be better to make the content view a lot bigger than the scroll view, so we can do some major scrolling. Change the second line to this:

contentView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier:2 ).isActive = true

Yeah, baby! Now we can really scroll.

Making the content more visible

Still, it's a little hard to see what's happening (everything is white on white), so I suggest you fill the content view with some color that will permit us to see what's going on. Declare a self-drawing view as follows:

class MyView : UIView {
    override class var layerClass : AnyClass { CAGradientLayer.self }
    override func willMove(toSuperview newSuperview: UIView?) {
        let lay = self.layer as! CAGradientLayer
        lay.colors = [UIColor.red.cgColor, UIColor.green.cgColor]
    }
}

Now change

    let view = UIView()

to

    let view = MyView()

Now it is very obvious when you scroll to the bottom; the real green is visible at the bottom.

Summary

Here is the complete code of the corrected example:

class MyView : UIView {
    override class var layerClass : AnyClass { CAGradientLayer.self }
    override func willMove(toSuperview newSuperview: UIView?) {
        let lay = self.layer as! CAGradientLayer
        lay.colors = [UIColor.red.cgColor, UIColor.green.cgColor]
    }
}

class ViewController: UIViewController {
    
    lazy var contentView : UIView = {
        let view = MyView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

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


    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(scrollView)
        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
        
        scrollView.addSubview(contentView)
        contentView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor).isActive = true
        contentView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor).isActive = true
        contentView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor).isActive = true
        contentView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor).isActive = true

        contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
        contentView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier:2 ).isActive = true

    }


}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...