iOS进阶指南试读之UI篇

子view跟随父view自动放大缩小的办法

实例代码如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    redView = [UIView new];
    redView.frame = CGRectMake(0, 200, 200, 200);
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];

    yellowView = [UIView new];
    yellowView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    yellowView.frame = CGRectInset(redView.bounds, 20, 20);
    yellowView.backgroundColor = [UIColor yellowColor];
    [redView addSubview:yellowView];
    // Do any additional setup after loading the view, typically from a nib.
}

黄色的View是红色View的子View,那么,如果我期望无论红色View变大还是变小,黄色View距离红色View的边距总是不变的,该怎么做呢?
一般来说有两种做法。

  1. 设置黄色View的autoresizingMask属性,设置为UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight,这样设置的结果是黄色View的宽高会随着父View宽高的改变而改变,但是不改变间距。
  2. 继承一个UIView,在UIView里的layoutSubviews里设置子View的宽高。如下。
- (void)layoutSubviews
{
    yellowView.frame = CGRectInset(self.bounds, 20, 20);
    [super layoutSubviews];
}

实际上这两种做法都是很麻烦而且不灵活的。例如autoresizingMask对应的UIViewAutoresizing模式只有6种,我们常常用到的居中对齐等完全没有。二是如果在layoutSubviews里设置布局,又会造成如果父View有动画,那么会出现奇怪的动画效果。

Autolayout的用法

1. Intrinsic Content Size

思考一下,当你拉约束的时候,为什么UILabel和UIButton只需要拉能够确定坐标的约束即可,而不需要确定宽高的约束?比如说。这个UILabel就是只确定了左边距和上边距,就可以了。但是如果我们放了一个UIView,只确定坐标而不指定大小,就会出错。Xcode会提示,这个View还需要指定宽和高。造成这种情况的原因就是,有些View可以通过自己的内容计算宽高,而有些View不可以。这也是我们要讲的内容,也即我们的标题,Intrinsic Content Size,翻译过来就是,固有内容大小。对应的系统方法就是- (CGSize)intrinsicContentSize要知道,Autolayout的本质无非就是系统通过你设置的约束来帮你计算一个控件的位置和大小。所以,一个UILabel肯定具备的四个条件是,内容、字体、行数、换行模式。也就是说,只要我们赋予了Label内容,那么它的大小也就确定了。所以,我们不需要特意指定一个UIlabel的宽高,除非你有什么特殊要求。

那么UIView为什么不可以自己计算大小?答案其实也可以猜到,因为他没有内容。

由系统自动设置的UILabel,它的大小总是会和内容大小刚好一致,但是如果我们期望UILabel的大小总是比内容宽高都大一些,也就是所谓的留白。

那么,我们应该怎么做呢?
首先,我们创建一个继承于UILabel的自定义试图,然后重写
- (CGSize)intrinsicContentSize,这个方法。代码如下。

- (CGSize)intrinsicContentSize
{
    CGSize originalSize = [super intrinsicContentSize];

    CGSize size = CGSizeMake(originalSize.width+20, originalSize.height+20);

    return size;
}

上述代码的意思就是,我们先获取系统通过Label的内容计算出来的宽和高,再分别给他增大再返回新的Size就可以了。再运行一下,你就会发现,Label的大小就会比内容大了。(别忘了,把对齐方式设置为居中)

再回到之前的那个问题,UIView如果只设置坐标,不设置大小会报错的问题。
如果是用代码写约束,如果你只想设置坐标不想设置大小,那么你需要像上面的代码一样,在- (CGSize)intrinsicContentSize为你的UIView指定一个默认大小。
如果是在XIB里,那么你需要在下图这个Instrinsic Size的属性里设置为Placeholder。这样,Xcode就不会报错了。

2. Content Hugging Priority和Content Compression Resistance

这两个概念需要结合我们上面讲的Intrinsic Content Size来理解,每一个控件都有一个系统计算的最佳大小。
所以,Content Hugging Priority这个属性就代表着,一个控件拒绝本身size大于InstrinsicSize的优先级。
那么Content Compression Resistance,这个属性代表着,一个控件拒绝本身size小于InstrinsicSize的优先级。
这样子说还是有点抽象。那么我们来看一个例子好了。

我们在storyboard中创建了一个UITableView,并在其中创建了一个自定义的UITableViewCell。
这个自定义的UITableviewCell中有两个Label,都是多行显示的Label。然后给他们设定约束。
第一个Label设定的约束为:

  • Leading Space:8
  • Trailing Sace:8
  • Top Space:8
  • Bottom Space(距离下面的Label的间距): 9

第二个Label设定的约束为:

  • Leading Space:8
  • Trailing Sace:8
  • Bottom Space:8
  • Top Space(距离上面面的Label的间距): 9

设定好了,ok,好像没什么问题。

但是,如果这时候你把UITableViewCell的高度扩大。看看是怎样的结果。Xcode报错了。为什么?
因为Cell的高度扩大,势必会影响两个Label的位置和大小,所以,现在Label面临着一个问题,在保持和父View(也就是UITableViewCell的contentView)间距不变的情况下,必须有一个Label是需要妥协的,怎么个妥协法呢?就是要扩大高度,扩大高度,就意味着比label本身的文字内容的高要大了。
那么,到底是两个Label中的哪一个label做出这个妥协呢?
Xcode并不知道,因为这个不知道,所以Xcode报错了。
话说回来了,Xcode为什么会不知道,就是因为两个Label的Content Hugging Priority里面的Vertical这个属性的值是一样的。因为两个Label拒绝变高的优先级相同,以至于Xcode不知道到底该拉伸哪个Label。所以,解决方案就是,把其中一个的改小
这样就可以了。那么,这样就结束了么?
如果这时候,你再把UITableViewCell的高度减小,会发生什么情况呢?Xcode又报错了。那么,这次的原因又是什么?

其实和上一个原因较为类似。

Cell的高度减小导致UILabel为了保持和父View间距不变,所以面临一个高度压缩的情况,那么到底谁压缩,Xcode依然不知道,因为两个Label的Content Compression Resistance这个属性里的Vertical优先级一样,和上面的解决办法一样只需要改小一个即可。

如果是手写Autolayout,在哪里写最好?

结论如下:

  • 如果是在自定义view中,写在init方法中。
  • 如果是在ViewController中,写在- (void)viewDidLoad()中。
    为什么不能写在viewDidAppear或者viewWillAppear中?
    因为这个东西和NSNotification是一样的,你不能确定viewDidAppearviewWillAppear调用的时机和调用的次序,不信的话,你可以用NSLog打印一下,并且使用手势在NavigationController中不停的左右滑动控制器,看看打印的结果。
    但,viewDidLoad是可以保证在整个生命周期只出现一次的。为了避免约束重复添加,所以你应该在viewDidLoad中添加。
文/叶孤城___(简书作者)
原文链接:http://www.jianshu.com/p/c4f3303c63d8
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值