UIScrollView的Autolayout问题

UIScrollView本质上是简单的,通过frame指定了视窗的位置大小,通过contentSize指定了内容的大小(可滚动的区域)。

Autolayout本质上也是简单的,通过指定相对和绝对量,控制UI组件的位置和大小。

这两个本质上简单的东西碰撞到一起后,产生了可怕的火花。

首先,contentSize的概念隐藏了,我们也不愿意去计算实际的size去指定scrollview的contentSize。

所以,我们最常见的做法是用一个空的view,通常命名为contentView,去包含原本属于scrollView的一切子视图。然后将contentView插入到scrollView中,并且用contentView去对scrollView进行约束:

[_contentView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(_scrollView);
    }];

contentView的size会撑开scrollView的contentSize,然后用实际的view去撑开contentView的size,看上去似乎更简单了。

愿景是美好的,需求是复杂的,iOS6是坑爹的。iOS6有一个bug,在更新约束时,无耻的假定了scrollView的contentOffset是(0,0)。所以如果有需求是根据scrollview的滚动动态的调整scrollView的宽度或高度,杯具就发生了。

- (void)switchToIndex:(NSInteger)index
{

    if (index == [_views indexOfObject:_activeView]) {
        return;
    }

    UIView *targetView = _views[index];

    self.activeView = targetView;


	[_scrollView setContentOffset:CGPointMake(targetView.left, 0) animated:YES];


    [self.view setNeedsUpdateConstraints]; //对于iOS6,此时更新约束,会发生杯具,表现为contentView的frame会跑到屏幕之外

}

所以,不得已,需要写一些恶心的代码解决iOS6的问题:

- (void)switchToIndex:(NSInteger)index
{

    if (index == [_views indexOfObject:_activeView]) {
        return;
    }
    UIView *targetView = _views[index];
    self.activeView = targetView;

    if( floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_7_0 ) {
        [_scrollView setContentOffset:CGPointZero animated:NO]; //iOS7之前的版本,更新约束之前,滚动转到(0,0)位置,禁用动画效果
        _offsetX = targetView.left; //记录下真正想要滚动到的位置
    } else {
        [_scrollView setContentOffset:CGPointMake(targetView.left, 0) animated:YES];
    }
    [self.view setNeedsUpdateConstraints];
}
-(void) viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    if( floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_7_0 &&
        _offsetX > 0 && fabs((_scrollView.contentOffset.x - _offsetX )) > 0.1 ) {
        [self.scrollView setContentOffset:CGPointMake(_offsetX, 0) animated:NO]; //约束更新完毕,如果之前记录了要跳转到的位置,此时开始滚动
        _offsetX = -1;
    }
}

最后,为了适当的改善胃部的不适,用RAC美化一下代码形式:

- (void)switchToIndex:(NSInteger)index
{

    if (index == [_views indexOfObject:_activeView]) {
        return;
    }
    UIView *targetView = _views[index];
    self.activeView = targetView;

    if( floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_7_0 ) {
        [_scrollView setContentOffset:CGPointZero animated:NO];
        CGFloat offsetX = targetView.left;
        [[[self rac_signalForSelector:@selector(viewDidLayoutSubviews)] take:1 ] subscribeNext:^(id x) {
            if( fabs((_scrollView.contentOffset.x - offsetX )) > 0.1 ) {
                [self.scrollView setContentOffset:CGPointMake(offsetX, 0) animated:NO];
            }
        }];
    } else {
        [_scrollView setContentOffset:CGPointMake(targetView.left, 0) animated:YES];
    }

    [self.view setNeedsUpdateConstraints];
}

猜你喜欢

转载自blog.csdn.net/ivolcano/article/details/89445750
今日推荐