【问题标题】:How to set content size of UIScrollView dynamically如何动态设置 UIScrollView 的内容大小
【发布时间】:2012-05-18 03:03:57
【问题描述】:

我有关于UIScrollview的问题。

故事是我有一个名为 ChartsView 的 UIView,我自己通过覆盖方法 drawRect() 重新绘制了它。绘图内容是动态生成的。所以直到运行时我才知道它的大小。问题是我如何/在哪里可以动态设置其超级视图的 (scrollView) 内容大小?对此有什么想法吗?

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

    }

【问题讨论】:

    标签: iphone objective-c uiview uiscrollview


    【解决方案1】:

    更简单的方法

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

    【讨论】:

    • 不错的把戏。它可以用于任何UIScrollView。为此 +1。
    • 我只想添加一件事。你们中的一些人可能喜欢将此代码放在 viwDidAppear 中:因为您的 UIView 可能在 viewDidAppear 之前但在调用 viewDidLoad 之后绘制;这将使您能够访问视图的最新高度。
    • lastObject 返回子视图数组中索引的最后一个对象,而不是屏幕上最底部的子视图。其实lastObject与view的位置无关,使用是错误的。
    • 返回弹出导航控制器时出现问题
    • 由于某种原因,这从未为我返回正确的子视图,即使它在我的 nib 文件中是正确的。我最终放弃了对最后一个子视图的调用,并硬编码了我希望它基于的子视图。
    【解决方案2】:

    不能 100% 保证 scrollView.subviews 数组中的最后一个对象将返回滚动视图中最高的 y 原点对象。 subviews 数组按 Z-Index 排列(即 subviews 数组中的最后一个对象将是堆叠最高的对象,并且将是滚动视图的子视图中最顶层的子视图。相反,使用基本排序功能更准确遍历子视图并获取具有最高 y 原点的对象。

    斯威夫特 4

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

    用法(在viewDidLayoutSubviews 或您的内容大小更新时):

    myScrollView.updateContentView()
    

    【讨论】:

    • 记得在调用updateContentView()之前添加这一行:self.view.layoutIfNeeded()
    • 这确实是我找到的唯一合法的解决方案。将内容高度限制为滚动视图的超级视图的经常重复的一个(如下)不起作用。我确实修改了它并将其变成了 UIView 的扩展,因此我可以查询人们倾向于嵌套在 UIScrollViewController 下的 scrollContent 视图,并找出在任何给定时间包含其子视图所需的高度。
    【解决方案3】:

    迅速 (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
    
    }
    

    当然,当您在滚动视图上只有固定数量的视图时。 IOS Rocks 方法可用,但对我不利,创造了比我需要的更多空间。

        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
    

    【讨论】:

      【解决方案4】:

      感谢 IOS Rocks 和 CularBytes 在这方面提供的线索。我发现上述解决方案有两个问题(对我来说):

      1. 我始终将容器 UIView 作为 UIScrollView 的唯一子项,因此我必须更新在该子项 UIView 中查找最后一个视图的方式(见下文)
      2. 我在 UIScrollView 中偏移我的内容,因此需要计算最后一个 UIView 的绝对位置(因此,相对于窗口而不是父视图)

      我还使用自动布局来固定 UIScrollView 的侧面,所以我不需要担心宽度(CularBytes 也是如此)。

      这是我想出的和工作的(在 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
      }
      

      我在 viewDidAppear() 方法中调用它。

      【讨论】:

        【解决方案5】:
        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
        }
        

        【讨论】:

        • 欢迎来到stackoverflow。请编辑您的答案,解释为什么您的代码 sn-p 解决了 OP 的问题。更多信息:How to Answer
        • 虽然有更多细节可能会更好,但答案很好,很有帮助。
        • 谢谢,我会从现在开始添加更多细节
        【解决方案6】:

        试试这个

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

        【讨论】:

        • 还是没有运气。我尝试找到一种解决方法,在绘制完成后在图表视图中设置滚动视图的内容大小。现在的问题是我可以使用什么样的 api 来获取图表视图的实际大小 ((UIScrollView *)[self superview]).contentSize = CGSizeMake (320, 480);
        【解决方案7】:

        动态计算 UIScrollview contentSize(取决于子视图框架)

        斯威夫特:

        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
            }
        }
        

        目标 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;
                               });
        }
        

        【讨论】:

          【解决方案8】:

          在故事板中使用滚动视图时,最好根据滚动视图中存在的视图数量来计算内容大小,而不是通过静态值以编程方式给出内容大小。 以下是动态获取内容大小的步骤。

          第 1 步: 添加 Scrollview 以在情节提要中查看并添加前导、尾随、顶部和底部约束(所有值均为零)。

          第 2 步: 不要直接在滚动视图上添加您需要的视图,首先向滚动视图添加一个视图(这将是我们所有 UI 元素的内容视图)。将以下约束添加到此视图。 前导、尾随、顶部和底部约束(所有值均为零)。 将等高、等宽添加到主视图(即包含滚动视图)。对于相等的高度,将优先级设置为低。 (这是设置内容大小的重要步骤)。 此内容视图的高度将根据添加到视图中的视图数量而定。假设您添加的最后一个视图是一个标签,并且他的 Y 位置为 420,高度为 20,那么您的内容视图将为 440。

          第 3 步:根据您的要求为您在内容视图中添加的所有视图添加约束。

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

          【讨论】:

          • 这是我搜索了很多后找到的最佳答案。
          • 我发现这行不通。您这里的方法告诉iOS滚动内容与屏幕一样高,但这可能不是真的。如果你在运行时降低滚动视图的高度(为下面引入的新 UI 元素腾出空间),最终会在滚动内容的底部出现空白区域。如果你的滚动内容比屏幕高怎么办?告诉 iOS 滚动内容仅与屏幕一样高,将在底部将其截断。还是我错过了什么?
          【解决方案9】:

          SWIFT 5

          您也可以通过调整UIScrollView.contentLayoutGuide 以编程方式更新UIScrollView 的contentView,下面是将contentview 限制为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
          

          【讨论】:

            【解决方案10】:

            试试这个 -

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

            【讨论】:

              【解决方案11】:

              将 contentInset 用于

              故事板

              以编程方式

              关注

              【讨论】:

                【解决方案12】:

                这是在 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
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  • 2021-10-23
                  • 2020-05-05
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-11-11
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多