如何自动调整UIScrollView的大小以适合其内容

时间:2010-05-31 14:54:48

标签: iphone ios objective-c cocoa-touch uiscrollview

有没有办法让UIScrollView自动调整滚动内容的高度(或宽度)?

类似的东西:

[scrollView setContentSize:(CGSizeMake(320, content.height))];

21 个答案:

答案 0 :(得分:292)

我遇到过根据其中包含的子视图更新UIScrollView内容大小的最佳方法:

  

<强>目标C

CGRect contentRect = CGRectZero;

for (UIView *view in self.scrollView.subviews) {
    contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;
  

<强>夫特

var contentRect = CGRect.zero

for view in mainScrollView.subviews {
   contentRect = contentRect.union(view.frame)
}
mainScrollView.contentSize = contentRect.size

答案 1 :(得分:75)

UIScrollView不会自动知道其内容的高度。你必须自己计算身高和宽度

之类的东西做
CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scrollView.subviews)
{
   scrollViewHeight += view.frame.size.height;
}

[scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))];

但这只有在视图低于另一视图时才有效。如果您有一个彼此相邻的视图,如果您不想将滚动条的内容设置为大于实际值,则只需添加一个高度。

答案 2 :(得分:37)

解决方案,如果您正在使用自动布局:

  • 在所有相关视图上设置translatesAutoresizingMaskIntoConstraintsNO

  • 使用滚动视图外部的约束来定位和调整滚动视图。

  • 使用约束在滚动视图中布置子视图,确保约束绑定到滚动视图的所有四个边,并且不依赖于滚动视图来获取它们大小

来源: https://developer.apple.com/library/ios/technotes/tn2154/_index.html

答案 3 :(得分:35)

我将此添加到Espuz和JCC的答案中。它使用子视图的y位置,不包括滚动条。 编辑使用可见的最低子视图的底部。

+ (CGFloat) bottomOfLowestContent:(UIView*) view
{
    CGFloat lowestPoint = 0.0;

    BOOL restoreHorizontal = NO;
    BOOL restoreVertical = NO;

    if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
    {
        if ([(UIScrollView*)view showsHorizontalScrollIndicator])
        {
            restoreHorizontal = YES;
            [(UIScrollView*)view setShowsHorizontalScrollIndicator:NO];
        }
        if ([(UIScrollView*)view showsVerticalScrollIndicator])
        {
            restoreVertical = YES;
            [(UIScrollView*)view setShowsVerticalScrollIndicator:NO];
        }
    }
    for (UIView *subView in view.subviews)
    {
        if (!subView.hidden)
        {
            CGFloat maxY = CGRectGetMaxY(subView.frame);
            if (maxY > lowestPoint)
            {
                lowestPoint = maxY;
            }
        }
    }
    if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
    {
        if (restoreHorizontal)
        {
            [(UIScrollView*)view setShowsHorizontalScrollIndicator:YES];
        }
        if (restoreVertical)
        {
            [(UIScrollView*)view setShowsVerticalScrollIndicator:YES];
        }
    }

    return lowestPoint;
}

答案 4 :(得分:13)

对于任何懒得转换它的人来说,这是swift中接受的答案:)

var contentRect = CGRectZero
for view in self.scrollView.subviews {
    contentRect = CGRectUnion(contentRect, view.frame)
}
self.scrollView.contentSize = contentRect.size

答案 5 :(得分:7)

这是@ leviatan答案的Swift 3改编版:

<强> EXTENSION

token.destroy(delete_from_facebook: true)

<强> USAGE

#include <GLFW/glfw3.h>

#define SCREEN_W 640
#define SCREEN_H 480

