UIScrollView 的内容大小上的 SizeToFit

Posted

技术标签:

【中文标题】UIScrollView 的内容大小上的 SizeToFit【英文标题】:SizeToFit on a UIScrollView's content size 【发布时间】:2010-10-25 20:49:54 【问题描述】:

UIView 有一个 SizeToFit 方法,它可以使 UIView 适合它的所有子视图。有没有类似的东西,它只会返回它计算的大小而不修改任何视图的框架。

我在 UIScrollView 上有几个子视图,我想对滚动视图的 contentSize 执行 SizeToFit,而不是它的框架。我必须在 contentSize 上做,因为我不想增加 UIScrollView 的“真实”大小,并且内容是动态异步加载的,所以当我将子视图添加到 UIScrollView 时我不能手动做.

【问题讨论】:

开始赏金。目前,我最好的答案是遍历子视图并查看它们的框架。我想知道是否有更好的方法。 SizeThatFits:似乎没有帮助。 尝试覆盖 sizeThatFits: 以进行所需的计算。 Swift extension 【参考方案1】:

目前,这是我拥有的最好的:

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

【讨论】:

这个方法很棒,但是......由于某种原因,如果手机处于横向模式,它对我不起作用。有什么想法吗? 如果滚动条设置为显示,此解决方案不考虑已经作为 UIScrollView 子视图存在的 UIImageViews(请参阅this SO post)。过滤掉这些的一种方法是将用户想要包含在 for 循环计算中的子视图的 tag 属性设置为相同的非零值,并使用 if 语句进行过滤。 @adamjansch 或使用isKindOfClass: 代替tags。或使用:[self.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"class = %@", [UILabel class]]]; - 不正确,因为它使用 class 而不是 isKindOfClass:,但对于大多数用途来说已经足够了。【参考方案2】:

如果你的函数被频繁调用,你就不想每次都迭代子视图。相反,每当添加子视图时,将当前 contentSize 与新子视图的框架合并。

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

【讨论】:

在某些情况下可能是一个很好的解决方案。不幸的是,我的子视图经常调整大小。所以就我而言,这不是一个好的解决方案。 @WilliamJockusch 如果子视图经常调整大小,您可以为framebounds 添加一个KVO 观察者并相应地更新contentSize。这会很棘手,但根据子视图的数量,可能是值得的。【参考方案3】:

在ScrollView ContentSize 找到了一个解决方案,您将能够找到子视图的大小并找到组合的内容大小,还可以选择说明子视图是水平排列还是垂直排列。

这是上面链接中的代码:

@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

另外,请务必查看博客页面上的 cmets,因为它提供了替代解决方案。

-anoop

【讨论】:

【参考方案4】:

我做了一个设置scrollView内容大小的功能试试看

首先编写这个函数...

-(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)];

然后在添加所有元素后调用此函数并将滚动视图作为参数传递.....

[self setContentSizeOfScrollView:self.YourScrollView];

我 100% 确信它会起作用.........

【讨论】:

【参考方案5】:

这是 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)")

【讨论】:

【参考方案6】:

虽然William's answer 非常有用,但它并不能完美地工作,因为滚动条也是滚动视图的子视图。事实证明,诀窍是在计算内容大小之前隐藏它们。下面是 Swift 3 代码的样子:

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

您还需要在项目的某处添加以下扩展:

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

        return rect.size
    

【讨论】:

【参考方案7】:

将 UIView 放入具有相同宽度和高度的 UIScrollView 中,并将所有子视图添加到 IT 而不是滚动视图。然后将滚动视图上的 clipToBounds 属性设置为 TRUE,并在 UIView 上调用 sizeToFit。您还可以将 UIView 的背景的不透明度设置为 0,使其不可见,但仍显示其子视图。

【讨论】:

我刚刚意识到它应该是相反的:封闭视图应该是 UIView,而内部(更大)视图应该是透明的 UIScrollView。那么它应该可以工作了。 你确定吗,你在回答中写的有道理,我虽然知道你在那里所说的,但似乎有点“不恰当” 抱歉反应迟了...上周我在自己的项目中部分使用了上述解决方案。不幸的是,sizeToFit 似乎对 UIView 中的 UIScrollView 没有任何影响。因此,在将所有子视图添加到其中之后,我手动设置了它的 contentSize。这样做它甚至不再需要封闭的 UIView。 您可以使用 for 循环遍历 UIScrollView 的所有子视图并总结它们的宽度/高度属性,然后手动将滚动视图的 contentSize 设置为该值。【参考方案8】:

在 swift 2.1 中,将其添加到您的视图控制器中:

override func viewDidLayoutSubviews() 
    super.viewDidLayoutSubviews()

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

    scrollView.contentSize = aggregateView.size


【讨论】:

【参考方案9】:

联合框架大小来计算内容大小是不够的。因为有时,您的子视图需要重叠,有时您的子视图之间有边距。您必须知道并计算子视图的来源。

联合方法为 A 和 B 设计提供相同的内容高度。但是如果你知道最大的最后一个子视图,你可以计算出正确的内容高度:

这个扩展可以计算真实的内容高度。

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)
    
 

【讨论】:

【参考方案10】:

Swift 2 扩展:

用法:

scrollView.contentResizescrollView.contentWidthResizescrollView.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

    


【讨论】:

view.wview.h 未定义为 mate。 @YoussefSami 已编辑...,如果您觉得有用,请考虑删除不赞成票并投票,感谢您提供的信息。

以上是关于UIScrollView 的内容大小上的 SizeToFit的主要内容,如果未能解决你的问题,请参考以下文章

UIScrollView 神秘地改变框架和内容大小

在 UIScrollView 中调整内容视图的大小不起作用

UIScrollView

UIScrollView,滑动,缩放

在UIScrollView子类中设置contentOffset的适当位置在哪里?

UIScrollView bounds.size