在 UIScrollView 子类中设置 contentOffset 的正确位置在哪里?

Posted

技术标签:

【中文标题】在 UIScrollView 子类中设置 contentOffset 的正确位置在哪里?【英文标题】:Where is the proper place to set contentOffset in UIScrollView sub-class? 【发布时间】:2018-02-28 10:55:31 【问题描述】:

我正在开发一个继承自UIScrollView 的视图,要求它应该从取决于视图大小的contentOffset.y 位置开始。具体来说,我想在 3 x 视图高度的内容中向下启动一个屏幕。 像这样:

- (void)configureStartCondition 
    self.contentSize = CGSizeMake(self.bounds.size.width, self.bounds.size.height * 3.0);
    self.contentOffset = CGPointMake(0.0, self.bounds.size.height * 1.0);

视图本身与 Storyboard 中的约束相关联,就像任何视图一样。当它工作时,框架最初会为视图提供它在故事板中的大小,然后当设备大小已知时,视图的大小将更改为其最终大小。这就是它应该如何工作的,我对此很好。我的问题是我在哪里打电话给configureStartCondition

一个明显的解决方案是将此代码放在setFrame: 中,但它不起作用。 setFrame: 仅针对初始帧大小调用,这可能是也可能不是最终大小。这是为什么呢?

// NOT working
- (void)setFrame:(CGRect)frame 
    [super setFrame:frame];
    [self configureStartCondition];

一个更常见的地方是layoutSubview,我通常在那里进行这种设置。然而,由于它是UIScrollView,所以当用户滚动视图时,layoutSubview 经常被称为非常。这意味着我需要保存最后一个高度并对其进行比较以使事情正常进行,然后运行此测试数百万次才能进行初始化。对我来说,这就像一个杂物。

// Working, but ugly
- (void)layoutSubview 
    [super layoutSubviews];

    if (self.bounds.size.height != self.savedHeight) 
        self.savedHeight = self.bounds.size.height;
        [self configureStartCondition];
    

    // Do layout stuff

另一个看起来不错的地方是setBounds:。它被调用以更改视图大小,但由于contentOffset 属性与bounds 属性相关联,因此我实际上在这里收到与layoutSubviews 一样多的调用。

那么,有没有更好的地方,或者更好的方法呢?

附带问题,在我的情况下不太重要,但是可以从情节提要中设置内容偏移量吗?

编辑:Swift 中的解决方案也很好。

【问题讨论】:

您是否尝试过添加观察者来处理边界发生变化的情况? 我现在试过了,但它的调用次数与覆盖setBounds: 一样多,请参阅上面我对setBounds: 的测试。 @rmaddy 为什么要删除swift 标签?我接受任何一种语言的解决方案。这是错误使用标签吗? 标签代表你的问题实际上是关于什么的。您的问题与 Swift 编程语言无关。仅仅因为你愿意接受 Swift 代码的答案并不意味着你的问题是关于 Swift 的。 感谢您的澄清。 【参考方案1】:

setContentSize: 非常适合我。它仅在大小更改时调用。

PS:我要检查的代码:(抱歉 swift 但 UIKit 没有区别)

class CustomScrollView: UIScrollView 
    override var contentSize: CGSize
        didSet
            var offset = contentOffset
            offset.y = contentSize.height / 2.0
            contentOffset = offset
        
    


【讨论】:

对不起,但在我的例子中,正如你在上面看到的,我自己设置了 contentSize,它应该是与视图最终高度相关的大小。您的解决方案将创建一个无限循环。 我没注意到,抱歉。您知道实际上边界必须由 contentOffset 和 contentSize 组成吗?所以基本上你试图做的事情没有意义。 也许您想在某个特定时刻更改滚动视图的“边界”(conte size + offset)?例如,当控制器执行layoutSubviewswillAppear 我想调整contentSize与最终视图大小成比例,然后设置一个初始contentOffset。为什么这没有意义? 有两个“最终”尺寸。 External(frame) - 表示视图如何放置在它的容器中,Internal(bounds) - 表示它的子视图可用的空间。据我了解,您希望在更改内部最终尺寸时更改内部最终尺寸。肯定会出现无限循环。但是您通过if (self.bounds.size.height != self.savedHeight) 避免了它您可以在我提出的代码中使用相同的条件。如果它适用于setBounds,它将适用于setContentSize。但是当用户滚动时内容大小不会改变。

以上是关于在 UIScrollView 子类中设置 contentOffset 的正确位置在哪里?的主要内容,如果未能解决你的问题,请参考以下文章

在 UIScrollview 中设置 size 属性

在 UIPageViewController 中设置 UIScrollView 的内容大小

如何在 swift 中设置 UIScrollView 内的全宽图像

尝试在 UIScrollview 中设置宽图像,高度固定

UIScrollView 实现委托

iOS - 如何在 UIPageViewController 中设置 UIScrollView 以在重新启用后开始滚动