自动布局正在改变 UIScrollView 的 contentOffset 旋转

Posted

技术标签:

【中文标题】自动布局正在改变 UIScrollView 的 contentOffset 旋转【英文标题】:Autolayout is changing UIScrollView's contentOffset on rotation 【发布时间】:2015-08-05 23:31:43 【问题描述】:

为了试验自动布局和 uiscrollview,我一直在使用 this example

我已经编辑为在滚动视图中包含 2 个视图,我已经设置了自动布局约束以将视图水平相邻放置,并且它们的大小设置为填充滚动视图框架。

UIView *beeView = [[[NSBundle mainBundle] loadNibNamed:@"BeeView" owner:nil options:nil] firstObject];
beeView.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:beeView];
UIView *beeView2 = [[[NSBundle mainBundle] loadNibNamed:@"BeeView" owner:nil options:nil] firstObject];
beeView2.backgroundColor= [UIColor orangeColor];
beeView2.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:beeView2];

NSDictionary *views = @@"beeView":beeView,@"beeView2":beeView2, @"scrollView":self.scrollView;
NSDictionary *metrics = @@"height" : @200;
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[beeView(==scrollView)][beeView2(==beeView)]|" options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:metrics views:views]];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[beeView(==scrollView)]|" options:kNilOptions metrics:metrics views:views]];

这很好地产生了我的意图。

但是,如果滚动视图的contentOffset 不为零并且设备从纵向旋转到横向,则滚动视图的内容偏移量会自动设置为32px。 (见截图)

我尝试保存 contentOffset 并在调用 scrollViewDidEndDecelerating: 时将其设置为这个保存的值,这可以工作,但很难看,因为滚动视图滚动到 32px 偏移量,然后返回到我想要的位置。

如何控制滚动视图的contentOffset?自动布局约束是否错误?在调整视图大小时,我是否可以添加额外的约束来控制contentOffset

【问题讨论】:

尝试将H:|[beeView(==scrollView)][beeView2(==beeView)]|更改为H:|-[beeView(==scrollView)]-[beeView2(==beeView)]-| 【参考方案1】:

32px 是从哪里来的?和你的左右scrollView margin有关吗?

每次更改页面时它都会保留错误的偏移量吗?如果是这种情况,您应该查看您的 scrollView 的 contentInsets 值。

否则,我通过分页管理滚动视图的旋转是观察滚动视图的 contentSize:

首先,当你加载视图时,添加观察者:

[self.scrollView addObserver:self forKeyPath:NSStringFromSelector(@selector(contentSize)) options:0 context:nil];

然后,当 contentSize 值发生变化时,调整 contentOffset:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 

    if (object == self.scrollView && [keyPath isEqualToString:NSStringFromSelector(@selector(contentSize))]) 

        //Note that you should track your page index
        self.scrollView.contentOffset = CGPointMake(self.pageIndex * self.scrollView.bounds.size.width, self.scrollView.contentOffset.y);

     else 
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    

最后,卸载scrollView时移除观察者:

[self.scrollView removeObserver:self forKeyPath:NSStringFromSelector(@selector(contentSize)) context:nil];

【讨论】:

32px 只是我测量的,一定是随机故障,但是 addObserve 方法工作得很好,谢谢! 这是个好建议;但是,请注意 UIKit 不保证支持 KVO。因此,虽然这种方法现在可能有效,但它可能会在未来的 ios 更新中中断。另一种方法是继承UIScrollView 并覆盖setContentSize:。请参阅下面的答案以获取示例。【参考方案2】:

在参考了之前的几篇帖子后:one & two。您似乎可以按照以下步骤之一找到您的解决方案:

    以编程方式:如果您的 UIScrollViews 的父 VC 不直接位于 Nav Stack -

~编辑~

    // Without a Navigation Controller

    self.automaticallyAdjustsScrollViewInsets = NO;

    // With a Navigation Controller

    self.parentViewController.automaticallyAdjustsScrollViewInsets = NO;
    self.automaticallyAdjustsScrollViewInsets = NO;

    界面生成器:属性检查器 -> 取消选中布局属性中的调整滚动视图插图

    李>

    如果 仍然不成功:在呈现的 View Controller 的 ViewWillLayoutSubviews 方法中尝试设置以下每个 UIScrollView 属性:

    self.scrollView.contentOffset = CGPointZero;
    self.scrollView.contentInset = UIEdgeInsetsZero;
    

我猜选项 1 和 2 的组合会起作用,具体取决于导航堆栈的结构。

【讨论】:

谢谢,但我没有使用任何类型的导航控制器,因此不会干扰它,我认为这是一些奇怪的自动布局故障。将 contentOffset 设置为零并不是特别有用,因为它只会将滚动视图设置回开头。 @DotSlashSlash 我很高兴您能够通过我们的答案之一解决您的问题。我很确定,既然您已经解释过您没有使用导航控制器,那么您是否在 VC 中使用 self.automaticallyAdjustsScrollViewInsets = NO; 也可以解决问题。【参考方案3】:

对于 iOS 11.0 及更高版本,以下方法可以解决我的问题:

if (@available(iOS 11.0, *))

    self.myScrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;

【讨论】:

谢谢。这解决了我的滚动视图在滚动时或手动设置 contentoffset 后有额外的间距?【参考方案4】:

我知道这是一个老问题,但如果它对任何人都有用 - 我创建了一个名为 LMPageViewUIScrollView 的子类,它自动应用必要的布局约束并调整旋转时的内容偏移量。该课程可作为 GitHub 上的 MarkupKit 项目的一部分获得。示例用法(Swift):

// Add 3 page views
pageView.addPage(view1)
pageView.addPage(view2)
pageView.addPage(view3)

// Show the 3rd page
pageView.currentPage = 2

可以在此处找到使用标记初始化页面视图的另一个示例:

PageViewController.swift PageViewController.xml

【讨论】:

以上是关于自动布局正在改变 UIScrollView 的 contentOffset 旋转的主要内容,如果未能解决你的问题,请参考以下文章

在自动布局中动态更改 UIScrollView 的大小

带有 UIScrollView 的纯自动布局

UIScrollView 使用纯自动布局不向右扩展

UIScrollView 和自动布局

UIScrollView 不使用自动布局约束

UIScrollView 不使用自动布局约束