为什么说scrollview的自动化布局是难点?

对scrollview做自动化布局,无非就是想对scrollview里面的subviews来做自动化布局。但是scrollview里面的subviews的自动化布局不是由scrollview的高宽来决定的,而是由scrollview的contentSize共同决定的,这样就出现一个问题了,就算scrollview的高宽是改变了,但是只要contentSize不变,那么对于scrollview里面的subviews的高宽其实是没有影响的。而实现自动化布局的NSLayoutConstraint也无法实现对scrollview的contentSize属性做自动化布局的。那么纯粹的想使用NSLayoutConstraint来对scrollview做自动化布局的方法是行不通的。

到这里咱再换一个方法,其实我们平常用scrollview更多的场景是用来做上下滚动或左右滚动,很少有上下滚动和左右滚动同时存在的情况。现在假设我们的自动化布局的scrollview就是上下滚动的,在水平方向,subviews的宽度永远跟scrollview的宽度一致,这样的场景是不是能实现?针对这样的场景我们马上就能想到,只要把subviews的宽度用NSLayoutConstraint实现跟scrollview的宽度绑定就可以了啊。

    UIScrollView *scroll=[[UIScrollView alloc] init];
scroll.backgroundColor=[UIColor blueColor];
scroll.isBindSizeToSuperView=YES;
[self.view addSubview:scroll];
[scroll setContentSize:CGSizeMake(, )]; UILabel *label = [[UILabel alloc] initWithSize:CGSizeMake(, )];
label.backgroundColor = [UIColor blackColor];
label.textColor=[UIColor whiteColor];
label.font=[UIFont systemFontOfSize:];
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:]];

我们发现这样做可以啊!别急,你试着再添加一个subview看看?你会发现用这样的方法虽然能让subviews实现宽度跟scrollview的宽度保持一致,但是在垂直方向上无法顺序布局。这是为什么呢?

问题在于translatesAutoresizingMaskIntoConstraints这个属性我们设置了no,为什么要设置no?只要我们使用自动布局,或者更通俗的说,只要我们使用了NSLayoutConstraint,那么必须要把这个属性设置为no。而一旦设置了no,那么这个view的位置和大小也只能是通过NSLayoutConstraint来实现了。说道这里,看过我前三篇博客的同学就能想到,不是有一个UIStackPanel正好可以实现这样的功能的吗?对的。我们可以直接拿来用,把uiStackpanel做未subview添加到scrollView中,然后将本来要添加到scrollvew中的subviews添加到stackpanel中。这样就能实现以上的场景了。但是这样做还是有一个问题,就是stackpanel的宽度我们是可以绑定到scrollview的宽度,但是高度呢?高度必须跟contentSize的height做绑定。而要实现这样的需求我们就得借助IOS的KVO技术来实现,获取scrollview的contentSize属性变化事件,然后再次绑定。这样就能完全的实现以上的需求了。

为了把以上的实现过程进行一个封装,我们在UIPanel里面添加了一个bindToScrollView的方法。

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:]]; [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeWidth multiplier:1.0f constant:]]; [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeTop multiplier:1.0f constant:]]; [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;
}
}

方法已经封装好了,那么后面就是如何使用了。代码如下:

    //初始化UIScrollView
UIScrollView *scroll=[[UIScrollView alloc] init];
scroll.backgroundColor=[UIColor blueColor];
scroll.isBindSizeToSuperView=YES;//把UIScrollView的高宽绑定到父视图
[self.view addSubview:scroll];
[scroll setContentSize:CGSizeMake(, )];//设置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(, )];
label.backgroundColor = [UIColor blackColor];
label.textColor=[UIColor whiteColor];
label.font=[UIFont systemFontOfSize:];
label.text = @"Label1";
label.textAlignment = NSTextAlignmentCenter;
[_panel addSubview:label]; label = [[UILabel alloc] initWithSize:CGSizeMake(, )];
label.backgroundColor = [UIColor blackColor];
label.textColor=[UIColor whiteColor];
label.font=[UIFont systemFontOfSize:];
label.text = @"Label2";
label.margin=UIEdgeInsetsMake(, , , );
label.textAlignment = NSTextAlignmentCenter;
[_panel addSubview:label];

至此,uiscrollview的自动化解决方案已经完成了。

下一遍介绍UIView在如何停靠在superView中,实现不管superview的高宽如何改变,都不会改变UIView的停靠位置。

连带本篇的源码都会在下一篇中给出。

