Roger Lee
Roger Lee

Reputation: 471

How to set content size of UIScrollView dynamically

I got question about UIScrollview.

The story is I have a UIView named ChartsView which I re-draw it myself by override method drawRect(). The content of drawing was generated dynamically. So I do not know its size until runtime. The question is how/where can I set its super view's (scrollView) content size dynamically?Any idea about that?

    - (void)viewDidLoad
    {
        [super viewDidLoad];

// not this way. it's fixed size.....
        ChartsView *chartsView = [[ChartsView alloc]initWithFrame:CGRectMake(0, 0, 320, 800)]; 

        self.scrollView.contentSize = chartsView.frame.size;

        [self.scrollView addSubview:chartsView];

    }

Upvotes: 39

Views: 106616

Answers (12)

Mr.SwiftOak
Mr.SwiftOak

Reputation: 1834

SWIFT 5

You can also programmatically update contentView of the UIScrollView by adjusting UIScrollView.contentLayoutGuide , here is the example of constraining contentview to the height of yourView:

self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.heightAnchor.constraint(equalTo: self.view.heightAnchor).isActive = true
self.scrollView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
self.scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.scrollView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true

self.scrollView.contentLayoutGuide.heightAnchor.constraint(equalTo: yourView.heightAnchor, multiplier: 1).isActive = true
self.scrollView.contentLayoutGuide.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 1).isActive = true
self.scrollView.contentLayoutGuide.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.scrollView.contentLayoutGuide.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true

Upvotes: 2

Ryan110
Ryan110

Reputation: 740

use contentInset with

storyboard

and

programmatically

follow this works best

Upvotes: 0

Charlton Provatas
Charlton Provatas

Reputation: 2274

It is not 100% guaranteed that the last object in scrollView.subviews array will return the highest y-origin object in your scroll view. The subviews array is arranged by Z-Index (i.e the last object in the subviews array will be the object stacked the highest and will be the top-most subview in the scroll view's subviews. Instead it is more accurate to use a basic sort function to iterate through the subviews and get the object with the highest y-origin.

Swift 4

extension UIScrollView {
    func updateContentView() {
        contentSize.height = subviews.sorted(by: { $0.frame.maxY < $1.frame.maxY }).last?.frame.maxY ?? contentSize.height
    }
}

Usage (in viewDidLayoutSubviews or whenever your content size updates):

myScrollView.updateContentView()

Upvotes: 25

Mr.Javed Multani
Mr.Javed Multani

Reputation: 13236

While using scrollviews in storyboard it’s better to calculate content size according to number of views present in scrollview rather than giving content size programmatically with static value. Here are the steps to get content size dynamically.

Step 1 : Add Scrollview to view in storyboard and add leading, trailing, top and bottom constraints (All values are zero).

Step 2 : Don’t add directly views which you need on directly scrollview, First add one view to scrollview (that will be our content view for all UI elements). Add below constraints to this view. Leading, trailing, top and bottom constraints (All values are zero). Add equal height, equal width to Main view (i.e. which contains scrollview). For equal height set priority to low. (This is the important step for setting content size). Height of this content view will be according to the number of views added to the view. let say if you added last view is one label and his Y position is 420 and height is 20 then your content view will be 440.

Step 3 : Add constraints to all of views which you added within content view as per your requirement.

enter image description here

enter image description here

Ref:https://medium.com/@javedmultani16/uiscrollview-dynamic-content-size-through-storyboard-in-ios-fb873e9278e

Upvotes: 3

Asad Jamil
Asad Jamil

Reputation: 198

override func viewDidAppear(_ animated: Bool) {
    var height: CGFloat
    let lastView = self.scrollView.subviews[0].subviews.last!

    let lastViewYPos = lastView.convert(lastView.frame.origin, to: nil).y  
    let lastViewHeight = lastView.frame.size.height

    height = lastViewYPos + lastViewHeight
    scrollView.contentSize.height = height
}

Upvotes: 3

Karen Hovhannisyan
Karen Hovhannisyan

Reputation: 1248

Calculate UIScrollview contentSize dynamically(depending subviews frame)

Swift:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    DispatchQueue.main.async {
        var contentRect = CGRect.zero

        for view in self.scrollView.subviews {
           contentRect = contentRect.union(view.frame)
        }

        self.scrollView.contentSize = contentRect.size
    }
}