int main(int argc, char * argv[]) {

    glfwInit();

    GLFWwindow* window = glfwCreateWindow(SCREEN_W, SCREEN_H, "GLFW Window", NULL, NULL);

    glfwMakeContextCurrent(window);

    float lineVertices[] = {
        0, 0, 0,
        SCREEN_W, SCREEN_H, 0
    };

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glViewport(0.0f, 0.0f, SCREEN_W, SCREEN_H);
    glOrtho(0, SCREEN_W, 0, SCREEN_H, 0, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    while (!glfwWindowShouldClose(window)) {
        glClear(GL_COLOR_BUFFER_BIT);

        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(3, GL_FLOAT, 0, lineVertices);
        glDrawArrays(GL_LINES, 0, 2);
        glDisableClientState(GL_VERTEX_ARRAY);

        glfwSwapBuffers(window);

        glfwPollEvents();
    }

    glfwDestroyWindow(window);

    glfwTerminate();

    return 0;
}

非常好用!

答案 6 :(得分:6)

以下扩展功能对 Swift 有帮助。

extension UIScrollView{
    func setContentViewSize(offset:CGFloat = 0.0) {
        // dont show scroll indicators
        showsHorizontalScrollIndicator = false
        showsVerticalScrollIndicator = false

        var maxHeight : CGFloat = 0
        for view in subviews {
            if view.isHidden {
                continue
            }
            let newHeight = view.frame.origin.y + view.frame.height
            if newHeight > maxHeight {
                maxHeight = newHeight
            }
        }
        // set content size
        contentSize = CGSize(width: contentSize.width, height: maxHeight + offset)
        // show scroll indicators
        showsHorizontalScrollIndicator = true
        showsVerticalScrollIndicator = true
    }
}

逻辑与给定答案相同。但是,它忽略了UIScrollView中的隐藏视图,并且在滚动指示器设置为隐藏后执行计算。

此外,还有一个可选的函数参数,您可以通过将参数传递给函数来添加偏移值。

答案 7 :(得分:5)

伟大&amp;来自@leviathan的最佳解决方案。只需使用FP(函数式编程)方法转换为swift。

self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), { 
  CGRectUnion($0, $1.frame) 
}.size

答案 8 :(得分:4)

您可以通过计算哪个孩子“达到进一步”来获取UIScrollView内容的高度。要计算这个,你必须考虑原点Y(开始)和项目高度。

float maxHeight = 0;
for (UIView *child in scrollView.subviews) {
    float childHeight = child.frame.origin.y + child.frame.size.height;
    //if child spans more than current maxHeight then make it a new maxHeight
    if (childHeight > maxHeight)
        maxHeight = childHeight;
}
//set content size
[scrollView setContentSize:(CGSizeMake(320, maxHeight))];

通过这种方式,项目(子视图)不必直接堆叠在另一个之下。

答案 9 :(得分:3)

因为scrollView可以有其他scrollViews或不同的inDepth子视图树,所以最好以递归方式运行。 enter image description here

Swift 2

extension UIScrollView {
    //it will block the mainThread
    func recalculateVerticalContentSize_synchronous () {
        let unionCalculatedTotalRect = recursiveUnionInDepthFor(self)
        self.contentSize = CGRectMake(0, 0, self.frame.width, unionCalculatedTotalRect.height).size;
    }

    private func recursiveUnionInDepthFor (view: UIView) -> CGRect {
        var totalRect = CGRectZero
        //calculate recursevly for every subView
        for subView in view.subviews {
            totalRect =  CGRectUnion(totalRect, recursiveUnionInDepthFor(subView))
        }
        //return the totalCalculated for all in depth subViews.
        return CGRectUnion(totalRect, view.frame)
    }
}

用法

scrollView.recalculateVerticalContentSize_synchronous()

答案 10 :(得分:3)

我想出了另一种基于@emenegro解决方案的解决方案

NSInteger maxY = 0;
for (UIView* subview in scrollView.subviews)
{
    if (CGRectGetMaxY(subview.frame) > maxY)
    {
        maxY = CGRectGetMaxY(subview.frame);
    }
}
maxY += 10;
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, maxY)];

基本上,我们找出视图中最下面的元素,并在底部添加10px填充

