开发者

SizeToFit on a UIScrollView's content size

开发者 https://www.devze.com 2023-01-22 10:23 出处:网络
A UIView has a SizeToFit method that will make the UIView fit all of it\'s subviews. Is there anything like that, which will just return the size that it calculates and not modify any view\'s frame.

A UIView has a SizeToFit method that will make the UIView fit all of it's subviews. Is there anything like that, which will just return the size that it calculates and not modify any view's frame.

I have several subviews on a UIScrollView and I want to do S开发者_Python百科izeToFit on the scroll view's contentSize, rather than it's frame. I have to do it on the contentSize, because I don't want to increase the "real" size of the UIScrollView, and the content is loaded dynamically and asynchronously so I can't do it manually when I add the subviews to the UIScrollView.


At the moment, this is the best I have:

CGRect contentRect = CGRectZero;
for (UIView *view in self.subviews)
    contentRect = CGRectUnion(contentRect, view.frame);
self.contentSize = contentRect.size;


If your function is called frequently, you wouldn't want to iterate over the subviews each time. Instead, whenever a subview is added, do a union of the current contentSize and the frame of the new subview.

- (void)didAddSubview:(UIView *)subview {
    CGRect tmp;
    tmp.origin = CGPointZero;
    tmp.size = self.contentSize;
    tmp = CGRectUnion(tmp,subview.frame);
    self.contentSize = tmp.size;
}


Found a solution here ScrollView ContentSize, you will be able to find the sizes of the subviews and find the combined content size and also options to say whether subviews are arranged horizontally or vertically.

Here is the code from the link above :

@interface UIScrollView(auto_size)
- (void) adjustHeightForCurrentSubviews: (int) verticalPadding;
- (void) adjustWidthForCurrentSubviews: (int) horizontalPadding;
- (void) adjustWidth: (bool) changeWidth andHeight: (bool) changeHeight withHorizontalPadding: (int) horizontalPadding andVerticalPadding: (int) verticalPadding;
@end

@implementation UIScrollView(auto_size)
- (void) adjustWidth: (bool) changeWidth andHeight: (bool) changeHeight withHorizontalPadding: (int) horizontalPadding andVerticalPadding: (int) verticalPadding {
    float contentWidth = horizontalPadding;
    float contentHeight = verticalPadding;
    for (UIView* subview in self.subviews) {
        [subview sizeToFit];
        contentWidth += subview.frame.size.width;
        contentHeight += subview.frame.size.height;
    }

    contentWidth = changeWidth ? contentWidth : self.superview.frame.size.width;
    contentHeight = changeHeight ? contentHeight : self.superview.frame.size.height;

    NSLog(@"Adjusting ScrollView size to %fx%f, verticalPadding=%d, horizontalPadding=%d", contentWidth, contentHeight, verticalPadding, horizontalPadding);
    self.contentSize = CGSizeMake(contentWidth, contentHeight);
}

- (void) adjustHeightForCurrentSubviews: (int) verticalPadding {
    [self adjustWidth:NO andHeight:YES withHorizontalPadding:0 andVerticalPadding:verticalPadding];
}

- (void) adjustWidthForCurrentSubviews: (int) horizontalPadding {
    [self adjustWidth:YES andHeight:NO withHorizontalPadding:horizontalPadding andVerticalPadding:0];
}
@end

Also make sure to have a look at the comments on the blog page as an alternative solution is provided.

-anoop


I have made one function for setting content size of scrollView try it and enjoy

First of all write this function...

-(void)setContentSizeOfScrollView:(UIScrollView*)scroll
{
    CGRect rect = CGRectZero;

    for(UIView * vv in [scroll subviews])
    {
        rect = CGRectUnion(rect, vv.frame);
    }

    [scroll setContentSize:CGSizeMake(rect.size.width, rect.size.height)];
}

then after adding all elements just call this function and pass your scroll view as argument.....

[self setContentSizeOfScrollView:self.YourScrollView];

I am 100% sure that it will work.........


Here is the answer for Swift 3.0

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    var contentRect: CGRect = CGRect.zero
    for view in self.scrollView.subviews {
        debugPrint("view frame \(view.frame)")
        contentRect = contentRect.union(view.frame)
        debugPrint("frame \(contentRect)")
    }
    self.scrollView.contentSize = contentRect.size
    debugPrint("content size \(self.scrollView.contentSize)")
}


While William's answer was very useful, it didn't work perfectly since the scrollbars are also subviews of the scrollview. Turns out, the trick is to hide them before calculating the content size. Here's what the Swift 3 code would look like:

override func viewDidLayoutSubviews() {
    scrollView.layoutSubviews()

    // disable scrollbars so they don't interfere during resizing
    scrollView.showsVerticalScrollIndicator = false
    scrollView.showsHorizontalScrollIndicator = false

    scrollView.contentSize = scrollView.subViewArea()

    // re-enable the scrollbars
    scrollView.showsVerticalScrollIndicator = true
    scrollView.showsHorizontalScrollIndicator = true
}

You'll also need to add the following extension somewhere in your project:

extension UIScrollView {
    func subViewArea() -> CGSize {
        var rect = CGRect.zero
        for subview in subviews {
            rect = rect.union(subview.frame)
        }

        return rect.size
    }
}


Put a UIView into the UIScrollView that has the same width and height and add all subviews to IT rather than the scrollview. Then set the clipToBounds property on the scrollview to TRUE, and call sizeToFit on your UIView. You can also set the opacity of the UIView's background to 0 making it invisible, but still showing its subviews.


In swift 2.1, add this to your view controller:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    let aggregateView = contentView.subviews.reduce(CGRect())
        { aggRect, view in aggRect.union(view.frame) }

    scrollView.contentSize = aggregateView.size

}


It's not enough that union frame sizes to calculate the content size. Because sometimes, your subview needs to overlap, and sometimes you have margins between subviews. You have to know and calculate origin of subviews.

Union approach gives same content hight for A and B designs. But if you know biggest last subview, you can calculate right content hight:

SizeToFit on a UIScrollView's content size

This extension can calculate the real content height.

extension UIScrollView {

 func adjustContentHeight(bottomPadding: Int = 0) {

    var lastSub: CGRect = .zero
    self.subviews.forEach({
        let sub = $0.frame
        if(sub.maxY > lastSub.maxY) {
            lastSub = sub
        }
    })

    self.contentSize.height = lastSub.maxY + CGFloat(bottomPadding)
 }   
} 


Swift 2 extension:

Usage:

scrollView.contentResize
scrollView.contentWidthResize
scrollView.contentHeightResize

extension UIScrollView {

    ///Will resize content size to fit all subviews.
    func contentResize(){

        var w = CGFloat(0)
        var h = CGFloat(0)

        for view in self.subviews{

            let fw = view.frame.origin.x + view.frame.width
            let fh = view.frame.origin.y + view.frame.height

            w = max(fw, w)
            h = max(fh, h)

        }

        contentSize = CGSize(width: w, height: h)

    }

    ///Will resize content width size to fit all subviews.
    func contentWidthResize(){

        var w = CGFloat(0)

        for view in self.subviews{

            let fw = view.frame.origin.x + view.frame.width

            w = max(fw, w)

        }

        contentSize.width = w

    }

    ///Will resize content height size to fit all subviews.
    func contentHeightResize(){

        var h = CGFloat(0)

        for view in self.subviews{

            let fh = view.frame.origin.y + view.frame.height

            h = max(fh, h)

        }

        contentSize.height = h

    }

}
0

精彩评论

暂无评论...
验证码 换一张
取 消