UIScrollView上子View的自动布局处理

UIScrollView使用详解

contentSize:子控件的大小, 限定滚动的范围

showsHorizontalScrollIndicator

showsVerticalScrollIndicator

bounces:弹簧效果,默认是YES,一般是不关闭

alwaysBounceHorizontal

alwaysBounceVertical

contentInset:拖动后,会停留在设置的内边距的位置

contentOffset:偏移量,滚动到了某个位置

setContentOffset: animated:

userInteractionEnabled

scrollEnabled

了解更多

iOS开发UIScrollView使用详解

UIScrollView的详细使用介绍和实现原理分析

实现滚动翻页效果,用自动布局,以便页面能够上下拖动

在multiScrollView上添加多个子View,用自动布局,实现左右翻页

在autolayout下,会在viewDidAppear之前根据subview的constraint重新计算scrollview的contentsize。

这就是为什么,在viewdidload里面手动设置了contentsize没用。因为在后面,会再重新计算一次,前面手动设置的值会被覆盖掉。

解决办法就是:

1.去除autolayout选项,自己手动设置contentsize;

2.如果要使用autolayout,要么自己设置完subview的constraint,然后让系统自动根据constraint计算出contentsize。 要么就在viewDidAppear里面自己手动设置contentsize。

scrollview布局的注意事项:

首先,我们需要添加一个UIView类型的控件成为UIScrollView的唯一子控件,并设置其上下左右间距都为0。这时候我们发现设置完四个约束后仍然会报错,这里我们就来解释一下UIScrollView的特殊性:其实UIScrollView要想实现滚,必须设置其滚动区域contentSize,而这里UIScrollView的滚动区域是由其子控件的内容决定的,所以这里我们先添加一个UIView成为UIScrollView的唯一子控件,并设置其上下左右约束均为0。

然后设置UIView的宽度为300(这个高度就是UIScrollView的内容宽度:contentSize.width),再设置垂直居中,然后运行程序就会看到UIScrollView可以左右滚动了。

如果要上下滚动就将UIView的高度约束设置成300(这个高度就是UIScrollView的内容高度: contentSize.height),水平居中就可以了。

如果要想上下左右都可以滚动,那么就将宽高约束都设置为300,不用设置水平和垂直居中。

接下来所有的控件都添加到UIView中即可利用自动布局来设置约束。

问题的关键在于如何给scrollView内部的子控件添加约束。

scrollView内部子控件约束的添加需要遵循两个原则:
1、scrollView内部子控件的尺寸不能以scrollView的尺寸为参照;
2、scrollView内部的子控件的约束必须完整;

首先,子控件的尺寸不能以scrollView的尺寸为参照,那么我们有两种选择:
提供一个具体值的约束(比如200);
子控件的尺寸可以参照scrollView以外其它的控件的尺寸(如控制器的view的尺寸);
其次,约束“完整”的意思是说:子控件在水平及竖直方向上的约束要把scrollView“撑满”;

也就是说,在水平方向上,我们需要设置:

  • 子控件左侧与父控件的距离;
  • 子控件自身的宽度;
  • 子控件右侧距父控件的距离;

竖直方向上也一样,要设置:

  • 子控件顶部距父控件的距离;
  • 子控件的高度;
  • 子控件底部距父控件的距离;

为什么scrollView如此隔路(特殊,与众不同)呢?

这是因为,scrollView需要根据添加在其内部的子控件的宽高及与四周的距离计算出它的contentSize。

举个栗子:

一个添加在scrollView内部的imageView的宽高为{80, 50}, imageView距离上左下右的距离分别为:100, 200, 300, 400,那么不需要用代码赋值contentSize,我们就可以打印出scrollView的contentSize为{680, 450}。

IB添加约束的原理

如果理解起来还是有困难,我们可以把scrollView的contentSize的范围想象成一块UIView(上图中的蓝色区域),暂且叫它Container(实际是没有这个东西的)。当我们在storyboard或xib中设置子控件与scrollView之间约束时,实际上设置的是子控件与container之间的约束。