答案 11 :(得分:2)

或者只是这样做:

int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];

(此解决方案是我在此页面中添加的评论。在此评论获得19票之后,我决定将此解决方案添加为正式答案,以造福社区!)

答案 12 :(得分:1)

对于使用reduce的swift4:

self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
   return $0.union($1.frame)
}).size

答案 13 :(得分:1)

包装Richy的代码我创建了一个自动化的自定义UIScrollView类 内容完全调整大小!

SBScrollView.h

@interface SBScrollView : UIScrollView
@end

SBScrollView.m:

@implementation SBScrollView
- (void) layoutSubviews
{
    CGFloat scrollViewHeight = 0.0f;
    self.showsHorizontalScrollIndicator = NO;
    self.showsVerticalScrollIndicator = NO;
    for (UIView* view in self.subviews)
    {
        if (!view.hidden)
        {
            CGFloat y = view.frame.origin.y;
            CGFloat h = view.frame.size.height;
            if (y + h > scrollViewHeight)
            {
                scrollViewHeight = h + y;
            }
        }
    }
    self.showsHorizontalScrollIndicator = YES;
    self.showsVerticalScrollIndicator = YES;
    [self setContentSize:(CGSizeMake(self.frame.size.width, scrollViewHeight))];
}
@end

使用方法:
只需将.h文件导入视图控制器即可 声明一个SBScrollView实例而不是普通的UIScrollView实例。

答案 14 :(得分:1)

像这样设置动态内容大小。

 self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);

答案 15 :(得分:1)

大小取决于其中加载的内容以及剪切选项。如果是文本视图,那么它还取决于包装,文本行数,字体大小等等。你几乎不可能自己计算。好消息是,它是在视图加载后和viewWillAppear中计算出来的。在此之前,它都是未知的,并且内容大小将与帧大小相同。但是,在viewWillAppear方法和之后(如viewDidAppear)内容大小将是实际的。

答案 16 :(得分:0)

它真的取决于内容:content.frame.height可能会给你你想要的东西?取决于内容是单一内容还是一组内容。

答案 17 :(得分:0)

import UIKit

class DynamicSizeScrollView: UIScrollView {

    var maxHeight: CGFloat = UIScreen.main.bounds.size.height
    var maxWidth: CGFloat = UIScreen.main.bounds.size.width

    override func layoutSubviews() {
        super.layoutSubviews()
        if !__CGSizeEqualToSize(bounds.size,self.intrinsicContentSize){
            self.invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        let height = min(contentSize.height, maxHeight)
        let width = min(contentSize.height, maxWidth)
        return CGSize(width: width, height: height)
    }

}

答案 18 :(得分:0)

为什么不单行代码?

_yourScrollView.contentSize = CGSizeMake(0, _lastView.frame.origin.y + _lastView.frame.size.height);

答案 19 :(得分:0)

我认为这可以是更新UIScrollView内容视图大小的简洁方法。

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 + 20)
        contentSize = newSize
    }
}

答案 20 :(得分:0)

我还发现了leviathan的答案是最好的。然而,它正在计算一个奇怪的高度。循环遍历子视图时,如果将scrollview设置为显示滚动指示符,则这些将位于子视图数组中。在这种情况下,解决方案是在循环之前暂时禁用滚动指示器,然后重新建立其先前的可见性设置。

-(void)adjustContentSizeToFit是UIScrollView的自定义子类上的公共方法。

-(void)awakeFromNib {    
    dispatch_async(dispatch_get_main_queue(), ^{
        [self adjustContentSizeToFit];
    });
}

-(void)adjustContentSizeToFit {

    BOOL showsVerticalScrollIndicator = self.showsVerticalScrollIndicator;
    BOOL showsHorizontalScrollIndicator = self.showsHorizontalScrollIndicator;

    self.showsVerticalScrollIndicator = NO;
    self.showsHorizontalScrollIndicator = NO;

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

    self.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
    self.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;
}