为何说scrollview的自动化布局是难点?布局
对scrollview作自动化布局,无非就是想对scrollview里面的subviews来作自动化布局。可是scrollview里面的subviews的自动化布局不是由scrollview的高宽来决定的,而是由scrollview的contentSize共同决定的,这样就出现一个问题了,就算scrollview的高宽是改变了,可是只要contentSize不变,那么对于scrollview里面的subviews的高宽实际上是没有影响的。而实现自动化布局的NSLayoutConstraint也没法实现对scrollview的contentSize属性作自动化布局的。那么纯粹的想使用NSLayoutConstraint来对scrollview作自动化布局的方法是行不通的。ui
到这里咱再换一个方法,其实咱们日常用scrollview更多的场景是用来作上下滚动或左右滚动,不多有上下滚动和左右滚动同时存在的状况。如今假设咱们的自动化布局的scrollview就是上下滚动的,在水平方向,subviews的宽度永远跟scrollview的宽度一致,这样的场景是否是能实现?针对这样的场景咱们立刻就能想到,只要把subviews的宽度用NSLayoutConstraint实现跟scrollview的宽度绑定就能够了啊。spa
UIScrollView *scroll=[[UIScrollView alloc] init]; scroll.backgroundColor=[UIColor blueColor]; scroll.isBindSizeToSuperView=YES; [self.view addSubview:scroll]; [scroll setContentSize:CGSizeMake(100, 1000)]; UILabel *label = [[UILabel alloc] initWithSize:CGSizeMake(100, 50)]; label.backgroundColor = [UIColor blackColor]; label.textColor=[UIColor whiteColor]; label.font=[UIFont systemFontOfSize:12]; label.text = @"Label1"; label.translatesAutoresizingMaskIntoConstraints=NO; label.textAlignment = NSTextAlignmentCenter; [scroll addSubview:label]; [scroll addConstraint:[NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:scroll attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0]];
咱们发现这样作能够啊!别急,你试着再添加一个subview看看?你会发现用这样的方法虽然能让subviews实现宽度跟scrollview的宽度保持一致,可是在垂直方向上没法顺序布局。这是为何呢?code
问题在于translatesAutoresizingMaskIntoConstraints这个属性咱们设置了no,为何要设置no?只要咱们使用自动布局,或者更通俗的说,只要咱们使用了NSLayoutConstraint,那么必需要把这个属性设置为no。而一旦设置了no,那么这个view的位置和大小也只能是经过NSLayoutConstraint来实现了。说道这里,看过我前三篇博客的同窗就能想到,不是有一个UIStackPanel正好能够实现这样的功能的吗?对的。咱们能够直接拿来用,把uiStackpanel作未subview添加到scrollView中,而后将原本要添加到scrollvew中的subviews添加到stackpanel中。这样就能实现以上的场景了。可是这样作仍是有一个问题,就是stackpanel的宽度咱们是能够绑定到scrollview的宽度,可是高度呢?高度必须跟contentSize的height作绑定。而要实现这样的需求咱们就得借助IOS的KVO技术来实现,获取scrollview的contentSize属性变化事件,而后再次绑定。这样就能彻底的实现以上的需求了。server
为了把以上的实现过程进行一个封装,咱们在UIPanel里面添加了一个bindToScrollView的方法。blog
NSString *const KVCUIPanelString_ContentSize = @"scrollView_ContentSize"; -(void)bindToScrollView:(UIScrollView *)scrollView{ _scrollView=scrollView; self.translatesAutoresizingMaskIntoConstraints=NO; [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0]]; [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0]]; [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeTop multiplier:1.0f constant:0]]; [scrollView addObserver:self forKeyPath:KVCUIPanelString_ContentSize options:NSKeyValueObservingOptionNew context:nil]; [self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:[scrollView contentSize].height]];//第一次绑定的时候直接把scrollView的contentSize.height做为panel的高度 } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if([keyPath isEqualToString:KVCUIPanelString_ContentSize]){ [self removeConstraints:self.constraints]; [self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:[(UIScrollView *)object contentSize].height]]; } }
使用kvo的时候必定要记得释放。事件
-(void)removeFromSuperview{ [super removeFromSuperview]; if(_scrollView){ [_scrollView removeObserver:self forKeyPath:KVCUIPanelString_ContentSize context:nil]; _scrollView=nil; } }
方法已经封装好了,那么后面就是如何使用了。代码以下:ip
//初始化UIScrollView UIScrollView *scroll=[[UIScrollView alloc] init]; scroll.backgroundColor=[UIColor blueColor]; scroll.isBindSizeToSuperView=YES;//把UIScrollView的高宽绑定到父视图 [self.view addSubview:scroll]; [scroll setContentSize:CGSizeMake(100, 1000)];//设置UIScrollView的contentSize UIStackPanel *_panel=[[UIStackPanel alloc] init]; [scroll addSubview:_panel]; _panel.backgroundColor=[UIColor yellowColor]; [_panel bindToScrollView:scroll];//将UIStackPanel绑定到UIScrollView UILabel *label = [[UILabel alloc] initWithSize:CGSizeMake(100, 50)]; label.backgroundColor = [UIColor blackColor]; label.textColor=[UIColor whiteColor]; label.font=[UIFont systemFontOfSize:12]; label.text = @"Label1"; label.textAlignment = NSTextAlignmentCenter; [_panel addSubview:label]; label = [[UILabel alloc] initWithSize:CGSizeMake(100, 50)]; label.backgroundColor = [UIColor blackColor]; label.textColor=[UIColor whiteColor]; label.font=[UIFont systemFontOfSize:12]; label.text = @"Label2"; label.margin=UIEdgeInsetsMake(10, 0, 0, 0); label.textAlignment = NSTextAlignmentCenter; [_panel addSubview:label];
至此,uiscrollview的自动化解决方案已经完成了。rem
下一遍介绍UIView在如何停靠在superView中,实现无论superview的高宽如何改变,都不会改变UIView的停靠位置。源码
连带本篇的源码都会在下一篇中给出。