也就是说,子控件的约束决定了container的尺寸(contentSize)。

这就说明了为什么我们要在水平和竖直方向用约束“撑满”。如果不撑满,container不知道它自己应该多大。

也正是因为container的尺寸由子控件的约束决定,所以子控件的尺寸不能再反过来参照container的尺寸。不然你等于我,我等于你,那到底是多少呢?如果你是Xcode你也会抓狂。(再次强调那个container不是真的,只是为了方便理解)
理论部分解释完毕,回到一开始的案例上。
imageView有了上下左右四个约束,还缺少宽高,我们再添加个固定宽高的约束(可以大一点,太小了看不到滚动效果),问题就可以解决了。

强调一下, 用代码给scrollView添加约束(包括使用Masonry的情况)是一样的。

    [self.pageStackView removeAllSubviews];
    [self.multiTableViewArr removeAllObjects];
    [self.pageBtnArr removeAllObjects];
    [self.multiTableBackViewArr removeAllObjects];
    self.isMulti = YES;
    WS(weakSelf);
    UIView *lastBackView;
    self.multiScrollView.contentSize = CGSizeMake(kFullScreenWidth * multiAnswerArr.count, 0);
    for (NSInteger i = 0; i < multiAnswerArr.count; i ++) {
        SSTCorrectHomeworkMultipageAnswerModel *multiModel = multiAnswerArr[i];
        UITableView *multiTableView = [self getMultiTableView];
        multiTableView.tag = MultiTableViewIndex + i;
        [self.multiTableViewArr addObject:multiTableView];
        // 用来显示页码的
        UIButton *pageBtn = [self getPageBtnWithModel:multiModel];
        pageBtn.tag = 100 + i;
        pageBtn.layer.borderColor = UIColorFromHex(COLOR_778ADC).CGColor;
        [self.pageBtnArr addObject:pageBtn];
        
        UIView *backView = [[UIView alloc] init];
        [self.multiScrollView addSubview:backView];
        [self.multiTableBackViewArr addObject:backView];
        
        if (lastBackView) { // 借助这个lastBackView来实现左右自动布局;
            [backView mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.mas_equalTo(lastBackView.mas_right);
                make.top.mas_equalTo(weakSelf.multiScrollView.mas_top);
                make.bottom.mas_equalTo(weakSelf.bgView.mas_bottom).offset(-40);
                make.width.mas_equalTo(kFullScreenWidth);
            }];
        }else {
            [backView mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.mas_equalTo(weakSelf.multiScrollView.mas_left);
                make.top.mas_equalTo(weakSelf.multiScrollView.mas_top);
                make.bottom.mas_equalTo(weakSelf.bgView.mas_bottom).offset(-40);
                make.width.mas_equalTo(kFullScreenWidth);
            }];
        }
        
        [backView addSubview:multiTableView];
        [multiTableView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.mas_equalTo(UIEdgeInsetsMake(0, 0, 0, 0));
        }];
        
        [self.pageStackView addArrangedSubview:pageBtn];
        [pageBtn mas_makeConstraints:^(MASConstraintMaker *make) {
            make.size.mas_equalTo(CGSizeMake(35, 12));
        }];

        if ([multiModel.pageName isEqualToString:@"当前页"]) {
            // 选中当前页,刷新数据;
            [self pageBtnAction:pageBtn];
        }
        lastBackView = backView;
    }