IOS 自动布局-UIStackPanel和UIGridPanel(四)的更多相关文章

  1. IOS 自动布局-UIStackPanel和UIGridPanel(三)

    在这一篇了我将继续讲解UIGridPanel. 在iphone的app里面可以经常看到一些九宫格布局的应用,做过html开发的对这类布局应该是很熟悉的.在IOS中要实现这样的布局方法还是蛮多的,但是我 ...

  2. IOS 自动布局-UIStackPanel和UIGridPanel(二)

    在上一篇中我提到了如何使用stackpanel和gridpanel来实现自动布局.而在这一篇中我着重讲解下其中的原理. 在(UIPanel   UIStackPanel  UIGridPanel)中主 ...

  3. IOS 自动布局-UIStackPanel和UIGridPanel(一)

    我以前是做windows phone开发的,后来转做IOS的开发,因此很多windows phone上面的开发经验也被我带到了IOS中.其实有些经验本身跟平台无关,跟平台有关的无非就是实现方法而已.好 ...

  4. IOS 自动布局-UIStackPanel和UIGridPanel(五)

    试想这样的一个需求场合,一个button靠右显示,并且距离superView的顶部和右边间距分别为10和5.如下图所示: 要实现这样的需求,如果不用自动布局技术,那么我们能想到的就是老老实实的使用绝对 ...

  5. iOS自动布局——Masonry详解

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由鹅厂新鲜事儿发表于云+社区专栏 作者:oceanlong | 腾讯 移动客户端开发工程师 前言 UI布局是整个前端体系里不可或缺的一环 ...

  6. iOS 导出 ipa 包时 四个选项的意义

    iOS 导出 ipa 包时 四个选项的意义 如图  在 iOS 到处 ipa包的时候 会有四个选项 1.Save for iOS App Store Deployment 保存到本地 准备上传App ...

  7. iOS开发Swift篇—(四)运算符

    iOS开发Swift篇—(四)运算符 一.运算符 1.Swift所支持的部分运算符有以下一些 赋值运算符:= 复合赋值运算符:+=.-= 算术运算符:+.-.*./ 求余运算符:% 自增.自减运算符: ...

  8. iOS 自动布局详细介绍

    1. 自动布局的理解 iOS自动布局很有用,可以在不同size的屏幕上运行,原先看的头痛,还是习惯用最蠢的[UIScreen mainScreen].bounds.size.width等来布局,后来实 ...

  9. ios页面间传递参数四种方式

    ios页面间传递参数四种方式 1.使用SharedApplication,定义一个变量来传递. 2.使用文件,或者NSUserdefault来传递 3.通过一个单例的class来传递 4.通过Dele ...

随机推荐

  1. 一个页面有相同ID元素的情况分析

    经常会遇到一个页面中有相同定义相同id的情况,从道理上来说,id应该是这个页面中某个元素的唯一标识,所以不应该出现有相同id的情况,否则会产生意想不到的结果.而且各个浏览器的表现也是不一样的.我只做了 ...

  2. onpageshow 监听页面是否是缓存页面

    需求:点击A页面跳转至B页面,在B页面点击手机物理回退键或者history.back回退时,需要在A页面判断当前页面是否是回退回来的页面,而不是新加载的.这里用到一个 onpageshow 事件. 定 ...

  3. 浅谈Hibernate中的三种数据状态

    Hibernate中的三种数据状态:临时.持久.游离 1.临时态(瞬时态) 不存在于session中,也不存在于数据库中的数据,被称为临时态. 数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器 ...

  4. asp也玩三层架构(有源代码)

    实体类 <% Class UserInfo Private mintId Public Property Let UserId(intUserId) mintId = intUserId End ...

  5. 我也质疑下petshop

           很多人都研究过petshop,我开始认识分层架构也是从研究这个petshop开始的.但是我发现很多人一谈三层架构就是 petshop那一套东西.实体类,DAL,BLL那一套东西.首先我不 ...

  6. calendar.getTimeInMillis() 和 System.currentTimeMillis() 的区别

    @Test public void test01(){ Calendar calendar=Calendar.getInstance(); // calendar.set(2019,06,04,16, ...

  7. azure 创建redhat镜像帮助

    为 Azure 准备基于 Red Hat 的虚拟机 从 Hyper-V 管理器准备基于 Red Hat 的虚拟机 先决条件 本部分假定你已经从 Red Hat 网站获取 ISO 文件并将 RHEL 映 ...

  8. fiddler+willow问题总结

    本文纯属用来记录自己学习过程中遇到的坑,如有朋友也遇到,可移步到这里查看是否为该问题导致. fiddler 安装不用说了,到官网直接去下载,自行下载最新版本 willow下载地址:http://qzo ...

  9. web前端性能优化 (share)

    本文转自:http://www.cnblogs.com/50614090/archive/2011/08/19/2145620.html 一. WEB前台的优化规则 一.尽量减少 HTTP 请求 有几 ...

  10. sql service 查询分析数据库

    --学会通配符 https://blog.csdn.net/blackfwhite/article/details/80382849 --学会变量中的变量 https://www.cnblogs.co ...