浅汇-iOS UI布局

,wan m

  iOS中UI的布局是很重要的,而在前期开发中就要选定好布局的方法,因为这对整个工程乃至于后期的版本维护都有很重要的影响。本文从比较老的Frame到新生AutoLayout都进行了浅显的总结,希望对大家的UI布局学习有所帮助


Frame


父试图是使用的AutoLayout  ,如果子试图的Frame  = supeView.Frame  就会无法获得足够的宽度。如果父试图使用的是 Frame  , 子试图使用 AutoLayout  不会有问题,

使用Frame 来布局UI是开始的做法,现在也有很多人仍然钟情与这种方法,这种方法很直观,使用起来也很简单,但是他的简单决定了他在屏幕适配和内容自适应上的局限性(横竖屏时不设置的话无法使用,因为横屏的时候,之前设置的Frame属性 还是竖屏的Frame)`,当然可以使用Fram的方法达到屏幕适配和自动布局,但是中间的过程是复杂而且工作量巨大的,写起来也是痛苦的。通常使用这种方法布局是通过比例放缩来达到的,比如使用如下的宏来替换掉系统的CGRectMake(x , y , width , height) 布局来达到屏幕适配的效果。

#define NEWX                        [UIScreen mainScreen].bounds.size.width/384   

#define NEWY                         [UIScreen mainScreen].bounds.size.height/512

#define NEWY                        NEWX

#define RECT(a,b,c,d)            CGRectMake(a*NEWX, b, c*NEWX, d) 

这里我们以宽度缩放比为整体缩放比,就可以完美适配更种机型,不管苹果以后出什么机型,这样设置后就可以保证在各种机型的屏幕上不出现变形的情况(尤其是iphoneX这样修长的机型上)。

但是因为系统的layoutSubviews 方法是默认不执行任何布局的,需要使用者在页面内容确定后再次对空间的Frame进行重置,牵一发而动全身的重置是痛苦而繁琐的。

AutoLayout(动画中使用是个弱点)


     使用SDAutoLayout  不光是为了横竖屏的适配(通过判断屏幕方向,重置 Frame  也可以做到),是为了TablevIew中cell  因为内容的不同而动态完美布局的效果。使用了这么久,

      对于父试图是  Button / UITextFeild等非UIView的直接子类,布局其子视图时,这里面的约束是不生效的。而且同层级的试图  无法达到重合布局,两个试图重合的话只有是  父/子视图的关系。

Autolayout简单来说就是一套 `适配iPhone机型`并且`兼容横竖屏`的UI布局系统,Auto Layout 是一个系统,可以让你通过创建元素之间关系的数学描述来布局应用程序的用户界面,是一种基于约束的,描述性的布局系统。这中页面布局方式的思维模式跟Frame完全不同,使用时应跳出Frame的坐标布局思维模式,站在关系依赖布局的思维方式上才可以达到娴熟正确的使用。

我们可以在XIB、StoryBoard中通过拉线的形式给控件视图添加布局约束,通过苹果强大的可视化界 IB(Interface Builder)我们能够轻松的使用AutoLayout完成界面视图的布局。另外一种方式就是通过纯代码的形式使用AutoLayout,即NSLayoutConstraint。

IB

原生的AutoLayout

原生的iOS布局,要添加`一个约束`是这样的:

 NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view //第一个view

attribute:NSLayoutAttribute //约束属性, 比如上下左右宽高等间距

 relatedBy:NSLayoutRelationEqual //相等,或者大于等于,小于等于

toItem:secondView //第二个view,也就是第一个view是要参照第二个view的

attribute:NSLayoutAttribute //参照第二个view的属性

 multiplier:multiplier  //比例0--1

 constant:0]; //约束值

就这样随便加一个约束就如此的繁琐,更何况一个view最起码有上边距,左边距和宽高,也就是所谓的x、y、width、height四个基本属性。就相当于以上那复杂的代码就要最少写四次。

所以对IOS的AutoLayout进行封装就显得很有必要了。本次推荐使用的是一个第三方的AutoLayout库这个库实现了对普通空间的自动布局,也实现了对ScrollView及UITableView的自动布局,操作简单,功能强大(SDAutoLayout)


SDAutoLayout

主要的功能是:

1、实现Label高度固定,宽度自适应(超出后不显示),宽度固定,高度自适应。

2、实现了UIView内子视图的自动布局;

3、实现了UIScrollView内容高度根据内部子视图的内容高度动态设置;

4、实现了一个UITableView有多个不同Cell的时候,所有cell高度自适应;


【1】实现Label高度固定,宽度自适应(超出后不显示),宽度固定,高度自适应。  

/** 设置单行文本label宽度自适应,超出了这个最大宽度则不显示,否则会根据文字多少自动调整宽度 */ 

- (void)setSingleLineAutoResizeWithMaxWidth:(CGFloat)maxWidth;

 /** 自适应高度,传入高宽比值,label可以传0实现文字高度自适应 */

  @property (nonatomic, copy, readonly) AutoHeight autoHeightRatio;  

【2】   当父试图的高度没有定义的时候,需要使用一下方法来自动布局,并且这个时候不可以再以父试图的底为标准来设置其内部子视图,这是一种【从里到外】的布局思路,cell的自适应高度也是这种思路;平时我们的思路都是一种【从外到里】的思路,先确定外面的再使其自动布局里面的。

有一个需要注意的地方,cell中所有的子视图都需要加载在 self.contentView上才行,不可加载在self.contentView的子视图上,否则按下面的这个方法设置也会出问题。

[self setupAutoHeightWithBottomView:_timeLabel bottomMargin:margin + 5];//第一个参数是指 底部试图,第二个参数是指底部的留出来的空白区域的高度。

【3】实现了UIScrollView可滚动高度根据内部子视图的内容高度动态设置

/** 设置scrollview内容自适应,第一个参数为作为底部的子视图,第二个参数为到sc底部的间距。 */

- (void)setupAutoContentSizeWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin;

【4】这面这一个协议中设置即可,关键是cell中的设置,cell中的设置需要满足两点:第一个是子视图的相对位置关系的设置,第二个是设置[self setupAutoHeightWithBottomView:_view3 bottomMargin:10]即可,这个方法在 子视图初始化的最后设置可以在 Model赋值后设置也可以

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath    {

       /* model 为模型实例, keyPath 为 model 的属性名,通过 kvc 统一赋值接口 */

  return [self cellHeightForIndexPath:indexPath cellContentViewWidth:[UIScreen mainScreen].bounds.size.width tableView:tableView];

   }

使用SDAutoLayout来布局UIImageView的时候,遇到圆角的问题,headHeadImageV.layer.cornerRadius=40;是不会起作用的。

UIImageView *imageV = [UIImageView new]; [self.view addSubview:imageV]; imageV.image = [UIImage imageNamed:@"loginLogo"]; imageV.sd_layout.leftSpaceToView(self.view,20).topSpaceToView(self.view,80).widthIs(80).heightIs(80);

 [imageV setSd_cornerRadius:@40];

方法名中带有“SpaceToView”的方法表示到某个参照view的间距,需要传递2个参数:(UIView)参照view 和 (CGFloat)间距数值  `当这个参照View是他的父试图时,leftSpaceToView就表示当前这个试图的左边到父试图左边的距离,当这个试图是同一层级的参照的话,就表示当前试图的左边到参照试图右边的距离,依次类推,传入的这个CGFloat值需要是一起的值才行,如果这个值是组合值的话就要加()如:leftSpaceToView(self.sc,(passWordBgView.width+5))。各种设置彼此之间是彼此独立的,后面的设置效果会覆盖前面设置的效果。考虑到完美适配的问题  引入一下两个宏:

#define Scale_X(a)(a*NEWX)

#define Scale_Y(a)(a*NEWY)

eg:.heightIs((Scale_Y(45)));

.topSpaceToView(leftView,Scale_Y((yValue+60*i+3))),

注意:,Scale_Y((yValue+60*i+3))当内层参数是组合参数时,需要双层(),否则无法把组合参数作为一个整体,从而在屏幕尺寸适配上出问题。而且要先加载到父试图上才能使用 layout 语句,否则约束不回起作用(针对 SDAutoLayout)。

动画中试图的自动布局刷新(SDAutoLayout在动画中并不友好)

[UIView animateWithDuration:0.8 animations:^{

self.view0.sd_layout .widthRatioToView(self.view, _widthRatio);

[self.view layoutSubviews];

[self.view0 layoutSubviews];

}];

//刷新试图达到自动布局其子试图的功能`

在相对布局中某个试图自己修改了自己的高度,如何更新SC的 ContentSize?

    [selectSevenV updateLayout];

    [self.sc setContentSize:CGSizeMake(WIDTH, selectSevenV.frame.origin.y+selectSevenV.frame.size.height)];

这里中 selectSevenV 是众多相对布局的试图中的底部的那一个,中间的某个试图通过 self.sd_layout.heightIs(Scale_Y(467)); 修改了自身的高度,此时想更改父试图(sc 的 ContentSize)调用上面两个语句即可。