-(void)pageBtnAction:(UIButton *)btn {
    if (btn == nil || btn.tag < 100 || btn.tag > 102) {
        return;
    }
    NSLog(@" --- 页码点击 --- tag: %ld",btn.tag);
    self.oldPageBtn.selected = NO;
    self.oldPageBtn.layer.borderColor = UIColorFromHex(COLOR_778ADC).CGColor;
    btn.selected = YES;
    self.oldPageBtn = btn;
    self.oldPageBtn.layer.borderColor = UIColorFromHex(COLOR_FF6465).CGColor;
    NSInteger index = btn.tag - 100;
    self.multiScrollView.contentOffset = CGPointMake(kFullScreenWidth * index, 0);
    UITableView *currentTableView = [self.multiTableViewArr objectAtIndexCheck:index];
    [currentTableView reloadData]; // 刷新数据;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if ([scrollView isKindOfClass:[UITableView class]]) {
        return;
    }
    NSInteger tag = scrollView.contentOffset.x/kFullScreenWidth;
    UIButton *pageBtn = [self.pageBtnArr objectAtIndexCheck:tag];
    [self pageBtnAction:pageBtn];
}

实现页面的拖拽

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(dragTableView:)];
    [self.btnShow addGestureRecognizer:pan];
-(void)dragTableView:(UIPanGestureRecognizer *)panGestureRecognizer{ 
    UIView *view = panGestureRecognizer.view.superview; // 当前view = SSTHomeworkCorrectAnswerListView
    CGPoint translation = [panGestureRecognizer translationInView:view.superview]; //获取到的是手指移动后,在相对坐标中的偏移量 (x = 0, y = -5.333343505859375)
    [panGestureRecognizer setTranslation:CGPointZero inView:view.superview];
    CGPoint center = CGPointMake(view.center.x + translation.x, view.center.y + translation.y);
    view.centerY = center.y;
    //1、拖到最高 - 答案页面高度最大
    if (view.frame.origin.y <= kFullScreenHeight*0.3) {
        view.y = kFullScreenHeight*0.3;
        view.height = kFullScreenHeight-BottomBar_H-BottomHeight-kFullScreenHeight*0.3;
        self.bgView.hidden = NO;
        self.isShow = YES;
        self.btnShow.selected = YES;
    //2、拖动变化 - 答案高度最高和最低之间
    }else if (view.frame.origin.y > kFullScreenHeight*0.3 && view.frame.origin.y < (kFullScreenHeight-BottomBar_H-AnswerListView_H-BottomHeight)) {
        CGFloat view_Height = kFullScreenHeight-BottomHeight-BottomBar_H-view.y;
        view.height = view_Height;
        self.bgView.hidden = NO;
        self.isShow = YES;
        self.btnShow.selected = YES;
    //3、拖动最低高度范围 - 保持最低高度
    }else if (view.frame.origin.y >= (kFullScreenHeight-BottomBar_H-AnswerListView_H-BottomHeight) && view.frame.origin.y < (kFullScreenHeight-BottomBar_H-AnswerShowBtnIcon_H-BottomHeight)){
        view.height = AnswerListView_H; //有一个最小的高度;
        self.bgView.hidden = NO;
        self.isShow = YES;
        self.btnShow.selected = YES;
    //4、拖到最低 - 关闭答案页面
    }else{
        view.y = kFullScreenHeight-BottomBar_H-AnswerShowBtnIcon_H-BottomHeight;
        view.height = AnswerShowBtnIcon_H;
        self.bgView.hidden = YES;
        self.btnShow.selected = NO;//答案批注收起
        self.isShow = NO;
    }
    if ([self.delegate respondsToSelector:@selector(answerListView:isShow:)]) {
        [self.delegate answerListView:self isShow:self.btnShow.selected];
    }
}
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveAction:)];
    self.pan = pan;
    pan.enabled = NO;
    [self addGestureRecognizer:pan];
-(void)moveAction:(UIPanGestureRecognizer *)panGestureRecognizer {
    UIView *view = panGestureRecognizer.view; // 当前view
    CGPoint translation = [panGestureRecognizer translationInView:view.superview]; //获取到的是手指移动后,在相对坐标中的偏移量
    CGPoint center = CGPointMake(view.center.x + translation.x, view.center.y + translation.y);
    view.xl_centerY = center.y;
    [panGestureRecognizer setTranslation:CGPointZero inView:view.superview];
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值