一. UIScrollView内容的自动偏移
当A控制器是UINavigationController的子控制器时,并且A控制器的view的第一个子控件是UIScrollView(包括继承自UIScrollView的子类,如:UITableView,UICollectionView,或者是自定义继承自UIScrollView的类),内容会有自动偏移的现象,有如下两种情况。
- 竖屏的情况下,ScrollView会偏移64个像素;横屏的情况下,ScrollView会偏移32个像素。
- 当隐藏导航控制器的导航栏时,竖屏的情况下,ScrollView会偏移20个像素;横屏的情况下,ScrollView不偏移。
二. 关闭系统的自动偏移
当控制器的view有多个ScrollView时,Apple建议关闭系统的自动调整功能。
- 方法一
选中要关闭的控制器 —> 属性栏 —> View Controller —> Layout —> Adjust Scroll View Insets - 方法二
在要关闭的控制器中代码关闭:self.automaticallyAdjustsScrollViewInsets = NO;
三. 在storyboard或XIB中布局UIScrollView
- 当ScrollView没有子控件的情况下,和其他控件的布局一样,布局位置和宽高就完成约束,添加子控件可以在代码中进行,并且用代码设置contentSize就可以滚动。
- 当ScrollView有子控件的情况下,UIScrollView本身布局位置和宽高和其他控件的布局还是一样,但其子控件的布局和一般情况下的布局就不太一样了,因为UIScrollView不仅要布局位置和宽高,而且还要通过子控件的位置和尺寸来自动计算出contentSize,换句话说子控件的位置尺寸必须明确。
要点:
(1). 子控件的上下左右约束必须设置
注意:这里设置了上下约束,不等于高就确定了。例如:在只有一个子控件情况下,这个子控件的上下左右约束都为50,只表示这个子控件的上下左右边框距离ScrollView的边框都是50。(2). 子控件的宽高约束也必须设置
注意: 子控件的宽高只能等于已经确定了宽高的控件的宽高的倍数,或者是用具体的数值设置宽高的约束。例如:
ScrollView有一个子控件,这个子控件的高约束为800,宽的约束等于父控件ScrollView的宽;也可以是高约束为控制器view的2倍,宽为父控件ScrollView宽的3倍等。
- 举一个完整约束的例子:控制器的view有一个填充的UIScrollView,UIScrollView有两个子控件UIView,两个子控件上下排列,这里把上面的比作A控件,下面的比作B控件,添加完整的约束使UIScrollView可以滚动。
(1). 设置UIScrollView的约束
设置UIScrollView的上下左右与控制器的view的上下左右距离约束都为0。这样UIScrollView的位置和宽高已经确定。(2). 设置A控件的约束
A控件的顶部左边右边距离UIScrollView顶部左边右边为50,底部距离B控件为100,再设置宽的约束为400,高的约束为500。这里的A控件宽高已经确定。(3). 设置B控件的约束
B控件的左边右边底部距离UIScrollView左边右边底部为50,宽等于A控件的宽,高等于控制器的view的高。这里就完成了所有的约束。(4). UIScrollView的contentSize可以通过计算得知(设控制器的高为667)
横向:50 + 400 + 50 = 500
纵向:50 + 500 + 100 + 667 + 50 = 1367
三. 自动布局后的contentSize的获取和修改
在storyboard或XIB中布局UIScrollView,UIScrollView会在viewDidAppear之前根据其子控件的约束计算UIScrollView的contentSize。当在控制器的view的生命周期viewDidAppear之前获取contentSize时,获取的值是0;可以在viewDidAppear方法中获取contentSize的值,也可以在它的父控件的layoutSubviews方法中获取。同样的,可以在viewDidAppear方法或layoutSubviews方法中修改contentSize的值。
四. UIScrollView布局的使用
例子:UIScrollView的子控件悬浮于屏幕顶部
(一). 界面自动布局
- 添加一个UIScrollView,设置上下左右约束与控制器view的距离为0。
- 给UIScrollView添加第一个子控件UIView,这里称为控件A,设置顶部左边右边距离UIScrollView边框为0;宽等于UIScrollView的宽,高约束设置为150。
- 给UIScrollView添加第二个子控件UIView,这里称为控件B,设置底部左边右边距离边框为0;顶部距离控件A的底部为50,宽等于UIScrollView的宽,高约束设置为700。
- 给UIScrollView添加第三个子控件UIView,这里称为控件C,设置左边右边距离UIScrollView边框为0;宽等于UIScrollView的宽,高约束设置为50。
- 上面的布局已经完成,这里而外的添加控件,用于做参照物,往控件C里添加一个UILabel,设置上下左右约束距离边框为0,文字为“SSIrreplaceable”,文字居中,字体尺寸为36,颜色为黄色;再往控件B中加两个UISwitch控件,第一个UISwitch的左边和顶部距离父控件的左边和顶部都为0,宽高约束分别为49,31;第二个UISwitch的左边距离父控件的左边为0,顶部距离第一个UISwitch为50,宽高约束分别为49,31。
提示:谁悬浮谁最后添加,不然即使悬浮了,也会被最后的添加的控件覆盖。
效果图:
(二). 拖线设置代理
(三). 拖线连接属性
把 控件A 和 控件C的顶部等于控件A的底部的约束分别脱线到控制器的属性上。
下图为“控件C的顶部=控件A的底部“约束图
(四). 代码实现悬浮
#import "ViewController.h"
@interface ViewController ()
/** 控件A */
@property (weak, nonatomic) IBOutlet UIView *viewA;
/** 控件C的顶部==控件A的底部的约束 */
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *viewCTopConstraint;
@end
@implementation ViewController
#pragma mark - <UIScrollViewDelegate>
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// 获取纵向偏移量
CGFloat offsetY = scrollView.contentOffset.y;
// 获取控件A的高
CGFloat viewAH = self.viewA.bounds.size.height;
// 偏移量大于或等于控件A的高就悬浮
if (offsetY >= viewAH) {
self.viewCTopConstraint.constant = offsetY - viewAH;
} else {
self.viewCTopConstraint.constant = 0.0;
}
}
@end
(五). 子控件悬浮效果图