一个问题

在一个自定义控件中使用 bounds或者 Frame 设置宽高后,使用 sd_layout.heightIs 改变 高度无法奏效,只能继续使用setBounds: 来修改了。而且通过 subV.height  也获取不到该自定义控件的高度,只能通过 subV.frame.size.height 的方式获取到。

         // baseItem[6].sd_layout.heightIs(0.1);

          [baseItem[6] setBounds:CGRectMake(0, 0, 0, 0.1)];               

        //subV.height     

        subV.frame.size.height;

但是值得注意的是,有时候可以使用 sd_layout.heightIs 改变视图的高,但是有时候又不可以,但是通过 setBounds 是一定可以改变视图的高度的。

关于位置,有时候使用 setFranme 可以修改位置,但是有时候又不行,此时 使用 sd_layout.topSpaceToView 却可以,有时候又不可以。

所以,最后总结一句话, setFranme  、setBounds 和  sd_layout 其中之一不行的时候就试试另外一种方式,两种方式都试试。


小结

 iOS关于UI布局的知识还有很多,至此我列举了一些需要注意的地方,使用的时候是先初始化`new`比较方便,先加载到父视图上后设置相关的属性,然后再进行布局方面的设置,若后加到父试图上,会设置无效或者是默认的布置设置。

   所以当我们要创造一个待加载的试图(只是创建,而不加载)就无法使用SDLayout  来布局了,就只能使用Frame来布局了。否则里面的子试图会布局的很乱。这大概就是SDLayout 的使用禁区了,SDLayout需要先加载到父视图上才有效果。

使用 NEWX、NEWY 做宽高比例放缩 ,加上 SDLayout的相对布局就实现了所谓的完美适配。

SDAutoLayout 可以完美解决页面高度自适应的问题,从此子试图文本根据内容自动换行,页面自动布局不再是恶心的事。不光适用于cell的高度自适应,而是所有的视图,包括 SC。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • Frame 使用Frame 来布局UI是开始的做法,现在也有很多人仍然钟情与这种方法,这种方法很直观,使用起来也很...
    poo_om阅读 327评论 0 0
  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 8,985评论 3 38
  • 代码创建UIWindow对象 Xcode7之后使用代码创建UIWindow对象: //创建UIWindow对象 s...
    云之君兮鹏阅读 1,306评论 0 2
  • 一时冲动,便来了一次说走就走的旅行。带着悲伤的心情,轻而易举地就踏上了南下的火车,路过了山东济南、江苏南京,最后到...
    Fiocca阅读 419评论 0 1
  • 朋友来到寝室,突然哭了。最近太多倒霉的事发生在她身上,觉得委屈和难过。其实就是这样的,人倒霉起来,喝口水都塞牙缝,...
    WZDaisy阅读 265评论 0 0