Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a vertical UIScrollView programmatically in Swift

I've been looking for days for a (working) tutorial or even an example app that uses UIScrollView to scroll vertically, programatically that is. There's tons of tutorials on using storyboard, leaving me at a loss.

I looked through apple's documentation, and their "guide" still not having a solid example or hint as to where to start. What I've attempted so far, is doing some of the following.

Making my view a scrollview Directly in the class

let scrollView = UIScrollView(frame: UIScreen.mainScreen().bounds)

Then assigning it to the view in my viewDidLoad function

self.view = scollView

Attempting to change the content size.

self.scrollView.contentSize = CGSize(width:2000, height: 5678)

Trying to enable scrolling with

scrollView.scrollEnabled = true

And the last suggestion I could find on doing this programatically

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    scrollView.frame = view.bounds
}

Currently I havn't tried to start adding my objects to the scrollview, (I don't need to zoom, just do vertical scrolling), but I havn't managed to get anything working whatsoever :( In fact, running the app with those additions simply causes UIProblems, The screen is moved up weirdly, and it doesn't fit the entire width of my screen? I tried fixing that making the frame bounds equal to the width, but still didn't work

I'm not getting any errors.

I would love to see an example viewcontroller that you can scroll vertically in! Or any help would be hugely appreciated!

Here is a screenshot of the disaster, attempting to make my view scrollable causes.

Screen Shot

(I made the scrollview background red, to see if it was even showing up correctly. Which it seems to be. Yet I can't scroll anywhere

As suggested in the comments, instead of doing self.view = self.scrollview, I tried adding scrollview as a subview of self.view instead, but with no positive results.

Adding

scrollView.contentSize = CGSize(width:2000, height: 5678)

to viewDidLayoutSubviews, as suggested in the comments below made my view scrollable!

However my layout still looks like a complete mess for some reason (it looks as it's supposed to, before I made it scrollable).

Here's an example constraint for my topBar (blue one), that's supposed to take up the entire horizontal space.

self.scrollView.addConstraints(
        NSLayoutConstraint.constraintsWithVisualFormat(
            "H:|[topBar]|", options: nil, metrics: nil, views: viewsDictionary))

Any ideas why this doesn't work?

like image 709
Mark L Avatar asked Mar 17 '15 08:03

Mark L


1 Answers

Thanks to Tom Insam who helped me put this answer together:

You need to add all constraints as you usually do i.e.

  • add constraints for the scrollview and its superview.
  • add constraints for the contents of the scrollview.

Then pause. Understand that at this point, even if your scrollView's frame is known to be e.g. 400 * 600, still the size of its content is unknown. It could be 400 * 6000 or 400 * 300 or any other size.

There's no other edge (leading, trailing, left, right, top, bottom, margins) based constraint that you can use to calculate the scrollview's content size.

Unless your views have some intrinsicContentSize, then at this point if you run the code, you'll get a run-time error saying: ScrollView Content size is ambiguous. Continue reading to learn more about intrinsicContentSize.

What's the solution?

https://developer.apple.com/library/archive/technotes/tn2154/_index.html

To use the pure autolayout approach do the following:

  • Set translatesAutoresizingMaskIntoConstraints to NO on all views involved.
  • Position and size your scroll view with constraints external to the scroll view.
  • Use constraints to lay out the subviews within the scroll view, being sure that the constraints tie to all four edges of the scroll view and do not rely on the scroll view to get their size.

The part I bolded is tho whole focus of my answer. You need to set a (horizontal/vertical) constraint that's independent of edges. Because otherwise it would rely on the scrollview to get its size.

Instead you need to explicitly set constraints on the width, height or attach to the center of the axis. The 3rd part is unneeded if the view has intrinsicContentSize e.g. labels or textviews can calculate their contentsize based on font, character length, and line breaks. To dig deepinter into intrinsicContentSize, see here


To say things differently:

ScrollView needs:

  • external constraints against its superview
  • internal constraints for its content
  • edge-to-edge / chained subviews in a way that its height (if scrolling vertically can be calculated). To the Layout engine setting a contentSize with a height of 1000 shouldn't be any different from chaining multiple subviews where the height of the all the content can be calculated as 1000. e.g. you have two labels together they have 400 lines. Each lines takes 2points and there's 100points of line space and 100 points of space between the two labels equaling to 1000 ( 400 * 2 + 100 + 100) points.

You see, the OS can calculate that without looking into the surroundings of the label. That's what I mean by calculating the size without relying on the scrollview


Also see docs from here as well.

like image 90
mfaani Avatar answered Sep 28 '22 02:09

mfaani