Objective C:

 - (void)viewDidLayoutSubviews {
     [super viewDidLayoutSubviews];

     dispatch_async(dispatch_get_main_queue(), ^ {
                        CGRect contentRect = CGRectZero;

                        for(UIView *view in scrollView.subviews)
                           contentRect = CGRectUnion(contentRect,view.frame);

                           scrollView.contentSize = contentRect.size;
                       });
}

Upvotes: 4

Bogdan Ustyak
Bogdan Ustyak

Reputation: 5839

This is an extension written on Swift 3

extension UIScrollView {
    func updateContentViewSize() {
        var newHeight: CGFloat = 0
        for view in subviews {
            let ref = view.frame.origin.y + view.frame.height
            if ref > newHeight {
                newHeight = ref
            }
        }
        let oldSize = contentSize
        let newSize = CGSize(width: oldSize.width, height: newHeight + 100)
        contentSize = newSize
    }
}

Upvotes: 0

Abhishek Mishra
Abhishek Mishra

Reputation: 1655

Try this -

- (void)viewDidLoad
    {
        [super viewDidLoad];

// not this way. it's fixed size.....
        ChartsView *chartsView = [[ChartsView alloc]initWithFrame:CGRectMake(0, 0, 320, 800)]; 

        //self.scrollView.contentSize = chartsView.frame.size;
        [self.scrollView setContentSize:CGSizeMake(chartsView.frame.size.width, chartsView.frame.size.height)];
        [self.scrollView addSubview:chartsView];

    }

Upvotes: 1

the_dude_abides
the_dude_abides

Reputation: 563

Thanks to IOS Rocks and CularBytes for the leads on this. I found two problems (for me) with the above solutions:

  1. I always have a container UIView as the only child of my UIScrollView, so I had to update the way I look up the last view in that child UIView (see below)
  2. I am offset'ing my content in the UIScrollView and as such needed to calculate the absolute position of that last UIView (so, relative to the window not the parent view)

Also I am using autolayout to pin the sides of the UIScrollView, so I don't need to worry about width (as is CularBytes).

Here's what I came up with and works (in Swift 3.0):

func setScrollViewContentSize() {

    var height: CGFloat
    let lastView = self.myScrollView.subviews[0].subviews.last!
    print(lastView.debugDescription) // should be what you expect

    let lastViewYPos = lastView.convert(lastView.frame.origin, to: nil).y  // this is absolute positioning, not relative
    let lastViewHeight = lastView.frame.size.height

    // sanity check on these
    print(lastViewYPos)
    print(lastViewHeight)

    height = lastViewYPos + lastViewHeight

    print("setting scroll height: \(height)")

    myScrollView.contentSize.height = height
}

I call this in my viewDidAppear() method.

Upvotes: 4

CularBytes
CularBytes

Reputation: 10321

swift (2.0)

@IBOutlet weak var btnLatestButton: UIButton!

override func viewDidLoad() {
    super.viewDidLoad()

    let height = btnLatestButton.frame.size.height
    let pos = btnLatestButton.frame.origin.y
    let sizeOfContent = height + pos + 10
    scrollview.contentSize.height = sizeOfContent

}

Above ofcourse is when you just have a fixed amount of views on your scrollview. IOS Rocks method is available but was not good for me, created more space then I needed.

    let lastView : UIView! = scrollview.subviews.last
    let height = lastView.frame.size.height
    let pos = lastView.frame.origin.y
    let sizeOfContent = height + pos + 10
    scrollview.contentSize.height = sizeOfContent

Upvotes: 7

IOS Rocks
IOS Rocks

Reputation: 2127

One more easy way

- (void)viewDidLoad
{
    float sizeOfContent = 0;
    UIView *lLast = [scrollView.subviews lastObject];
    NSInteger wd = lLast.frame.origin.y;
    NSInteger ht = lLast.frame.size.height;

    sizeOfContent = wd+ht;

    scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, sizeOfContent);
}

Upvotes: 62

MrWaqasAhmed
MrWaqasAhmed

Reputation: 1489

try this

- (void)viewDidLoad
        {
            [super viewDidLoad];

    // not this way. it's fixed size.....
            ChartsView *chartsView = [[ChartsView alloc]initWithFrame:CGRectMake(0, 0, 320, 800)]; 

            self.scrollView.contentSize = chartsView.frame.size;
            [self.scrollView setNeedsDisplay];
            [self.scrollView addSubview:chartsView];

    }

Upvotes: 1

Related Questions