iOS 实战笔记4-(Geekband)

Frame

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

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

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

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

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

使用SDAutoLayout  不光是为了横竖屏的适配(通过判断屏幕方向,重置 Frame  也可以做到),是为了TablevIew中cell  因为内容的不同而动态完美布局的效果。使用了这么久,对于父试图是  Button / Label 时,这里面的约束是不生效的。而且同层级的试图  无法达到重合布局,两个试图重合的话只有是  父/子视图的关系。

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)

主要的API说明如下:

leftSpaceToView(self.view, 10)

方法名中带有“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)));

widthRatioToView(self.view, 1)

方法名中带有“RatioToView”的方法表示view的宽度或者高度等属性相对于参照view的对应属性值的比例,需要传递2个参数:(UIView)参照view 和 (CGFloat)倍数

topEqualToView(view)

方法名中带有“EqualToView”的方法表示view的某一属性等于参照view的对应的属性值,需要传递1个参数:(UIView)参照view

widthIs(100)

方法名中带有“Is”的方法表示view的某一属性值等于参数数值,需要传递1个参数:(CGFloat)数值

autoHeightRatio(0)

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

spaceToSuperView(UIEdgeInsetsMake(10, 10, 0, 10))

传入UIEdgeInsetsMake(top, left, bottom, right),可以快捷设置view到其父view上左下右的间距

对于cell的自适应,推荐两种方法

@interface UITableViewController (SDTableViewControllerAutoCellHeight)

(UITableViewController方法) 一行代码(一步设置)搞定tableview的cell高度自适应,同时适用于单cell和多cell    UITableViewController 我使用的比较少,他是继承 UIViewController的,自带tableView。更方便的一个VC

- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath cellContentViewWidth:(CGFloat)width;

@end

@interface NSObject (SDAnyObjectAutoCellHeight)

(NSObject方法)`一行代码(一步设置)搞定tableview的cell高度自适应,同时适用于单cell和多cell

- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath cellContentViewWidth:(CGFloat)width tableView:(UITableView *)tableView;

示例:UITableViewController中

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

return self.modelsArray.count;

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

Class currentClass = [DemoVC7Cell class];

DemoVC7Cell *cell = nil;

DemoVC7Model *model = self.modelsArray[indexPath.row];

if (model.imagePathsArray.count > 1) {

currentClass = [DemoVC7Cell2 class];

}

cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass(currentClass)];

cell.model = model;

这一步中赋值,cell中的表现为,重写私有方法 - (void)setModel:(DemoVC7Model *)model即可,不用在定义新的公共方法

return cell;

}

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

{

// >>>>>>>>>>>>>>>>>>>>> * cell自适应设置 * >>>>>>>>>>>>>>>>>>>>>>>>

// 只需一行代码即可实现单cell以及多cell的tableview高度自适应

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

}

cell 中的关键设置为:

- (void)setup{

_titleLabel = [UILabel new];

[self.contentView addSubview:_titleLabel];

_titleLabel.textColor = [UIColor darkGrayColor];

_titleLabel.font = [UIFont systemFontOfSize:15];

_titleLabel.numberOfLines = 0;

_imageView = [UIImageView new];

[self.contentView addSubview:_imageView];

_imageView.layer.borderColor = [UIColor grayColor].CGColor;

_imageView.layer.borderWidth = 1;

CGFloat margin = 15;

UIView *contentView = self.contentView;

_titleLabel.sd_layout

.leftSpaceToView(contentView, margin)

.topSpaceToView(contentView, margin)

.rightSpaceToView(contentView, 120)

.autoHeightRatio(0);

_imageView.sd_layout

.topEqualToView(_titleLabel)

.leftSpaceToView(_titleLabel, margin)

.rightSpaceToView(contentView, margin)

.heightIs(60);

[self setupAutoHeightWithBottomView:_imageView bottomMargin:margin];这一步是关键`

}

-  (void)setModel:(DemoVC7Model *)model

{

_model = model;

_titleLabel.text = model.title;

_imageView.image = [UIImage imageNamed:model.imagePathsArray.firstObject];

}

普通试图中的自动布局

.widthRatioToView(self.view, _widthRatio);

[self.view updateLayout];

[self.view0 updateLayout];

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

当父试图的高度没有定义的时候,需要使用一下方法来自动布局

[self setupAutoHeightWithBottomView:_timeLabel bottomMargin:margin + 5];

//第一个参数是指 底部试图,第二个参数是指底部的留出来的空白区域的高度。

关于定义视图边界UIEdgeInsets

typedef struct UIEdgeInsets {

CGFloat top, left, bottom, right;  // specify amount to inset (positive) for each of the edges. values can be negative to 'outset'

} UIEdgeInsets;

//eg

UIEdgeInsets,由函数 UIEdgeInsetsMake ( CGFloat top, CGFloat left, CGFloat bottom, CGFloat right );     构造出,分别表示其中的内容/标题/图片离各边的距离。

对于调整Label中的字体布局只能通过重写系统的 -(void) drawTextInRect: (CGRect) rect; 方法,来达到在画  Label 的文本时分别设置文本与  Label 四个边的间隙,即画在 Label 内的一个小矩形内,提供自己的 UIEdgeInsets 属性。

其实对于 UITextField 还有更好的实现办法, 可以直接设置 leftView 或 rightView, 然后文本输入区域就在 leftView 和 rightView 之间了

使用UIEdgeInsets 拉抻图片时遇到的问题

开始我设置参数{20,10,10,10},在图上的位置大致

iOS 实战笔记4-(Geekband)_第1张图片

很奇怪是不是,为什么出现了两个箭头(红色部分是设置的背景色用语区分)?再回头看下文档,才恍然大悟:

拉升的时候,是按前文说的两个方向来拉升

拉升的部分,是以tiled方式,简单的说就是以镜像的方式

按照1的规则,拉升的时候,水平和垂直方向都需要拉升。这样在水平拉升的时候,箭头其实处于拉升的部分。而拉升的时候,先按照原有的尺寸添加进去,不足的地方再把中间不拉升的部分填充进去,周而复始,直到填充完毕。因此,就有上面的现象了。

要达到需要的效果,必须按照如下的设置:

iOS 实战笔记4-(Geekband)_第2张图片

cell中 当把控件添加到 self.contentView 时,使用Layout布局,会无效,直接添加到  self (cell)上即可,若是修改cell的背景颜色,可以通过修改  self.contentView 的背景色来达到。。

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

headHeadImageV.sd_cornerRadiusFromHeightRatio=@(0.5); 即可在Layout的情况下圆角化。

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

文/流年逝金(作者)

原文链接:http://www.jianshu.com/p/22a1be03cec7

著作权归作者所有,转载请联系作者获得授权,并标注“作者”。

你可能感兴趣的:(iOS 实战笔记4-(Geekband))