<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Pluto Y]]></title><description><![CDATA[嗨，我叫章龙华，一名由Java EE转到iOS的程序猿。不断的修行与前进，喜欢学习新的知识，寻找志同道合之人。不断努力着，为了明天的更轻松。]]></description><link>http://www.pluto-y.com/</link><generator>Ghost 0.7</generator><lastBuildDate>Wed, 25 Feb 2026 15:36:07 GMT</lastBuildDate><atom:link href="http://www.pluto-y.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[从自己实现isa-swizzling到说一些Runtime的内容]]></title><description><![CDATA[<h2 id="">请叫我背景大人</h2>

<p>前段时间写了一篇<a href="http://www.nscookies.com/isa-swizzling/">isa-swizzling?什么鬼？</a>的文章，就想说稍微实现一下<code>isa-swizzling</code>。然而其实实现起来非常简单，但是其中的一些Runtime内容以及一些其他的知识还是值得一讲的。</p>

<p>好吧，我承认我本来是想写自己实现KVO，然后我发现太长了，想说先从<code>isa-swizzling</code>开始。然而这并不妨碍我可能还会更一篇来说一些手动实现KVO，但是谁知道呢。如果还完全不了解<code>isa-swizzling</code>干了什么的同学（路人甲：谁是什么~，路人乙：你好污，巫妖王），<a href="http://www.nscookies.com/isa-swizzling/">isa-swizzling?什么鬼？</a>不多说，自己看。</p>

<h2 id="">关门放狗，不对，是代码</h2>

<pre><code class="language-objectivec">static void DefaultSetterForKVO(id self, SEL _cmd, void *value)   {  
    NSString *setterName = NSStringFromSelector(_cmd);
    NSString *key = [self obtainGetterFromSetter:setterName]</code></pre>]]></description><link>http://www.pluto-y.com/isa-swizzling-and-runtime/</link><guid isPermaLink="false">e6706a64-0c1a-41a5-9b2e-5cfb938878fa</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Wed, 07 Sep 2016 23:42:54 GMT</pubDate><content:encoded><![CDATA[<h2 id="">请叫我背景大人</h2>

<p>前段时间写了一篇<a href="http://www.nscookies.com/isa-swizzling/">isa-swizzling?什么鬼？</a>的文章，就想说稍微实现一下<code>isa-swizzling</code>。然而其实实现起来非常简单，但是其中的一些Runtime内容以及一些其他的知识还是值得一讲的。</p>

<p>好吧，我承认我本来是想写自己实现KVO，然后我发现太长了，想说先从<code>isa-swizzling</code>开始。然而这并不妨碍我可能还会更一篇来说一些手动实现KVO，但是谁知道呢。如果还完全不了解<code>isa-swizzling</code>干了什么的同学（路人甲：谁是什么~，路人乙：你好污，巫妖王），<a href="http://www.nscookies.com/isa-swizzling/">isa-swizzling?什么鬼？</a>不多说，自己看。</p>

<h2 id="">关门放狗，不对，是代码</h2>

<pre><code class="language-objectivec">static void DefaultSetterForKVO(id self, SEL _cmd, void *value)   {  
    NSString *setterName = NSStringFromSelector(_cmd);
    NSString *key = [self obtainGetterFromSetter:setterName];
    [self willChangeValueForKey:key];
    Class subCls = object_getClass(self);
    Class supCls = class_getSuperclass(subCls);
    struct objc_super superInfo = {
        self,
        supCls
    };
    ((void (*) (void * , SEL, ...))objc_msgSendSuper)(&amp;superInfo, _cmd, value);
    [self didChangeValueForKey:key];
}

- (void)py_addObserver:(NSObject *)observer
            forKeyPath:(NSString *)keyPath
               options:(NSKeyValueObservingOptions)options
               context:(void *)context {
    // isa-swizzling implement
    NSString *newName = [@"PYKVONotifying_" stringByAppendingString:NSStringFromClass(object_getClass(self))]; 
    NSString *setterName = [[@"set" stringByAppendingString:[[[keyPath uppercaseString] substringToIndex:1] stringByAppendingString:[keyPath substringFromIndex:1]]] stringByAppendingString:@":"];
    Class subCls = objc_allocateClassPair(object_getClass(self), [newName UTF8String], 0);
    class_addMethod(subCls, NSSelectorFromString(setterName), (IMP)DefaultSetterForKVO, "v@:@");
    objc_registerClassPair(subCls);
    object_setClass(self, subCls);
}
</code></pre>

<p>其实代码挺简单的，也就这20+行代码，但是里面有的一些东西还是可以看看的。</p>

<h2 id="addobserverforkeypathoptionscontext">解析一：<code>addObserver:forKeyPath:options:context:</code></h2>

<p>上面是重写的<code>addObserver:forKeyPath:options:context:</code>一小部分，但是这里面的知识点也可圈可点。我们先从<code>addObserver</code>开始看吧。</p>

<p>前两句没什么好说的，其实就是根据原有类名以及<code>keyPath</code>来获得新类名以及需要重写的方法<code>setter</code>方法（不对啊，keyPath可能带'.'之类的啊），保安有人捣乱。好吧，其实这里就是简单的忽略keyPath，假设不存在那些特殊情况。</p>

<p>接下来剩下的三行代码分别是动态生成类以及以及添加新的方法以及注册类。我们说说最后一个调用<code>object_setClass</code>的作用，为什么要这么做呢？我们都说了是<code>isa-swizzling</code>了当然第一个作用就是通过这个方法来“偷梁换柱”，将<code>isa</code>换成我们新产生的这个类，那么为什么有必要将<code>isa</code>指向新的类呢？其实主要目的是：我们都知道<code>objc_class</code>中保存着我们实例方法的<code>objc_method_list</code>，而一个类的<code>isa</code>才是指向这个类的Class，也就是<code>objc_class</code>。也就是说Objc在查找一个类的实现方法是通过<code>isa</code>去查找具体Class的<code>objc_method_list</code>。
最终就会变成下面这样:</p>

<p><img src="http://www.pluto-y.com/content/images/2016/09/isa-swizzling-UML.png" alt="isa-swizzling-UML.png"></p>

<h2 id="defaultsetterforkvo">解析二：<code>DefaultSetterForKVO</code></h2>

<p>估计有些小伙伴要抓狂了，一方面是为什么<code>static void DefaultSetterForKVO(id self, SEL _cmd, void *value)</code>这样的函数声明，另一方面<code>objc_super</code>和<code>objc_msgSendSuper</code>这句是什么鬼。不急，在说这些之前，我们先做一个<code>clang -rewrite-objc</code>的操作，解析的内容是下面这些内容的<code>.m文件</code>:</p>

<pre><code class="language-objectivec">// Person.h文件
@interface Person : NSObject

@property (nonatomic, copy) NSString *id;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger age;

@end

// Person.m文件
@implementation Person

- (void)test {
    [super description];
}

@end
</code></pre>

<p>然后我们从中截取一部分对我们目前来说有用的信息，至于里面的一些其他内容小伙伴可以从Github上找到对应的Person.cpp文件查看。</p>

<pre><code class="language-c">struct __rw_objc_super {  
    struct objc_object *object; 
    struct objc_object *superClass; 
    __rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
};

// @implementation Person
static void _I_Person_test(Person * self, SEL _cmd) {  
    ((NSString *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}, sel_registerName("description"));
}

static NSString * _I_Person_id(Person * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_id)); }  
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_Person_setId_(Person * self, SEL _cmd, NSString *id) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _id), (id)id, 0, 1); }

static NSString * _I_Person_name(Person * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)); }  
static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _name), (id)name, 0, 1); }

static NSUInteger _I_Person_age(Person * self, SEL _cmd) { return (*(NSUInteger *)((char *)self + OBJC_IVAR_$_Person$_age)); }  
static void _I_Person_setAge_(Person * self, SEL _cmd, NSUInteger age) { (*(NSUInteger *)((char *)self + OBJC_IVAR_$_Person$_age)) = age; }  
// @end

static struct /*_method_list_t*/ {  
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[7];
} _OBJC_$_INSTANCE_METHODS_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    7,
    {{(struct objc_selector *)"test", "v16@0:8", (void *)_I_Person_test},
    {(struct objc_selector *)"id", "@16@0:8", (void *)_I_Person_id},
    {(struct objc_selector *)"setId:", "v24@0:8@16", (void *)_I_Person_setId_},
    {(struct objc_selector *)"name", "@16@0:8", (void *)_I_Person_name},
    {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_Person_setName_},
    {(struct objc_selector *)"age", "Q16@0:8", (void *)_I_Person_age},
    {(struct objc_selector *)"setAge:", "v24@0:8Q16", (void *)_I_Person_setAge_}}
};
</code></pre>

<p>我们来尝试看看，首先可以看到上面有<code>@implementation Person</code>和<code>@end</code>被注释了，在机上里面的内容我们可以看出来这一部分是<code>Person.m</code>文件转换成C之后的代码。而根据下面的一个struct，我们可以看得出来，其实clang为我们分装成具体<code>objc_method_list</code>方面之后好查找。</p>

<p>同时我们也可以看到原本我们的方法都被<code>_I_Person_方法名(Person *self, SEL _cmd, [其他参数...])</code>的形式。而这个形式正是我们上面的<code>DefaultSetterForKVO</code>的形式，也就是说我们可以通过这样写直接转换成<code>- (void)DefaultSetterForKVO:(void *)value</code>的形式, 只是我们直接将其转换成C的形式来实现。</p>

<p>然后我们来看看<code>test</code>方法的实现，看起来好像很复杂，我们一点点分解，先看这一段<code>(__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}</code>,其实这一段的作用就是创建一个最开头看到的<code>__rw_objc_super</code>的struct，而这个<code>__rw_objc_super</code>就和我们上面<code>objc_super</code>是等价的。然后就是调用<code>objc_msgSendSuper</code>这个C方法,这句话很纸老虎，其实它很多内容都是在进行类型转换。那么通过这个<code>[super description]</code>可以看得出来，这个其实就是调用了<code>objc_msgSendSuper</code>,然后是需要创建一个<code>objc_super</code>的struct作为第一个参数，而第二个参数是selector。那么根据我们<code>objc_msgSendSuper</code>的了解，如果之后函数还有其他参数就应该添加在之后。</p>

<blockquote>
  <p>从这里也能看出<a href="http://weibo.com/1364395395/">@我就叫Sunny怎么了</a>博客中<a href="http://blog.sunnyxx.com/2014/11/06/runtime-nuts/">神经病院objc runtime入院考试</a>中的第一题的解释中为什么<code>reveiver</code>还是<code>self</code>的答案。</p>
</blockquote>

<p>那么在<code>DefaultSetterForKVO</code>中原本我们的实现应该是:</p>

<pre><code class="language-objectivec">[self willChangeValueForKey:key];
[super setter:value];
[self didChangeValueForKey:key];
</code></pre>

<p>但是我们的setter是根据keyPath进行变化的，所以这里通过C来实现，并且将其转换成了<code>objc_msgSendSuper</code>的调用，最终实现上面这三行代码。</p>

<h2 id="">小结</h2>

<p>到这里如果我们通过手动实现<code>isa-swizzling</code>基本实现了,也知道了里面细节的内容。其实整体的逻辑非常简单，就是讲原本的类的isa替换成新生成类，并且新生成类是原本类的子类。在新生成类中添加插入<code>willChangeValueForKey</code>和<code>didChangeValueForKey</code>的调用。</p>

<p>就这样结束了？当然不是,通过一些实验发现其实<code>isa-swizzling</code>应该还有做一些其他的工作，例如通过这样替换<code>isa</code>后原本的<code>class</code>，<code>superClass</code>，<code>isKindOfClass</code>等一些跟类有关的相关方法应该也会重新实现。否则通过调用<code>class</code>方法即将返回新生成子类。但是这个这里就不具体讲了。</p>

<p>关于Demo的代码小伙伴可以在我的<a href="https://github.com/Pluto-Y/Demos/tree/master/CustomIsaSwizzling">Github</a>上找到。</p>

<h2 id="">广告</h2>

<p>接下来是广告时间，本人开了一个订阅号，有兴趣的小伙伴关注一下：iOS周记。</p>

<p>可以查找关键字：Weekly_iOS进行关注，也可以扫一扫下方的二维码
<img src="http://www.pluto-y.com/content/images/2016/02/qrcode_for_gh_da9e1ad55fff_430.jpg" alt="订阅号"></p>]]></content:encoded></item><item><title><![CDATA[iOS适配之旅——Autolayout时代]]></title><description><![CDATA[<h2 id="">开篇</h2>

<p>其实对于<code>Autolayout</code>的资料非常非常的多，博主也是略自皮毛，也上不了大雅之堂。这里只是给大家稍微整理一下，可定也不是很全面，主要是针对<code>AutoLayout</code>给这个系列进行稍微的讲解一下。其实呢，对于很多人来说<code>Autolayout</code>都懂的非常多，我呢，其实也不是很想写，我也是被人架着刀放在脖子上去写的。既然大家不想听，那这篇就到此为止吧。（特么的，你是来骗我们进来的么，要么讲要么退票钱😡）好吧，既然大家都想稍微听听，那就听老娘，咳咳咳，不好意思，听本少爷慢慢说来。</p>

<h2 id="">引言</h2>

<p>其实相对于<code>Autoresizing</code>来说，<code>Autolayout</code>是另一个质的飞跃。他能适配的情况更多。而对于<code>Autolayout</code>来说他是一个非常强大的工具，即使对于之后的<code>Size Class</code>来说其中核心用的还是<code>Autolayout</code>,那么就有人会问了，<code>AutoLayout</code>到底是什么呢，或者说是他是怎么实现的呢。其实<code>Autolayout</code></p>]]></description><link>http://www.pluto-y.com/ios-layout-guides-autolayout/</link><guid isPermaLink="false">6f8ce36a-b6b5-4778-8452-2a69b090df3c</guid><category><![CDATA[Layout]]></category><category><![CDATA[AutoLayout]]></category><category><![CDATA[iOS]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Tue, 12 Apr 2016 05:30:20 GMT</pubDate><content:encoded><![CDATA[<h2 id="">开篇</h2>

<p>其实对于<code>Autolayout</code>的资料非常非常的多，博主也是略自皮毛，也上不了大雅之堂。这里只是给大家稍微整理一下，可定也不是很全面，主要是针对<code>AutoLayout</code>给这个系列进行稍微的讲解一下。其实呢，对于很多人来说<code>Autolayout</code>都懂的非常多，我呢，其实也不是很想写，我也是被人架着刀放在脖子上去写的。既然大家不想听，那这篇就到此为止吧。（特么的，你是来骗我们进来的么，要么讲要么退票钱😡）好吧，既然大家都想稍微听听，那就听老娘，咳咳咳，不好意思，听本少爷慢慢说来。</p>

<h2 id="">引言</h2>

<p>其实相对于<code>Autoresizing</code>来说，<code>Autolayout</code>是另一个质的飞跃。他能适配的情况更多。而对于<code>Autolayout</code>来说他是一个非常强大的工具，即使对于之后的<code>Size Class</code>来说其中核心用的还是<code>Autolayout</code>,那么就有人会问了，<code>AutoLayout</code>到底是什么呢，或者说是他是怎么实现的呢。其实<code>Autolayout</code>都是基于一种叫做约束(<code>NSLayoutConstraint</code>)的东西。其实很多iOS程序员应该都懂。至于是关于<code>NSLayoutConstraints</code>入门我就不说了。这里我就开始说一些我想说的东西吧。
好了接下来我主要分三个方面来给大家讲述如何添加约束。当然，下面的知识最好是建立在对<code>NSLayoutConstraint</code>有一定了解的情况下来看，毕竟我是一个非常傲娇的啦。</p>

<h2 id="">代码添加</h2>

<p>首先其实对于<code>Autolayout</code>来说添加的方式有两种，分别是xib和代码添加。
而对于xib上的添加小伙伴应该会比较熟悉一点，那么我们就从代码添加开始吧。众所周知，所谓的<code>Autolayout</code>时间里在一些列的<code>NSLayoutConstraint</code>的实例上进行确定每个控件的大小以及位置的。</p>

<h3 id="">创建</h3>

<p>好了下面先说说<code>NSLayoutConstraint</code>在代码上是如何创建的吧。</p>

<h4 id="ios6">iOS 6</h4>

<p>上代码，关门放OC：</p>

<pre><code class="language-objectivec">// 水平居中
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:centerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
// 垂直居中
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:centerView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];
// 宽度为20
[centerView addConstraint:[NSLayoutConstraint constraintWithItem:centerView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeWidth multiplier:1.0 constant:20]];
// 高度为20
[centerView addConstraint:[NSLayoutConstraint constraintWithItem:centerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:centerView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
</code></pre>

<p>Swift也就类似了，这里就写一个吧。Swift上:</p>

<pre><code class="language-swift">NSLayoutConstraint(item: firstItem, attribute: .Trailing, relatedBy: .Equal, toItem: secondItem, attribute: .Leading, multiplier: 1.0, constant: 0)  
</code></pre>

<blockquote>
  <p>如果想要用代码进行添加约束，记得将对应的<code>translatesAutoresizingMaskIntoConstraints</code>属性设置成NO或者false.</p>
</blockquote>

<p>可以看得出其实一个<code>NSLayoutConstraint</code>是由两个控件以及对应的属性和关系得到的。（当然两个控件也有可能是指同一个控件，如要设置一个控件的高等于宽的情况等）那么，这里就采用网络上经常看到的等式进行说明一下，其实这里就是<code>firstItem.attribute relatedBy secondItem.attribute * multiplier + constant</code>这样的等式。当然其实上面方法还有一个参数是没有初始化的，就是<code>priority</code>,这个用来规定是用来规定优先级的，数值是从1-1000，数值越大优先级越高。优先级高的先满足，在满足优先级高的情况下再去满足优先级低的。通过上述方法创建的情况下<code>priority</code>是默认值是1000的。
当然对于firstItem和secondItem还是有一定条件约束的，也就是这两个视图有同一个“祖先”视图，比如下面这种情况是允许的：</p>

<ul>
<li>UIView:A
<ul><li>UIView:B</li>
<li>UILabel </li>
<li>UIImageView</li></ul></li>
<li>UIView:C
<ul><li>UIButton </li></ul></li>
</ul>

<p>那么在这种情况下<code>UILabel和UIImageView</code>以及<code>UIViewB和UIImageView</code>之间是可以建立约束的，但是<code>UIButton和UIView:A中的其他控件</code>之间是不可以建立约束的。这是为什么呢，这个就有点像在加家谱中。如A家族中的所有成员都有同一个祖先，那么A家族中的所有成员之间多少都扯的上一些亲属关系，可是你突然来一个B家族，他们之间没有血缘关系，平时也没有关联，那么总不能说B家族中的成员跟A家族中的成员有亲属关系吧。（那B家族中的成员嫁给A家族呢？）咳咳咳，保安保安，这里有人捣乱，赶紧拉出去。</p>

<p>好了，回归正传，这里其实还有一个很多刚开始用代码写约束的人有的一个疑问，就是经常我们需要给某一个<code>UIView</code>添加约束，那么它的约束应该添加到firstItem还是secondItem呢？其实单纯的说添加给他们两个哪一个控件都是不够准确的，当然在一些情况下也有可能是两个Item中的一个。其实应该是加在离他们最近的"祖先"上。当然这个"祖先"也有可能就是他们自己。比如<code>UIILabel和UIView:B</code>的约束就加在<code>UIView:B</code>上。而<code>UIImageView和UILabel</code>加在<code>UIView:A</code>上。(这里就的思想有点像求最小公倍数的赶脚。)</p>

<p>除了上面这种创建方式以外，还有在采用<code>VFL</code>来进行创建一系列的约束，比如他可以为一个视图创建好一个方向上的所有约束之类的。但是关于<code>VFL</code>资料还是相对比较少，而且官方文档讲解的也比较少。博主也不爱用这种方式，如果有兴趣的小伙伴可以Google中搜索百度然后再搜索VFL来查找资料。😈
不过这里还是提供一下他的创建方法：</p>

<pre><code class="language-objectivec">// OC代码,在NSLayoutConstraint中
+ (NSArray&lt;__kindof NSLayoutConstraint *&gt; *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary&lt;NSString *,id&gt; *)metrics views:(NSDictionary&lt;NSString *, id&gt; *)views;
</code></pre>

<pre><code class="language-swift">// Swift代码
public class func constraintsWithVisualFormat(format: String, options opts: NSLayoutFormatOptions, metrics: [String : AnyObject]?, views: [String : AnyObject]) -&gt; [NSLayoutConstraint]  
</code></pre>

<h4 id="ios7">iOS 7</h4>

<p>关于Auto Layout在iOS7上的变动相对比较少，其中最大的变动应该是关于创建约束的方式进行巨大变化(与其说是iOS 7的变化，不如说是Xcode的变化)，在Xcode 5中是支持了通过Nib来进行添加约束了，详情看后一小节。</p>

<h4 id="ios8">iOS 8</h4>

<p>在iOS 8中添加了许多<code>NSLayoutAttribute</code>的属性，让在添加约束上更加方便以及可添加的约束更加细致到具体细节。这里需要特别提的是关于<code>Margin</code>的属性，具体是什么情况可以看看下文的一些提到的一些地方。</p>

<h4 id="ios9">iOS 9</h4>

<p>是不是觉得上面的创建方式麻烦的要死。博主也觉得,所以在Github上就出现了一个开源叫做<a href="https://github.com/SnapKit/Masonry">Masonry</a>，有兴趣的小伙伴可以看看，特别是那些需要从低版本开始兼容的小伙伴多看看。
如果即熟悉<a href="https://github.com/SnapKit/Masonry">Masonry</a>并且也熟悉iOS 9关于AutoLayout的新特性的小伙伴一定觉得有一部分特性是类似的。关于建立约束上，iOS 9在<code>UIView</code>中添加了<code>NSLayoutAnchor</code>以及其中的API方便大家在创建约束上更加直观以及方便。
下面稍微上一下代码，具体的大家可以查看<code>NSLayoutAnchor</code>这个类:</p>

<pre><code class="language-objectivec">// 可以这样
[UIImageView.trailingAnchor constraintEqualToAnchor:UILabel.leadingAnchor]
// 或者
[UIImageView.trailingAnchor constraintEqualToAnchor:UILabel.leadingAnchor constant:20]
</code></pre>

<pre><code class="language-swift">// Swift 代码
UIImageView.leadingAnchor.constraintEqualToAnchor(UILabel.trailingAnchor)  
// 或者
UIImageView.leadingAnchor.constraintEqualToAnchor(UILabel.trailingAnchor, constant: 20)  
</code></pre>

<p>是不是觉得相对于之前创建方式更加直观以及方便，并且这个创建好之后完全不用在意他是哪一个<code>UIView</code>来添加<code>Constraint</code>。它默认会将其加到正确的视图上。当然在建立好正确的Constraints之后，记得用<code>NSLayoutConstraint</code>中的<code>activateConstraints</code>来激活对应的约束。</p>

<p>在iOS 9中还添加了一个名为<code>UILayoutGuide</code>,这个组件虽然不像是<code>UIView</code>一样显示在界面上,但是确实有想其他视图控件占据着布局中的一个位置和空间，就好像有一个有一个透明的<code>UIView</code>一样，但是它却不在视图的层次中。下面举一个使用的例子🌰：</p>

<pre><code class="language-objectivec">UILayoutGuide *space1 = [[UILayoutGuide alloc] init];  
[self.view addLayoutGuide:space1];
UILayoutGuide *space2 = [[UILayoutGuide alloc] init];  
[self.view addLayoutGuide:space2];
[space1.widthAnchor constraintEqualToAnchor:space2.widthAnchor].active = YES;
[self.saveButton.trailingAnchor constraintEqualToAnchor:space1.leadingAnchor].active = YES;
[self.cancelButton.leadingAnchor constraintEqualToAnchor:space1.trailingAnchor].active = YES;
[self.cancelButton.trailingAnchor constraintEqualToAnchor:space2.leadingAnchor].active = YES;
[self.clearButton.leadingAnchor constraintEqualToAnchor:space2.trailingAnchor].active = YES;
</code></pre>

<pre><code class="language-swift">let space1 = UILayoutGuide()  
self.view .addLayoutGuide(space1)  
let space2 = UILayoutGuide()  
self.view.addLayoutGuide(space2)  
space1.widthAnchor.constraintEqualToAnchor(space2.widthAnchor).active = true  
self.saveButton.trailingAnchor.constraintEqualToAnchor(space1.leadingAnchor).active = true  
self.cancelButton?.leadingAnchor.constraintEqualToAnchor(space1.trailingAnchor).active = true  
self.cancelButton.trailingAnchor.constraintEqualToAnchor(space2.leadingAnchor).active = true  
self.clearButton?.leadingAnchor.constraintEqualToAnchor(space2.trailingAnchor).active = true  
</code></pre>

<p>上方的代码是建立的三个按钮，并且在<code>saveButton</code>、<code>cancelButton</code>以及<code>clearButton</code>之间建立了相等的间距。</p>

<p>除了以上的新增内容外，其实在iOS9中也添加了一个很重要的视图，如果那些只从iOS9开始兼容的小伙伴，可以多用这个视图------<code>UIStackView</code>。有兴趣的小伙伴可以多去尝试以下，是一个非常不错的空间，能适配一些之前适配过程中不易适配的一些情况。当然如果有小伙伴希望从iOS6.0开始兼容，但是也想用<code>UIStackView</code>的功能的话，小伙伴可以使用以下用<a href="https://github.com/forkingdog/FDStackView">FDStackView</a>这个控件。由于<code>UIStackView</code>不是本篇的重点，所以这里就不给大家展开来讲解了。</p>

<p>好了，既然上面说了添加约束，那么在代码上怎么添加约束呢？</p>

<h3 id="">添加(删除)约束</h3>

<h4 id="ios6">iOS 6</h4>

<p>直接上代码吧，先来个OC版的：</p>

<pre><code class="language-objectivec">[UIView:A addConstraint:[NSLayoutConstraint constraintWithItem:UIImageView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:UILabel attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]]
</code></pre>

<p>然后来个Swift版本</p>

<pre><code>UIView:A.addConstraint(NSLayoutConstraint(item: UIImageView, attribute: .Trailing, relatedBy: .Equal, toItem: UILabel, attribute: .Leading, multiplier: 1.0, constant: 0))  
</code></pre>

<p>通过上述代码就可以进行添加了。当然除了这个<code>addConstraint</code>还有一个<code>addConstraints</code>方法是用来添加多个约束的情况。当然，在添加约束之后，小伙伴们最好调用一些跟新最近的“祖先”的<code>layoutIfNeeded</code>方法。这样才能生效。
除了添加约束的Add方法外，苹果官方还提供了对应的remove方法，与之相对应的分别是<code>removeConstraint</code>和<code>removeConstraints</code>的方法，小伙伴可以自己去尝试一下。</p>

<blockquote>
  <p>当然，如果在讲约束的某一个视图删除之后，不要忘了将含有该视图的所有约束情况，否则会出现crash的情况</p>
</blockquote>

<h4 id="ios8">iOS 8</h4>

<p>上面提到添加和删除约束的方式，可是根据前人的经验，发现通过Add和Remove的方法来控制约束的话，相对来说还是比较笨重的，而且效率不是很高。为此，苹果官方在iOS 8中推出了<code>activateConstraints</code>和<code>deactivateConstraints</code>的接口。特别是有些时候我们只是针对两到三种不同情况进行不同的适配，那么在重复的使用Add和Remove的方式会使我们App的效率降低很多，因此在iOS 8中推出了<code>activateConstraints</code>和<code>deactivateConstraints</code>。
那么具体怎么用呢？其实也是非常非常简单的，只要通过下面这种方式即可:</p>

<pre><code class="language-objectivec">// 使约束生效
[NSLayoutConstraint activateConstraints:constraintsArray]
// 使约束失效
[NSLayoutConstraint deactivateConstraints:constraintsArray]
</code></pre>

<pre><code class="language-swift">// 使约束生效
NSLayoutConstraint.activateConstraints(constraintsArray)  
// 使约束失效
NSLayoutConstraint.deactivateConstraints(constraintsArray)  
</code></pre>

<p>当然相对应的在<code>NSLayoutConstraint</code>中也添加了<code>active</code>的属性，我们可以通过这个属性来判断以及使一个约束生效。
并且在WWDC 2015上也建议尽量用<code>activateConstraints</code>和<code>deactivateConstraints</code>来提高程序的效率。</p>

<p>可是如果小伙伴们在自定义的控件的时候，如果将添加约束的代码放在<code>updateConstraints</code>来进行添加的，然后大家加完之后发现是没有效果的，那是因为这里其实是需要对UIView的<code>requiresConstraintBasedLayout</code>这个方法进行重写，需要将其返回值设置成<code>YES</code>或<code>true</code>才可以。</p>

<h3 id="priority">Priority</h3>

<p>关于Priority在前面我们也稍微提过，就是针对优先级来说的。可是对于<code>Priority</code>来说还有两个特别的优先级，分别为<code>Content Hugging Priority</code>以及<code>Content Compression Resistance Priority</code>,这两个如果有了解的童鞋几乎可以跳过，没了解的童鞋这里稍微讲一下。通过这两个属性来看看优先级是怎么工作的。</p>

<p>在讲解这个之前，先给大家讲一个概念，叫做<code>Intrinsic Content Size</code>。什么叫做<code>Intrinsic Content Size</code>呢？这里举个例子，比如对于一个<code>UILabel</code>字体、内容等一系列参数，其实是可以立即确定<code>UILabel</code>的Size的。那么这种内在的Size,不用通过其他手段或者是跟其他视图无关而能获得的Size被称为<code>Intrinsic Content Size</code>。类似的还有<code>UIButton</code>以及<code>UIImageView</code>都有对应的<code>Intrinstic Size</code>。换一句话来说，其实在我们对于这些控件的Size不进行设置，而只对Position进行设置的情况下，这些控件都会自动根据内容进行产生<code>Intrinsic Content Size</code>。</p>

<p>好了，继续说上面两个<code>Priority</code>,这两个<code>Priority</code>可以理解为<code>可以压缩度</code>以及<code>可拉伸度</code>（当然别的博客可能有别的说法），不过中文的翻译都是为了让童鞋们更容易理解。至于这里我为什么叫做<code>可压缩度</code>和<code>可拉伸度</code>呢？因为对我来说<code>Hug</code>是拥抱的意思，那这个<code>Content Hugging Priority</code>应该是如果优先级越高，那就<code>可压缩度</code>应该就越高，那剩下的另一个就相反咯。如果<code>Content Compression Resistance Priority</code>的值越大，那就是可拉伸度就越高咯。而这里的可拉伸度和可压缩度针对的对象是什么呢？聪明的小伙伴们应该猜出来了，就是<code>Intrinsic Content Size</code>。</p>

<p>下面用一个🌰来解释给大家看看是什么意思。
比如我有一个昵称的长度不确定，可是当我给<code>UILabel</code>添加了一个<code>Width</code>的约束为60，可是昵称突然长度变成了70怎么办，其实只需要将<code>Content Compression Resistance Priority</code>的值设置的大于60约束的Priority即可。也就是在两个针对同一个属性值约束的情况下，当然是优先满足大的，也就是可拉伸度的属性啦。虽然你说宽度为60，可是我的等级比你大，也就是说先满足我，所以可以拉伸。
同样的，对于如果出现<code>Width</code>的约束为60，可是昵称的长度变成50的情况，如果<code>Content Hugging Priority</code>的优先级大于<code>Width</code>的情况下，<code>UILabel</code>的宽度也会变成50。
下面通过两张图来看看上面所说的例子：</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/ContentCompressionPriority.png" alt="ContentCompressionPriority.png">
<img src="http://www.pluto-y.com/content/images/2016/04/ContentHuggingPriority.png" alt="ContentHuggingPriority.png"></p>

<p>可以看出虽然宽度都设置为60了，可是在内容多或者少的时候，其宽度都会根据内容还自动压缩或者拉伸。因为我讲<code>Width</code>这个约束的<code>Priority</code>设置为10，而<code>Content Compression Resistance Priority</code>和<code>Content Hugging Priority</code>的值分别为251何750,所以总会根据内容进行变化，当然其实这样就跟没设置宽度约束一样了。因为<code>UILabel</code>的宽度总是等于<code>Intrinstic Size</code>。
那么在代码上怎么进行设置呢？其实也是非常简单的：</p>

<pre><code class="language-objectivec">// Objective-C代码
[longLbl setContentCompressionResistancePriority:750 forAxis:UILayoutConstraintAxisHorizontal];
</code></pre>

<pre><code class="language-swift">// Swift代码
longLbl.setContentHuggingPriority(750, forAxis: .Horizontal)  
</code></pre>

<p>不管从代码上还是xib上，我们都可以看得出来，关于这两个约束其实还有分的是水平方向还是垂直方向，其实<code>压缩</code>和<code>拉伸</code>就是指针对一个方向的嘛。</p>

<h2 id="xibstoryboard">xib和storyboard的逆袭</h2>

<p>这边我们将讲讲在Xcode中一些常用的关于适配方面的工具吧，用好这些工具能让你的适配效率更好，而且也能体会到其中的便利的地方。</p>

<h3 id="">工具</h3>

<p>首先先来说说在xib和storyboard(以下都用xib代替二者)右下角的这个几个按钮<iframe src="http://www.pluto-y.com/content/images/2016/04/xib_right_bottom.png" width="216" height="46" scrolling="no" frameborder="0" class="iframe-resizer"></iframe>的功能，以及提供讲解以下大概的用途和使用方法。</p>

<h4 id="stack">Stack</h4>

<p>左边第一个工具的功能非常简单，就是将选择好的几个控件放到一个<code>UIStackView</code>中，由于是<code>UIStackView</code>所以这里就不进行展示了。</p>

<h4 id="align">Align</h4>

<p>从图表可以看得出来他是针对于两个控件之间，由下图可以看得出来这个主要是针对两个视图相对位置的功能，洋气一点的说话就叫Align咯。</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/align.png" alt="Align"></p>

<p>那么具体怎么能方便的用这个功能呢？其实也非常简单的,就是只要将要进行对其的几个控件都选择然后点击要根据要对其的方式，选择对应的方式即可，具体方法如下：</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/align.gif" alt="Align"></p>

<p>可以看得出上图是将三个控件先选中(按住cmd然后左键点击需要选择的控件)后，然后再点击需要对其的方式即可。这里指的一提的在下方有一个<code>Update Frames</code>的下拉框里面有有三个选项，分别是：<code>None</code>, <code>Items of New Constraints</code>以及<code>All Frames in Containers</code>,那么这三个都是什么用的呢？其实可以看得出来这个主要是根据控件的位置和大小的，从名字就可以看得出来<code>None</code>就是不更新，这里主要讲下后面两个，第一个是对于那些新加的约束有关的控件都会出行刷新布局，而对于那些跟新的约束无关的视图不会变化。而最后一个，只要在同一个Container中的所有控件的视图都会进行刷新布局。小伙伴们可以试试。</p>

<h4 id="pin">Pin</h4>

<p>关于右边第二个的功能总结起来就是添加增对其他控件(默认是父控件)以及控件本身的一些约束，怎么说呢？看看点击Pin这个按钮的弹出框吧。</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/Pin.png" alt="Pin"></p>

<p>上图是只选择一个控件的情况下，可以看得出当前情况下的<code>Equal Widths</code>、<code>Equal Heights</code>以及<code>Align</code>都是暗的，即不可选择的。在这种情况下默认情况下是针对离自己最近的控件的约束以及自己的约束。
这里主要讲解一下两个部分上面那个部分，其他部分都是很好理解的，即上面的四个选择上面四个选择主要是针对里自己最近的一个控件的约束，怎么说呢？看下这张图：</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/Pindemo.png" alt="Pin_Demo"></p>

<p>如果在红色控件添加上下左右的约束的情况下，控件下边(Bottom)的约束的相对约束是UILabel的上边。而在除了上下左右以外,还有一个<code>Constrain to margins</code>的单选框,而这个主要是针对父控件来说的。那么具体是什么意思呢？其实简单的理解就是针对父控件20单位的位置的约束。也就是说如果我们选择这个选项，并且我们选择了上下左右(假设是针对父控件的情况下)都为0的情况下，那么其实这个时候就等价于我们具体父控件的上下左右都为20的情况。那么这个可以用在什么时候呢？博主对于具体的使用场景也不是很清楚，不过看过一篇文章有讲到一个使用场景，即在iOS 8的情况下，横屏的时候status bar的默认下是隐藏的（只是指不占高度位置，而不是完全不见），即我们的视图是从屏幕最顶端开始布局的，那么这个时候可能就会出现字跟status bar的重合，那么这个时候就有必要设置成针对<code>Margin</code>的约束了。</p>

<p>在上面我们提到了<code>Equal Widths</code>、<code>Equal Heights</code>以及<code>Align</code>三个不可使用的情况下，那么在上面情况下这些是可以用的呢？其实也很简单，就是在你选择多个控件的情况。在多选的情况下，那么这里的约束要么是相对于这两个控件(如<code>Align</code>和<code>Equal Widths</code>等)，要不然就是两个都是同样的针对选中控件的最近控件的约束，大家可以多去试试。这里就不一一讲述了。</p>

<h4 id="resolveautolayoutissues">Resolve Auto Layout Issues</h4>

<p>终于到最后一个了，从名字来看就知道这个其实是针对当出现问题的时候的解决方案。一般来说针对那些出现适配问题的处理方案。那么具体有哪些可选的方案呢？就不告诉你，就不告诉你，就不告诉你。</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/resolve_issue.png" alt="resolve_issue"></p>

<p>可以看的出来，主要分为两大块，一块是所谓的<code>Selected Views</code>和<code>All Views in View</code>,其实上下两个中的每个选项的功能是一样的，只是针对的视图不同而已。<code>Selected Views</code>是针对所选择的视图，而<code>All Views in View</code>是指所有的视图。所以这里就针对其中一个进行说明：</p>

<ul>
<li>Update Frames：根据当前约束改变视图布局，快捷键是<code>alt + cmd + =</code>这个快捷键很好用</li>
<li>Update Constraints：根据当前的Frame来修改约束</li>
<li>Add Missing Contraints：对那些还缺失约束视图的添加缺失的约束，当然这里添加的约束是Xcode推荐的</li>
<li>Reset to Suggested Constraints：将当前视图的约束变成Xcode推荐的约束(这个反而用的比较少)</li>
<li>Clear Constraints：清除所有跟视图相关的约束</li>
</ul>

<p>关于上方的提到的所谓的Xcode推荐的约束本宝宝真的不知道他是根据什么形式进行推荐的，所以不要再逼本宝宝了。麻烦那个知道的小伙伴能给本宝宝科普一下。
其实除了在这里的四个按钮以外，在菜单栏里面的<code>Editor</code>项中也有很多关于xib上的适配。这里就不一一讲解了，小伙伴可以自己去试试，里面有一些功能也是挺好用的，如果<code>Editor -&gt; Embed In</code>这个，还有<code>Editor -&gt; Algin</code>也是很好用的功能。</p>

<h3 id="utitlities">Utitlities视图</h3>

<p>关于这个这个视图估计小伙伴们熟的不能再熟了，那么他到底是什么鬼呢？</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/Utilities.png" alt="Utilities"></p>

<p>够熟悉吧，这里我就主要就讲讲<code>Utilities -&gt; Show the Size inspector</code>(倒数第二个tab)。毕竟跟约束相关的主要内容都是在这个tab中的。其中可以看到中间有一部分叫做<code>Constraints</code>的地方，可以看得出来我们可以在图中看到上下左右，水平垂直中线以及宽和高的约束。当其为虚线的时候代表其在这个地方没有约束，实线表示其有在这个地方有约束。</p>

<p>那么我们可以怎么使用这个地方的<code>Constraints</code>,首先我们可以点击某一个约束，如下图中我们点击了Bottom的约束,那么在下方我们可以看到所有跟选中视图的Bottom相关的约束都会列出来，比如和父视图的约束以及跟子视图的约束。</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/Constraints.png" alt="Constraints"></p>

<p>这里可以看得出<code>Constraints</code>可以看得出某个位置的约束，有一种类似过滤的功能。</p>

<p>其次下方的约束也可以进行点击右边的<code>Edit</code>进行修改约束的属性当然也可以通过双击来进行查看到具体约束的信息。当然也是在<code>Utilities -&gt; Show the Size inspector</code>这个位置。这里顺带提一句，当点击右边<code>Edit</code>的这个效果也可以在xib上双击某个约束出现相同的效果，如下图:(小伙伴可以自己试试)</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/dbclick_constraint.png" alt="dbclick_constraint"></p>

<p>然后我们再讲讲约束详情的一些东西吧，看看下图是一个约束的详情：</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/constraint_detail.png" alt="constraint_detail"></p>

<p>可以看得出<code>First item</code>和<code>Second item</code>是讲上面的<code>Item</code>和<code>Attribute</code>合在了一起。并且这里指的注意的是当如果是Button的时候，如果是<code>Margin</code>的<code>Attribute</code>的时候是相对文字的约束，我们都知道在<code>UIButton</code>在周围都会留一下空间，所以我们经常设置<code>UIbutton</code>的约束的时候，都会针对整个<code>UIButton</code>，如果在这里选择Attribute为<code>Margin</code>相关的时候，则会针对里面文字。小伙伴可以试试，剩余的东西大家应该都比较熟悉。</p>

<p>当然这里还有一个需要提的，就是<code>Identifier</code>在出现约束报错的情况下回起到非常大的作用。小伙伴们如果在出现报错的情况下，建议大家设置<code>Identifier</code>这样能将报错的信息从全地址的状态变化成显示具体<code>Identifier</code>的状态。</p>

<h3 id="xib">xib的结尾</h3>

<p>最后我们说说Xib最常用的一种添加约束的情况，小伙伴应该都会，就是通过点击按住<code>ctrl</code>然后点击鼠标左键进行拖拽进行添加新的约束，这里我就不给大家献丑了。而关于出现Xib上的约束的适配问题的情况小伙伴们应该也知道怎么处理了，都是非常常用的方法，这里就不进行累述了。</p>

<h2 id="skill">Skill-干货</h2>

<p>好了，终于到了这里了。上面理论的东西太多了，小伙伴们可以稍微消化消化。虽然内容多，可是大部分来说还是偏操作性质的，所以接受起来可能相对容易一点。想到以前上课看那么多理论的东西就头疼，我还是比较喜欢实践。咱们的老马克思主义说了，实践是检验真理的唯一标准嘛。在实践多了之后，对于一些理论的知识的接受度就更深入了。好了，废话不多说了，这里来说一些干货吧，基本上都是针对于Xib的。虽然博主是个IB粉呢。</p>

<h3 id="1preview">干货1 ---------- Preview工具</h3>

<p>那么什么是Preview工具呢？其实非常简单，同样一幅图可以搞定：</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/Preview.png" alt="Preview"></p>

<p>看出来了吧，他其实就是一个预览的功能，在大部分普通的适配其实都可以通过<code>Preview</code>工具来预览查看，这样就不用每次运行才能看到效果了，通过这个工具能查看在各个尺寸下我们的布局的排列情况。那么这个怎么点出来呢？</p>

<p>其实也非常简单，首先先打开<code>Assistant Editor</code><iframe src="http://www.pluto-y.com/content/images/2016/04/Assitant_Edit.png" width="192" height="60" scrolling="no" frameborder="0" class="iframe-resizer"></iframe>即中间那个，然后在点最左边那个按钮，就会出现如下的菜单:</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/Preview_enter.png" alt="Preview_enter"></p>

<p>选择最后一个即可。如果实在不懂的妹子，可以来问我，汉子就算了。哈哈。(Kidding!!!!)</p>

<h3 id="2">干货2 ---------- 查看视图层级</h3>

<p>很经常会出现一种情况，就是我们经常会有视图层级很多，即一个视图嵌套一个视图，嵌套的层很多。特别是如果出现视图重叠的情况下，想要正确的选择到正确视图这里主要讲解三个方法，用三个视图来给大家展示一下：</p>

<ul>
<li>通过文件导航器来选择</li>
</ul>

<p><img src="http://www.pluto-y.com/content/images/2016/04/select_view_by_navigator.png" alt="select_view_by_navigator"></p>

<ul>
<li>通过视图层级来选择</li>
</ul>

<p><img src="http://www.pluto-y.com/content/images/2016/04/select_view_by_view_tree.png" alt="select_view_by_view_tree"></p>

<ul>
<li>通过鼠标和左键选择(其实主要讲的是这个)</li>
</ul>

<p><img src="http://www.pluto-y.com/content/images/2016/04/select_view_by_mouse.png" alt="select_view_by_mouse"></p>

<p>前面两种都很常见，最后一种怎么出现的呢？很简单，通过按住<code>cmd + shift</code>,再点击需要选择控件的地方点击左键即可。这样就会列出点击地方的整个视图层次，最上面是在最后面的视图，最下面的是显示在最上方的视图。</p>

<h3 id="3wrap_content">干货3 ---------- wrap_content的实现</h3>

<p>本人以前也做过Android的开发，在Android的布局中，在设置高度和宽度的时候可以设置成两个值<code>fill_parent</code>(同match_parent)和<code>wrap_content</code>两个常量值。其实两个的意思分别是：子视图根据父视图缩放以及父视图根据子视图内容缩放。简单来说就是根据父视图大小改变还是根据子视图改变。
而在iOS开发中，我们经常用的应该就是根据父视图改变的，从<code>Autoresizing</code>到<code>AutoLayout</code>相对父控件的约束来说都是根据父视图改变子视图的大小。那么我就在想了，能不能再iOS中实现<code>wrap_content</code>即根据子视图的大小来调整父视图的大小呢？其实这个功能的使用场景还是挺多的。
下面我这里就举一个例子来说说，比如我们在一个希望自订一个titleView，这个titleView的左边有一个定位的图标，中间有一个<code>UILabel</code>用来显示定位的城市名，右边有一个箭头的图标。那么这个titleView的宽度应该就会根据城市的不同进行缩放了。那么我们在家的时候最外层的<code>UIView</code>即titleView就需要根据里面的内容进行所谓的<code>wrap_content</code>的处理。</p>

<p>下面我们就用一个Demo在Xib上的约束以及Preview的功能来进行讲解（假设上面所说的titleView中最高的是<code>UILabel</code>）。首先我们先来看看效果：</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/wrap_content.png" alt="wrap_content"></p>

<p>当前选中的是红色的背景的<code>UIView</code>可以从右边的视图看的出来，除了设置了相对父视图的位置信息的以外，没有对<code>UIView</code>设置关于大小的约束。那么<code>UIView</code>的带下是如何来的呢？可以看得出来，除了针对父视图的位置约束外，剩下的就是子视图相对<code>UIView</code>的约束了。那么肯定就是子视图生成的。（废话，你前面不是也说针对子视图内容来缩放父视图大小）其实实现的机理也非常简单，只要保证在距离<code>UIView</code>中的子视图在某一个方向上是打通的，例如水平方向上:</p>

<ul>
<li>|UIView.Leading - 20 - UIImageView.Leading|</li>
<li>UIImage.Width = 15</li>
<li>|UIImageView.Trailing - 20 - UILabel.Leading|</li>
<li>UILabel.Width = <code>Intrinsic size</code>.width</li>
<li>|UILabel.Trailing - 20 - UIImageView.Leading|</li>
<li>UIImageView.Widht = <code>Intrinsic size</code>.width</li>
<li>|UIImageView.Trailing - 20 - UIView.Trailing|</li>
</ul>

<p>可以看到水平上从UIView.Leading到UIView.Trailing是走的通的，而不能出现断掉的情况。而垂直方向就简单了：</p>

<ul>
<li>|UIView.Top - 20 - UILabel.Top|</li>
<li>UILabel.Height = <code>Intrinsic size</code>.height</li>
<li>|UILabel.Bottom - 20 - UIView.Bottom|</li>
</ul>

<p>同样在UIView.Top到UIView.Bottom的方向上也是完全走得通的。那么总结来说就是一句话，就是在父控件视图的任何方向上都能保证约束能从一个方向的一段走到另一端，其中不会出现断层的情况下，则能保证其能出现<code>wrap_content</code>的效果。就好像是在一个方向上搭建一条路，能从路的一段走到另一端即可。</p>

<p>博主估计这里的原因估计是因为当如果保证某一个方向上的约束链走的通的情况下能让UI布局的内部机制在计算UIView的宽度和高度的情况下都有可参考的值，主要是保证父视图能算出准确的宽和高。</p>

<p><strong>Demo见WrapContentViewController.xib!!!</strong></p>

<blockquote>
  <p>Tips: 其实下方另外两个干货都和这个干货的原理很类似，小伙伴可以思考一下。由于原理都差不多，所以接下里就稍微一带而过了，大概讲一下实现的方式和原理。</p>
</blockquote>

<h3 id="4uiscrollview">干货4 ----------  UIScrollView的适配</h3>

<p>很多小伙伴在刚开始适配<code>UIScrollView</code>的情况下都是相当蛋疼啊，因为明明都配置了<code>UIScrollView</code>相对父控件的上下左右距离都为0的。也就是<code>UIScrollView</code>的大小与父控件一样大，可是为什么偏偏在添加了子视图后，瞬间整个视图的适配就会出错，总是适配不好。记得博主刚开始适配<code>UIScrollView</code>是这个表情：</p>

<p><img src="http://www.pluto-y.com/content/images/2016/04/-TM---.png" alt="你TM在逗我"></p>

<p>那么到底是为什么呢？其实在添加子视图之后出错的原因是适配机制希望我们通过适配来确定<code>UIScrollView</code>的<code>contentSize</code>的大小。为什么这么说呢？其实如果适配成功过的小伙伴都知道，在我们适配好<code>UIScrollView</code>之后,<code>contentSize</code>是不需要设置的，那么如何设置呢？其实跟上面的<code>wrap_content</code>一样，保证在每个方向上的"路"都是通的就可以了。</p>

<p><strong>Demo见ScrollViewController.xib!!!</strong></p>

<h3 id="5cell">干货5 ----------  自动计算Cell的高度</h3>

<p>还记得之前博主要做一个类似朋友圈(QQ空间、微博等)的功能，即根据文字、图片、评论以及点赞等信息来自动调整一条说说的高度。当初那叫一个纠结啊，也只能说当初博主比较水，只能印着头皮在算所谓的<code>Frame</code>那一个一个的<code>if-else</code>语句写的博主那叫一个心惊肉跳。深怕其中一个写错了，找问题就麻烦了。结果最后还是因为各种奇葩的原因算的不够准。后来看了2015年的WWDC大会才知道了这个好东西。</p>

<p>由于<code>UITableViewCell</code>的宽度都是跟<code>UITableView</code>等宽，所以主要是针对<code>UITableViewCell</code>的高度。平常我们都是在Xib或者在<code>UITableViewDelegate</code>中实现方法来返回，可是其实在iOS 7之后就提供了实现动态高度的方法，具体原理还是跟上面一样，保证在垂直方向上保证"路"是能通的。然后针对tableView设置如下两个属性即可:</p>

<pre><code class="language-objectivec">tableview.estimatedRowHeight = 60; // 设置UITableViewCell每行大概多高  
tableview.rowHeight = UITableViewAutomaticDimension;  
</code></pre>

<pre><code class="language-swift">tableview.estimatedRowHeight = 60 // 设置UITableViewCell每行大概多高  
tableview.rowHeight = UITableViewAutomaticDimension  
</code></pre>

<p>其中第一个属性estimatedRowHeight是用来估算<code>UITableView</code>的<code>contentSize</code>用的。第二个是告诉UITableView去根据一些方法来计算Cell的高度。只要保证这两句代码和垂直"路"是通的情况下，则能实现Cell的动态高度。</p>

<h2 id="">苦逼的总结</h2>

<p>这篇文章的跨度好久，从三月初就开始写，写到四月中旬，主要是其中的三月份公司的项目太忙了，不仅博客停了，包括开源项目的更新基本也停了，不过这里也慢慢开始恢复分享一些内容给大家。当然其实本来还是比较喜欢讲解一些操作性质的内容，除了这些内容其实还有一些其他的内容，如以前的<code>Autoresizing</code>在与<code>Autolayout</code>混用的情况下会将之前的<code>Autoresizing</code>的配置都改成一个个<code>Constraints</code>--------<code>NSAutoresizingMaskLayoutConstraint</code>的形式，从而让<code>Autolayout</code>也能兼容到<code>Autoresizing</code>。</p>

<p>当然关于适配的内容也非常非常的多，我这里也一时半刻说不完，并且苹果官方的适配机制也会在不断的更新，我们需要不断的学习来做到更好的适配。当然这篇文章的中的一些内容与之前整理的<a href="http://www.pluto-y.com/tag/wwdc/">WWDC</a>的内容有些重合的地方，在<a href="http://www.pluto-y.com/tag/wwdc/">WWDC</a>的内容上也整理了很多关于IB以及AutoLayout的技巧，有兴趣的小伙伴可以返回重新看看。</p>

<p>关于Demo的代码小伙伴可以在我的<a href="https://github.com/Pluto-Y/Demos/tree/master/LayoutDemoOC">Github</a>上找到。</p>

<p>博主整理文章真的不容易，如果觉得文章内有问题的小伙伴可以留言，我会在看到的时候给你回复。博主整理文章不容易，且读且珍惜。</p>

<p>哦，忘记了，还有关于适配的最后一个部分<code>Size Class</code>，小伙伴们记得回来看啊，要有始有终啊。</p>

<h2 id="">广告</h2>

<p>接下来是广告时间，本人开了一个订阅号，有兴趣的小伙伴关注一下：iOS周记。</p>

<p>可以查找关键字：Weekly_iOS进行关注，也可以扫一扫下方的二维码
<img src="http://www.pluto-y.com/content/images/2016/02/qrcode_for_gh_da9e1ad55fff_430.jpg" alt="订阅号"></p>]]></content:encoded></item><item><title><![CDATA[iOS适配之旅——穿越时空的爱恋(手动适配和Autoresizing)]]></title><description><![CDATA[<p>其实很多人看到这个标题的时候会觉得有点陈词滥调了，毕竟关于适配的文章在网络上真的是多的多如牛毛,你这里有什么好看。其实我也不知道为什么要写这个文章。</p>

<p><img src="http://www.pluto-y.com/content/images/2016/03/-TM---.png" alt="你TM在逗我.png"></p>

<h2 id="">写在最前面</h2>

<p>前段时间公司在招iOS的人员，我去稍微搀和了一下，也面试了一些iOS的技术人员，有那么一句感慨就是：“紫薇，别离开我”，不对，是好像很多人在iOS上做了好多年，可是技术方面还是停留在几年前。别的都好说，关键对于适配方面好多人都是停留在某一个阶段或者某两个阶段上，更有甚至的就是完全不知道新的东西或者是完全是在根据屏幕大小进行按照比例缩放（全代码形式）。所以我就想说是不是要整理一下，关于iOS适配的发展稍微进行系统的讲解一下。至少大家对各个方面都稍微有点理解，当大家遇到旧的项目或者是遇到某一个适配情形下的代码或者界面可以有稍微理解。并且稍微整理一下一些在iOS 9里面的新特性，方面大家从整体方面来看适配的大体框架以及一些细节的新特征。</p>

<p>当然首先先说一下目前来说主流方面来说主要是经历一下几个阶段:</p>

<ul>
<li>手动适配</li>
<li>Autoresizing</li>
<li>AutoLayout</li>
<li>Size Class</li>
</ul>

<p>当然，在今年要出来的WWDC大会上希望能出现更好的特性来帮助我们开发者缩短或者是简化我们的适配流程。当然这篇稍微讲解一下手动适配和<code>Autoresizing</code>,当然这篇文章的也是博主自己的经验所述，如果有什么有问题的地方，麻烦大家指出，本人必定虚心改正，痛改前非。</p>

<h2 id="">穿越时空的爱恋(手动适配)</h2>

<p>看到这个题目大家有没有想到张庭当初演的那部电视剧，大家有没有想到当初那部剧的男主是谁，没错就是徐峥！！！！当初怎么也算的上是小鲜肉吧，我完全没有反应过来那个人是徐峥。那现在让我们将时间穿越到明朝，不对不对，是2011年。</p>]]></description><link>http://www.pluto-y.com/ios-layout-guides-autoresizing/</link><guid isPermaLink="false">81fea03f-204b-40b7-b1df-9c79fab9d503</guid><category><![CDATA[Layout]]></category><category><![CDATA[iOS]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Tue, 08 Mar 2016 16:03:25 GMT</pubDate><content:encoded><![CDATA[<p>其实很多人看到这个标题的时候会觉得有点陈词滥调了，毕竟关于适配的文章在网络上真的是多的多如牛毛,你这里有什么好看。其实我也不知道为什么要写这个文章。</p>

<p><img src="http://www.pluto-y.com/content/images/2016/03/-TM---.png" alt="你TM在逗我.png"></p>

<h2 id="">写在最前面</h2>

<p>前段时间公司在招iOS的人员，我去稍微搀和了一下，也面试了一些iOS的技术人员，有那么一句感慨就是：“紫薇，别离开我”，不对，是好像很多人在iOS上做了好多年，可是技术方面还是停留在几年前。别的都好说，关键对于适配方面好多人都是停留在某一个阶段或者某两个阶段上，更有甚至的就是完全不知道新的东西或者是完全是在根据屏幕大小进行按照比例缩放（全代码形式）。所以我就想说是不是要整理一下，关于iOS适配的发展稍微进行系统的讲解一下。至少大家对各个方面都稍微有点理解，当大家遇到旧的项目或者是遇到某一个适配情形下的代码或者界面可以有稍微理解。并且稍微整理一下一些在iOS 9里面的新特性，方面大家从整体方面来看适配的大体框架以及一些细节的新特征。</p>

<p>当然首先先说一下目前来说主流方面来说主要是经历一下几个阶段:</p>

<ul>
<li>手动适配</li>
<li>Autoresizing</li>
<li>AutoLayout</li>
<li>Size Class</li>
</ul>

<p>当然，在今年要出来的WWDC大会上希望能出现更好的特性来帮助我们开发者缩短或者是简化我们的适配流程。当然这篇稍微讲解一下手动适配和<code>Autoresizing</code>,当然这篇文章的也是博主自己的经验所述，如果有什么有问题的地方，麻烦大家指出，本人必定虚心改正，痛改前非。</p>

<h2 id="">穿越时空的爱恋(手动适配)</h2>

<p>看到这个题目大家有没有想到张庭当初演的那部电视剧，大家有没有想到当初那部剧的男主是谁，没错就是徐峥！！！！当初怎么也算的上是小鲜肉吧，我完全没有反应过来那个人是徐峥。那现在让我们将时间穿越到明朝，不对不对，是2011年。</p>

<p>背景：2011年苹果iOS 5发布会前
在没有出现Autoresizing之前，如果我们想要适配(在此之前可能没有适配这个词语，可能的情况应该是父视图大小发生变化的时候)的话只能通过<code>UIView</code>里的<code>layoutSubviews</code>来进行调整。因为当当父视图的大小发生变化的时候会调用<code>layoutSubviews</code>这个方法，只需在子类中重写这个方法即可。当然这是一个非常行之有效的方法，你可以在<code>layoutSubviews</code>中做所有你想做的事情。</p>

<p>终于到了11年的iOS 5的发布之后了，那时候终于出现了<code>Autoresizing</code>这个概念。关于这个适配刚出来的时候是什么情形博主完全不得而知啊。虽然当时没有所谓的多设备，那时候有的设备也相对比较单一，但是出现了<code>Autoresizing</code>相对那时候来说完全能满足当时的需求。</p>

<h2 id="autoresizingios5">Autoresizing(iOS 5)</h2>

<p>其实这个概念估计现在很多小伙伴还是能看到的，特别是如果那些我们经常用到一些其他人自定义的视图来说的话。我这里提一点小伙伴肯定能顿时领悟:<code>autoresizingMask</code>。系不系，系不系，系不系经常看到。当然这个在IB上也是有体现的，可以看下图：
<img src="http://www.pluto-y.com/content/images/2016/03/autoresizing_ib.png" alt="autoresizing_ib.png"></p>

<p>好怀念这个图啊，记得当初刚入行的时候虽然6也出来了，可是当初年少不懂事，太傻太天真。好吧，博主承认是被人忽悠进iOS界的，要想当年我还是做Java EE的，当时出来的时候......(喂，扯远了！)。咳咳咳，那个就是当初进来没人带，也不主动学习，所以不懂这方面的知识。所以当初只对这个界面比较熟悉一点，至于代码怎么实现完全不懂。</p>

<p>接下来我讲对<code>Autoresizing</code>在IB、OC以及Swift中的常见形态进行稍微讲解一下，并且写成demo提供大家参考一下。</p>

<h3 id="">首先</h3>

<p>先说说<code>Autoresizing</code>是个什么东西吧，其实它就有点像是弹簧和杠杆一样。为什么这么说呢，其实<code>Autoresizing</code>主要的功能就是当<code>superview</code>变化的时候，子控件应该跟着怎样变化。那么问题来了，根据上图来看的话，可变化的点就是上下左右以及高宽可变，那么如果当<code>supview</code>变化，上述的属性跟着变化的情形下就有点像是弹簧，跟着一样拉长，而如果<code>supverview</code>变化，属性不变的话，就像是杠杆，不会跟着变化。当然，这里所说的都是指的是等比例变化。</p>

<p>大家可以看到上面IB中的<code>Autoresizing</code>好像是可配置的是上下左右和宽和高,但是这里的可配置想跟代码方面有点相反的概念。接下来我们用Demo来看下IB和代码上是如何进行处理<code>Autoresizing</code>。</p>

<h3 id="ibautoresizing">IB上的Autoresizing</h3>

<p>如果在新版本的Xcode上默认是没有打开的<code>Autoresizing</code>的，以为默认情况下是打开<code>Auto Layout</code>的模式的。那怎么打开呢，很简单的，见下图：
<img src="http://www.pluto-y.com/content/images/2016/03/Autoresizing-2.gif" alt="Autoresizing.gif"></p>

<p>可以看得出，默认情况下是支持<code>Auto Layout</code>和<code>Size Classes</code>的。</p>

<p>其次这里还有一个小Tip，就是为了方便演示，这里利用了Xcode自带的<code>Preview</code>的功能，具体怎么打开呢，其实大家应该都会:</p>

<p>首先先打开<code>显示两个文件编辑框</code>就是在右上角<iframe src="http://www.pluto-y.com/content/images/2016/03/btn.png" width="62" height="52" scrolling="no" frameborder="0" class="iframe-resizer"></iframe>这个按钮,然后现在<iframe src="http://www.pluto-y.com/content/images/2016/03/menu.png" width="436" height="448" scrolling="no" frameborder="0" class="iframe-resizer"></iframe>选择最后一个即可直接看到一些布局的效果。</p>

<p>不废话，先来看看一个Gif，看完大家就明白IB上的<code>Autoresizing</code>是怎么回事了：</p>

<blockquote>
  <p>Tip: 大家在看主要高度宽度以及距离对父视图边距之间的变化，以及这里利用了3.5和5.5寸屏来模拟父视图不同情况下Autoresizing是怎么起作用的。</p>
</blockquote>

<p><img src="http://www.pluto-y.com/content/images/2016/03/Autoresizing_xib-4.gif" alt="Autoresizing_xib"></p>

<p>看完之后，大家应该知道如何设置了，那么在Xib下在集中情况下都有特性：</p>

<ul>
<li>1.在默认情况下是根据上下左右边距都跟着父视图进行变化，而高度和宽度不跟随视图进行变化</li>
<li>2.在选中上(下左右)的情况下，是保持选择方向距离固定不变，即如果我选择了左边的情况，就是代表不管父视图怎么变，我左边的距离都保持这么远</li>
<li>3.在选中高度(或宽度)情况下，高度(或宽度)都会跟着父视图变化而进行等比例变化</li>
<li>4.在同时选中了左右（或者上下）的情况，只选中左边(或者上边)的情况，即左边(或者上边)距离父视图保持不变即出现<code>情况2</code>只选择左边或者上边的情况</li>
<li>5.在同一个方向都选中的情况下，例如在同时选中左右以及宽度的情况下，这三个都会跟随父视图进行等比例的变化 </li>
</ul>

<p>大家就讲这么多的情况，至于这个大家完全没必要背，具体还是自己多试试就可以了，直到找到适合自己的组合。再说了，现在毕竟针对<code>Autoresizing</code>的Xib就更少了，反而代码见到的机会更多。那么下面来说说在OC以及Swift情况下如何进行<code>Autoresizing</code>的设置吧。</p>

<h3 id="autoresizing">代码里的Autoresizing</h3>

<p>前面我们也讲过了，<code>Autoresizing</code>就像是一个弹簧，大家都是弹簧也是在压缩到一定程度的情况下也是不可压缩的，当然，对于<code>Autoresizing</code>来说也是一样的，在一定程度下弹簧也是不生效的，例如在只设置左边根据父类大小进行变化的情况，当父类变小到一定程度，这个也是不会生效的。大家可以自行试一下。
好了，进入正题，其实在代码里面设置<code>Autoresizing</code>是非常简单的事情，而且相对可控性来说，我还是喜欢用代码的形式来进行控制。并且我们常见的自定义控件来说的话，其实大部分都是使用代码的形式来进行控制<code>Autoresizing</code>的，其实要进行设置<code>Autoresizing</code>是非常简单的，只需要通过<code>UIView</code>的<code>autoresizingMask</code>即可，而代码中的<code>autoresizingMask</code>属性设置都是确定的，如果你需要什么跟随父控件属性一切变化的话，只要设置对应的<code>autoresizingMask</code>即可。接下来主要给大家看下代码中是如何书写的。</p>

<p>首先是Objective-C:</p>

<pre><code class="language-objectivec">// 如果只有一个的情况下
contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;  
// 如果有多个的情况下
contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;  
</code></pre>

<p>接下来是Swift</p>

<pre><code class="language-swift">// 如果只有一个的情况下
view.autoresizingMask = .FlexibleBottomMargin  
// 或者
view.autoresizingMask = [.FlexibleHeight]  
// 如果有多个的情况下
view.autoresizingMask = [.FlexibleBottomMargin , .FlexibleHeight]  
</code></pre>

<p>最后大家来看看Demo的gif图，来感受一些<code>Autoresizing</code>的魔力吧。
<img src="http://www.pluto-y.com/content/images/2016/03/Autoresizing_code.gif" alt="autoresizing_code"></p>

<h2 id="">总结</h2>

<p>其实<code>Autoresizing</code>在多个配置组合在一起的情况下还是有点复杂的，不过大家在看比人代码或者是自己写控件的时候多尝试，特别是如果是IB粉的小伙伴记得多尝试一下。
至于其他的想要代码的小伙伴可以通过我的<a href="https://github.com/Pluto-Y/Demos/tree/master/LayoutDemoOC">Github</a>上下载到代码。</p>

<p>接下来是广告时间，本人开了一个订阅号，有兴趣的小伙伴关注一下：iOS周记。</p>

<p>可以查找关键字：Weekly_iOS进行关注，也可以扫一扫下方的二维码
<img src="http://www.pluto-y.com/content/images/2016/02/qrcode_for_gh_da9e1ad55fff_430.jpg" alt="订阅号"></p>]]></content:encoded></item><item><title><![CDATA[Cocoapods系列教程(三)——私有库管理和模块化管理]]></title><description><![CDATA[<h2 id="">写在前面</h2>

<p>年前到现在大部分时间都在整理和抽象之前项目的代码，那酸爽，真是够够的。主要是公司产品是做定制版的本需求，而前期对定制的内容需求太不明确了，导致领导先说前期就用不同代码管理不同的定制版。最后我们这里中英文版就有6套代码，导致管理起来特别不方便。而之前在写代码的时候完整体的框架是写好的，可是在细节上的封装来说就差太远了。导致整个代码的耦合度太高了，这段时间抽象起来相当痛苦。所以现在就开始对项目进行模块化管理，保证各个模块之前可以重用和替换，并且之后根据客户需求只加载用户需求的模块。</p>

<p>最后我决定采用<code>Cocoapods</code>对各个模块进行管理，采用<code>公有库</code>和<code>私有库</code>共存的状态。然后在添加配置文件以及一些<a href="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html">Runtime的机制</a>进行管理。</p>

<p>而对于一个公司的核心代码来说，当然不可能采用公开的形似来进行管理对已的框架。所以在<code>Cocoapods</code>中，还有另一种方式提供给公司内部管理进行管理代码，那就是<code>私有库</code>(<a href="https://guides.cocoapods.org/making/private-cocoapods.html">Private Pods</a>)。</p>

<h2 id="">私有库</h2>

<p>好了，废话不多说了，我们先开始说说如何创建<code>私有库</code>吧。其实创建<code>私有库</code>的核心过程还是跟<code>公有库</code>是差不多的。不管是<code>私有库</code></p>]]></description><link>http://www.pluto-y.com/cocoapod-private-pods-and-module-manager/</link><guid isPermaLink="false">16009cfd-b759-4e39-9004-848251532c23</guid><category><![CDATA[Cocoapods]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Fri, 26 Feb 2016 07:25:59 GMT</pubDate><content:encoded><![CDATA[<h2 id="">写在前面</h2>

<p>年前到现在大部分时间都在整理和抽象之前项目的代码，那酸爽，真是够够的。主要是公司产品是做定制版的本需求，而前期对定制的内容需求太不明确了，导致领导先说前期就用不同代码管理不同的定制版。最后我们这里中英文版就有6套代码，导致管理起来特别不方便。而之前在写代码的时候完整体的框架是写好的，可是在细节上的封装来说就差太远了。导致整个代码的耦合度太高了，这段时间抽象起来相当痛苦。所以现在就开始对项目进行模块化管理，保证各个模块之前可以重用和替换，并且之后根据客户需求只加载用户需求的模块。</p>

<p>最后我决定采用<code>Cocoapods</code>对各个模块进行管理，采用<code>公有库</code>和<code>私有库</code>共存的状态。然后在添加配置文件以及一些<a href="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html">Runtime的机制</a>进行管理。</p>

<p>而对于一个公司的核心代码来说，当然不可能采用公开的形似来进行管理对已的框架。所以在<code>Cocoapods</code>中，还有另一种方式提供给公司内部管理进行管理代码，那就是<code>私有库</code>(<a href="https://guides.cocoapods.org/making/private-cocoapods.html">Private Pods</a>)。</p>

<h2 id="">私有库</h2>

<p>好了，废话不多说了，我们先开始说说如何创建<code>私有库</code>吧。其实创建<code>私有库</code>的核心过程还是跟<code>公有库</code>是差不多的。不管是<code>私有库</code>还是<code>公有库</code>，关注点都在于<code>Podspec</code>文件的书写。但是在上篇文章中讲过了大体<code>Podspec</code>文件以及创建公有库的流程了，这里我就对那些部分不进行详细讲解了。这里针对一些不同的地方以及需要注意的地方进行讲解一下。</p>

<p>首先在创建<code>私有库</code>之前，我们是不是该先创建一个<code>私有库</code>该往哪个仓库提交的仓库(<code>Spec</code>)。  所以当然当务之急是先创建一个<code>私有仓库</code>啦。而这个仓库对于公司来说的话，最好是搭建在内网里面，用<a href="https://about.gitlab.com/gitlab-com/">Gitlab</a>之类的git仓库管理工具即可。</p>

<blockquote>
  <p>这里再带一句，其实我们<a href="http://www.pluto-y.com/cocoapod-private-pods-and-module-manager/">上章</a>所讲到<code>pod trunk push 项目名.podspec</code>这条命令，其实是默认我们的<code>Podspec</code>文件提交到<a href="https://github.com/CocoaPods/Specs">Cocoapod的仓库(Specs)</a>,然后我们之后的<code>pod install</code>或者<code>pod update</code>都是从这个仓库中提取<code>Podspec</code>文件，然后根据文件里面的信息去取对应的源代码。大家可以上去找找自己的开源的<code>Podespec</code>文件转换成json的文件。</p>
</blockquote>

<p>好了，废话不多说（不过好像也说了非常多的废话了）。先建仓库吧：</p>

<pre><code class="language-shell">pod repo add '仓库名' '仓库地址'  
</code></pre>

<p>这里的仓库地址最好是私有地址，让所有可以使用这个仓库的成员都可以访问的地址。当然通过上面这句话，就能非常简单的创建好私有库的地址了。当然，小伙伴们可以通过<code>cd ~/.cocoapods/repos</code>这个目录里面检查是否创建好具体的私有库。</p>

<p>当然之后就是写代码->写<code>Podspec</code>文件了->检查项目和<code>Podspec</code>文件->打tag，这里就不进行累述了。到了下一步按照原定计划来说的话，应该是要提交项目到<code>Cocoapods</code>上对了吧，可是我们这里讲的是私有库，当然不可能提交到<code>Cocoapods</code>上，而应该提交到自己的仓库里面。
将之前的<code>pod trunk push 项目名.podspec</code>修改为<code>pod repo push '私有仓库名' 项目名.podspec</code>即可。之后大家就可以前往之前创建的私有库的地址上查看是否上传版本成功。</p>

<p>好了，到此为止简单的创建私有库的流程就说完了。
不过还有一些小的方面跟大家顺带说一下，</p>

<ul>
<li>比如如何删除私有仓库:<code>pod repo remove [name]</code>。</li>
<li>在普通项目中如何使用私有仓库，可以在Podfile里面的开头声明所有包含的仓库名,即利用<a href="http://www.pluto-y.com/cocoapods-getting-stared/">第一章-入门</a>说到的<code>source</code>参数</li>
</ul>

<h2 id="">开发模式</h2>

<p>当然，在使用私有库的过程中，很大一部分时间私有库都是处于开发阶段，而我们总不能一直提交tag的方式进行<code>pod update</code>更新吧。因此<code>Cocoapods</code>就提供了一个开发模式,其实操作起来也是非常简单的事情，就是将所谓的引用路径修改成本地路径即可。就是讲<code>Podfile</code>中的<code>pod '库名', :path =&gt; '本地路径'</code>即可。这样在通常的修改代码中是不需要执行<code>pod update</code>的，但是对于如果修改了目录结构（添加、删除或者移动文件文件）或者是修改了<code>Podspec</code>文件的配置的话，最好是运行一下<code>pod update</code>的命令。普通修改代码的情况下就不需要运行<code>pod update</code>命令和打tag了。</p>

<pre><code class="language-shell">pod 'iOS-Echarts', :path =&gt; '../iOS-Echarts'  
</code></pre>

<p>上图就是<a href="https://github.com/Pluto-Y/iOS-Echarts">iOS-Echarts</a>开发模式下的一个简单的演示-<code>Podfile</code>里面的代码。</p>

<h2 id="">使用私有库</h2>

<p>使用私有库的方式我在这里主要列举了两种情况，下面针对这两中情况的具体注意项我这里稍微说明一下。
第一种,正常使用私有库的情况，即在Podfile中引用私有库
这种方式最简单，就是通过在<code>Podfile</code>开头列举说所有私有库的位置以及<code>Cocoapods</code>位置即可。</p>

<pre><code class="language-shell"># Podfile文件
# 公有仓库
source 'https://github.com/CocoaPods/Specs.git'  
# 私有仓库
source 'https://192.168.0.100/TestPrivate/Specs.git'  
</code></pre>

<p>第二种，在私有库中引用私有库，即在<code>Podspec</code>文件中依赖(dependency)<code>私有库</code>
这种情况就比较麻烦一点，因为毕竟<code>Podspec</code>文件中并没有指明私有仓库地址的地方。那么肯定就不在<code>Podspec</code>文件里面指明私有仓库的地方。而是在验证和上传私有库的时候进行指明。即在下面这两条命令中进行指明：<code>pod lib lint 项目名.podspec --sources=https://github.com/CocoaPods/Specs.git,192.168.0.100:Plutoy/Specs.git</code>以及<code>pod repo push --source=https://github.com/CocoaPods/Specs.git,192.168.0.100:Plutoy/Specs.git</code>,要不然你在检验项目以及提交项目过程中就会出现Error的情况。</p>

<p>但是这两种情况还是有点不同的，第一种情况是可以采用开发者模式，而第二种情况不能采用开发者模式，只能通过打tag之后才能进行使用，所以在使用第二种情况下最好是测试好之后打完tag再进行引用。</p>

<h2 id="cocoapods">Cocoapods用在模块化管理</h2>

<p>关于模块化管理，其实对于一个人力资源特别紧缺的公司来说还是挺有必要的。特别是遇到我这种需要定制版本的管理的公司，如果对于模块管理比较妥善的情况下，其实会为公司的人力资源和时间资源节省非常多的时间。本人确实有亲身经历，在经历了6个版本，可是核心功能相同的情况下，公司之前采用的是6套代码进行管理。从而产生了很多不必要的重复工作和人力资源的浪费，因此特别有必要对项目的各个模块化进行拆分和管理。
其实也有小伙伴跟我提过说用framework或者.a等框架形式不就好了么？可是对于framework要进行版本管理以及多地方引用的管理情况下，很多情况下都会忘记了当前大的包对应的是仓库管理里面的哪个版本，因为有可能我们经常打tag的时候，小修改还是会用同一个版本号，所以会出现很多误差性的情况。而通过<code>Cocoapods</code>进行管理的话，就可以追踪到仓库管理里面的具体提交号、版本号、branches等情况。</p>

<p>对于我们公司来说，由于有定制版的存在，所以我们基本上是封了很多小的私有库，如蓝牙模块，访问服务器模块，一些视图、第三登陆分享等。当然也有对于大的核心版本封装，如果整个App的核心功能的分装，当然通过模块也有可能分为秤的模块、手环的模块、血糖计血压仪的模块等等。当然这些大的模块中也都会引用小的模块然后最后根据<code>Cocoapods</code>配置以及plist文件进行扫描之后产生需要对应模块的版本，最后在通过代码扫描机制进行初始化项目。当然项目中还用到一些“黑魔法”和<code>Runtime</code>的一些知识，有兴趣的小伙伴可以去看看资料，或者联系我。</p>

<p>不过在进行模块化管理的过程中需要注意一些点：</p>

<ul>
<li>模块中不能出现循环依赖，即A依赖B，B依赖C，C依赖A的情况</li>
<li>如果一个子模块引用，需要填写完整的模块名，如在<code>Core</code>模块下面有<code>Controller</code>模块下面有个<code>Setting</code>模块，并且整个库的名字为TestProj的话,则依赖的名称需要这样写<code>s.dependency 'TestProj/Core/Controller/Setting'</code>的形式</li>
<li>如果出现模块特别多的情况下，在验证过程中，竟然采用<code>--subspec=子模块名</code>来进行一个模块一个模块验证，特别是对于如果只改动了一个模块的情况下，这里所说的字模块名也和上面一点异样，要填写完整的模块名</li>
<li>在写私有库的过程中，竟然不用prefix header的形式，因为在分子模块的过程中很容易出现忘记引用header而出现的Error</li>
<li>最后一点，多google，少百度，太多资料只能通过google查到，而在百度里面完全查不到</li>
</ul>

<h2 id="">写在最后</h2>

<p>到此为止，关于<code>Cocoapods</code>的使用，从使用者到最后的自己开发自己的库的教程就到此为止了。里面包含了很多内容，有些内容可能写的不够清楚，不过大体上这个也是说说我使用<code>Cocoapods</code>的教程，如果中间出现什么疑问或者错误，欢迎小伙伴留言或者联系我。
当然除了<code>Cocoapods</code>外，还有另外两个<a href="https://github.com/Carthage/Carthage">Carthage</a>以及<a href="https://github.com/apple/swift-package-manager">Swift Package Manager</a>依赖管理工具，有兴趣的小伙伴可以研究一下。这两个管理工具都是针对动态库，所以一般都是在iOS8以上，对于要兼容iOS7以上的小伙伴(包括我)来说是一个不利的地方，但是<code>Carthage</code>的使用比<code>Cocoapods</code>方便。你只需要保证你的代码能编译通过即可。至于<code>Swif Package Manage</code>本人没用过，不敢多说什么。不过竟然学了这么多的内容，希望小伙伴们还是可以学以致用，即使不能用在公司的项目，也可以考虑用在为开源的事业尽一份力的程度上，至少让中国的开发者能在开源的方面不落后其他国家的小伙伴。</p>

<p>打一个广告，本人开了一个订阅号，有兴趣的小伙伴关注一下：iOS周记。</p>

<p>可以查找关键字：Weekly_iOS进行关注，也可以扫一扫下方的二维码
<img src="http://www.pluto-y.com/content/images/2016/02/qrcode_for_gh_da9e1ad55fff_430.jpg" alt="订阅号"></p>]]></content:encoded></item><item><title><![CDATA[Cocoapods系列教程(二)——开源主义接班人]]></title><description><![CDATA[<h2 id="">引言</h2>

<p>在写该博客的时候，博主刚看到一个问题：“那些头衔只是看起来很厉害，实际不难获得？”。然后有个神回复写到：“共产主义接班人”。然后脑袋里面就响起那首斗志昂扬的歌：“我们都是共产主义接班人.....!@#$%^&amp;*()”。借此就来引入今天的博文，作为现在开源横行的年代。作为一位刚毕业的iOS小学生，开源主义接班人，我们怎么能不撸起代码来呢？特别是针对那些自己做的很溜的控件啊，插件啊之类的。作为开发者总有一中冲动想向全世界呐喊这个是我写的。当然，如果作为一个控件，框架或者是第三方的开源，想要给别人用，却又能做到版本管理，当然就少不了我们上节说到的<code>Cocoapods</code>啦。
“还有Carthage！”角落里默默传来了一个声音。是谁！是谁在拆我的台。好吧，开玩笑。
上节已经讲了<code>Cocoapods</code>作为使用者的基本用法，如果小伙伴没有看过的话，博主强烈建议大家去<a href="http://www.pluto-y.com/cocoapods-getting-stared/">瞅一瞅</a>。这节我们针对贡献者再进行细致的讲一讲。</p>

<h2 id="">“开源”代码</h2>

<p>说到“开源”代码很多人脑袋里面第一个想到的就是完全开源给全世界的人。但是这篇文章需要产生另一个分解，针对开源的对象进行分解。对于公开给所有的人的开源我们称之为<code>公有库</code>。而除了我们平常所说的开源外，还有一种形式是公司内部的开源，</p>]]></description><link>http://www.pluto-y.com/cocoapods-contribute-for-open-source/</link><guid isPermaLink="false">6a1accb8-ad19-4d27-902f-af160c5c362c</guid><category><![CDATA[Cocoapods]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Tue, 23 Feb 2016 16:51:08 GMT</pubDate><content:encoded><![CDATA[<h2 id="">引言</h2>

<p>在写该博客的时候，博主刚看到一个问题：“那些头衔只是看起来很厉害，实际不难获得？”。然后有个神回复写到：“共产主义接班人”。然后脑袋里面就响起那首斗志昂扬的歌：“我们都是共产主义接班人.....!@#$%^&amp;*()”。借此就来引入今天的博文，作为现在开源横行的年代。作为一位刚毕业的iOS小学生，开源主义接班人，我们怎么能不撸起代码来呢？特别是针对那些自己做的很溜的控件啊，插件啊之类的。作为开发者总有一中冲动想向全世界呐喊这个是我写的。当然，如果作为一个控件，框架或者是第三方的开源，想要给别人用，却又能做到版本管理，当然就少不了我们上节说到的<code>Cocoapods</code>啦。
“还有Carthage！”角落里默默传来了一个声音。是谁！是谁在拆我的台。好吧，开玩笑。
上节已经讲了<code>Cocoapods</code>作为使用者的基本用法，如果小伙伴没有看过的话，博主强烈建议大家去<a href="http://www.pluto-y.com/cocoapods-getting-stared/">瞅一瞅</a>。这节我们针对贡献者再进行细致的讲一讲。</p>

<h2 id="">“开源”代码</h2>

<p>说到“开源”代码很多人脑袋里面第一个想到的就是完全开源给全世界的人。但是这篇文章需要产生另一个分解，针对开源的对象进行分解。对于公开给所有的人的开源我们称之为<code>公有库</code>。而除了我们平常所说的开源外，还有一种形式是公司内部的开源，即对部分开源，通常情况下我们针对这个库有称之为<code>私有库</code>。但是怎么说也是对少部分人开源了对吧。而本章中就针对这两部分人群进行讲解，分别<code>公有库</code>和<code>私有库</code>在<code>Cocoapods</code>上的使用进行讲解一下。
当然，对于这两种情况下的<code>库</code>来说还是有一些共同的部分，所以我们先对<code>Cocoapods</code>中的一些使用和配置进行讲解，在讲解之后我们再针对不同的场景进行讲解不同的一些命令和流程。</p>

<h2 id="podsepc">Podsepc文件</h2>

<p>在所有支持<code>Cocoapods</code>导入的库的开源目录(如Github)下，我们都能看到一个*.podspec文件。当然我们不管是做<code>公有库</code>或者是<code>私有库</code>都是必须配置这个文件的。这个文件是告诉<code>Cocoapods</code>你这个库的一些基本信息，包括你的版本号、获取的地址、那些文件是希望被包含进来的等一些信息。细节方面这边就不进行累述了，因为我不是一个特别喜欢讲理论的美男子。
废话不多说，上Demo。(我又来做广告了)</p>

<pre><code class="language-shell">Pod::Spec.new do |s|  
  s.name         = "iOS-Echarts"
  s.version      = "1.1.4"
  s.summary      = "A custom component for the ecomfe's echarts."
  s.homepage     = "https://github.com/Pluto-Y/iOS-Echarts"
  s.license      = { :type =&gt; "MIT", :file =&gt; 'LICENSE.md' }
  s.author       = { "PlutoY" =&gt; "kuaileainits@163.com" }
  s.platform     = :ios, "7.0"
  s.source       = { :git =&gt; "https://github.com/Pluto-Y/iOS-Echarts.git", :tag =&gt; s.version}
  s.frameworks   = 'UIKit'
  s.source_files = "iOS-Echarts/**/*.{h,m}"
  s.resource     = "iOS-Echarts/Resources/**"
  s.requires_arc = true
end  
</code></pre>

<p>对于一个普通的Podspec文件来说一般都是有这些内容就可以了。而这个文件是在你新建或者需要新提交一个版本的时候回进行改动的文件。里面很简单的内容就不说了，有一点开发基础和英文底子的都看得出来，无非就是你要提交到<code>Cocoapods</code>上的名字、版本号，简介、主页、License、作者信息、最低平台信息、从哪个Git上下载、需要引入的framework、那些文件需要被引入，那些文件是资源文件以及是否需要ARC的模式。</p>

<p>大体就是包含这些信息。不过针对其中<code>source_file</code>、<code>resource</code>以及<code>license</code>稍微说两句。对于<code>source_file</code>主要是指你那些目录是可以被包含进来的，而<code>resource</code>是指哪些文件是作为资源文件进行引入的。而对于这些文件的描述都是可以用一些匹配公式的。可匹配的符号有<code>*</code>, <code>**</code>, <code>?</code>, <code>[set]</code>, <code>{p,q}</code>, <code>\</code>.常用匹配符的人应该能看得懂这些匹配符的意思。</p>

<blockquote>
  <p>文件匹配的属性有:source<em>files, public</em>header<em>files,private</em>header<em>files,vendored</em>frameworks,vendored<em>libraries,resource</em>bundles,resources,exclude<em>files,preserve</em>paths,module_map.</p>
</blockquote>

<p>而针对license来说的话，一定要填写正确的，要不然在之后的验证过程中(validate)，会一直出现warming的信息，让人看着很不舒服。</p>

<p>在<code>Podspec</code>文件中，很经常看到<code>do |名字|</code>的形式，这个有点类似于在对应end之前声明了一个local的参数一样。除了上问所示的<code>Pod::Spec.new do |s|</code>的情况外，还有可能会出现在<code>s.subspec 'SubModule' do |sm|</code>的形式，同样sm也是可以自己修改成自己想要的名字。</p>

<h2 id="podspec">Podspec文件进阶</h2>

<p>进阶其实也是非常简单的内容，主要是针对如果处理一个<code>公有库</code>或者<code>私有库</code>要进行模块的话，也就是针对上文出现的<code>Subspec</code>说一些注意点。
照惯例，还是先上Demo：</p>

<pre><code class="language-shell">s.subspec 'Security' do |ss|  
  ss.source_files = 'AFNetworking/AFSecurityPolicy.{h,m}'
  ss.public_header_files = 'AFNetworking/AFSecurityPolicy.h'
  ss.frameworks = 'Security'
end  
</code></pre>

<p>可以看出，需要声明一个子模块的话只需通过<code>父节点名.subspec '子模块名' do |子节点名|</code>来进行声明一个子模块，而子模块里面一般都只需要配置一些文件匹配的信息、资源文件的信息以及一些依赖的信息即可。当然需要声明的都只是跟这个子模块有关的。大体的配置项跟上一小节中的配置项差不多，只是这部分的配置项的作用效果只是针对这个子模块而已。</p>

<p>除了上述所说的常用的配置项之外，还有其他的配置项大家可以通过<a href="https://guides.cocoapods.org/syntax/podspec.html">Cocoapods的Podspec语法</a>进行查询。这里就不对每个参数进行讲解了。其实对于一个<code>公有库</code>或者<code>私有库</code>来说，<code>Podspec</code>文件是非常重要的，因为这里面定义了</p>

<h2 id="">开源的接班人</h2>

<p>上面简单的讲述了<code>Podspec</code>文件，下面开始我们的开源之旅吧。
在开始提交项目到<code>Cocoapods</code>之前，小伙伴们有没有想过在提交源代码前怎么标示你的源代码是属于你的，我们总要一个类似账号的东东吧。当然需要，那么我们先来搞一个账号吧:</p>

<pre><code class="language-shell">pod trunk register abc@163.com 'Pluto Y' --description='My own computer'  
</code></pre>

<p>只要运行上面命令则会像<code>Cocoapods</code>方面注册一个账号。不过他的账号没有类似登陆的机制，所以在你切换设备后，需要再次使用这个命令进行“登陆”操作。其语法为<code>pod trunk register 邮箱 '昵称' --description='设备信息'</code>，其中的昵称和<code>--description</code>是可有可无的。对于昵称来说，第一次"注册"时最好填写一下。第二次"登陆"时，则可不比填写，而对于<code>--description</code>来说的话，其实就是为了让你区分不同设备"登陆"，因为<code>Cocoapods</code>是以<code>Session</code>的形式将用户信息缓存在机子中，而可能出现多台设备"登陆"的情况，所以在<code>register</code>的时候带上<code>--description</code>方面之后查看哪些设备“登陆”过.
只要只要通过<code>pod trunk me</code>来查看是否"注册"成功。
如果成功的话,并且多台设备登陆的情况，具体如下图:
<img src="http://www.pluto-y.com/content/images/2016/02/cocoapods-pod-me.png" alt="cocoapods-pod-me.png"></p>

<p>好了，有了账号之后，当然是着手开始做了。既然要开源，那么当然首先就是写代码，你不会是希望我帮你写代码吧，好吧如果是美铝我会考虑考虑的(I'm kidding)。在写好代码后，在你项目的根目录下运行:</p>

<pre><code class="language-shell">pod spec create 'SlideMenuControllerOC'  
</code></pre>

<p>当然<code>SlideMenuControllerOC</code>就是你开源出去让别人搜到的项目名啦，你直接替换成你的项目名即可。运行完这个目录后，<code>Cocoapods</code>会自动帮你产生一个包含一部分基础内容的<code>Podspec</code>文件，通过对里面进行一些必要的修改即可。当然对于开源项目一般都不需要对于子模块的定义。然后根据上述的内容以及<code>Cocoapods</code>官网文档描述进行定义那个文件即可。</p>

<p>当然这个是针对于那些已经创建好项目的小伙伴么，对于那些连项目都没有创建的小伙伴们，<code>Cocoapods</code>还提供了另一个命令:</p>

<pre><code class="language-shell">pod lib create 'FirstCocoapodsProject'  
</code></pre>

<p>通过这条命令会创建好一个项目,在执行这个项目的过程中，根据命令行的引导，你会创建好一个<code>Cocoapdos</code>定义好的一个项目模板。如果对于刚开始创建项目的小伙伴们来说，或许这个是一个不错的选择。通过这个命令<code>Cocoapods</code>会给你创建好,<code>Podspec</code>文件，<a href="https://travis-ci.org/">travis</a>文件,<a href="https://en.wikipedia.org/wiki/MIT_License">MIT Lisence</a>文件以及<code>README.md</code>文件等。
当然对于一个好的开源文件来说的话，一个好的文档说明来说是非常有必要的。所以对于一个你来说最简单的文档说明当然是<code>README.md</code>，特别是那些需要提交到<code>Cocoapds</code>上的Github上的项目。我们一进入Github上的一个项目时，首先看到的就是<code>README.md</code>的内容，所以小伙伴对于这个文件里面的内容要慎重进行书写。当然Github上的这个文件是支持<a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a>的语法，所以对于书写文档也是一种版主。</p>

<p>好了，在准备好文档，代码，内容以及一些其他的资料后，肯定很多小伙伴很迫不及待的想要上传了，但是桥到嘛太(日文-等等的意思),小伙伴有木有想过，我们还有一些其他的工作需要做。</p>

<h2 id="">准备向发布进攻</h2>

<p>首先需要验证你的代码是否有错以及<code>Podspec</code>文件，那么这个时候下面这条命令就起到非常大的作用了：</p>

<pre><code class="language-shell">pod lib lint Name.podspec  
</code></pre>

<p>这条命令是用来验证你的<code>Podspec</code>文件是否有问题，并且代码里面是否有问题的命令。当然我还是建议这条命令后面跟着<code>--verbose</code>的参数，除此之外，这条命令还有其他参数是可以跟着的，这里稍微讲讲其中的一部分，毕竟参数也是比较少嘛：
* --allow-warnings 是否允许警告，在用到第三方框架的时候，有的时候是自带会有warmings的代码，用这参数可以屏蔽警告
* --fail-fast 原本这条命令是在所有代码校验完成后，才会输出所有信息，可是可以通过这个参数在出现第一个错误的时候就停止
* --use-libraries 如果用到的第三方中需要使用库文件的话，则会用到这个参数。（如在项目中依赖了ShareSDK的情况）
* --sources 如果一个库的podspec包含了除了Cocoapods仓库以外的其他库的引用，则需要该参数指明，用逗号分隔，这个参数对于私有库是非常有用的
* --verbose 这条参数可以输出所有的消息信息，在验证过程中，博主还是建议大家都用这个参数
* --subspec=NAME 这个参数是用来指检验某一个子模块的情况
当然，这里只是对参数进行大概的描述，上述参数中，<code>--allow-warnings</code>和<code>--verbose</code>是博主强烈希望大家加上的。</p>

<p>好了，在验证通过你的项目之后，你就可以通过git上的进行打tag了。具体打tag的命令博主就不说了，自己去打tag吧。也有小伙伴将打tag和验证的顺序调换了，不过小伙伴们听哥一句劝啊，千万先验证完后在打tag啊，要不然你打完tag后发现验证不通过之前的tag就没有太大的作用了。</p>

<p>最后，终于到达上传的时间了，运行命令:</p>

<pre><code class="language-shell">pod trunk push 项目名.podspec  
</code></pre>

<p>只要通过上述命令即可上传项目到<code>Cocoapods</code>官方的仓库里去，当然如果你有新的tag版本的话也是通过该命令进行上传新的版本到<code>Cocoapods</code>的。而我们可以通过<code>pod search 名字</code>来查询是否上传成功。</p>

<pre><code class="language-shell">-&gt; iOS-Echarts (1.1.4)
   A custom component for the ecomfe's echarts.
   pod 'iOS-Echarts', '~&gt; 1.1.4'
   - Homepage: https://github.com/Pluto-Y/iOS-Echarts
   - Source:   https://github.com/Pluto-Y/iOS-Echarts.git
   - Versions: 1.1.4, 1.1.3, 1.1.2 [master repo]
</code></pre>

<p>成功的话就会出现上述信息，里面包含了对于项目的描述。
至此，对于提交开源的项目到<code>Cocoapods</code>就结束了。最后在附带一条命令吧，如果你是多人开发的情况，或者是希望加入一个小伙伴到你的项目中的话，可以通过:
<code>pod trunk add-owner '项目名' '邮箱'</code>的方式，将别人加入到你的项目中去。</p>

<h2 id="">总结</h2>

<p>至此，本系列的第二节也就讲完了。本节主要讲了一个<code>Podspec</code>文件的内容、提交项目到<code>Cocoapods</code>仓库的流程以及一些命令的讲解。希望大家多为开源的事业做出自己能做的一部分。本人讲的比较马虎，如果中间有什么不懂的小伙伴可以留言或者是直接联系我。而在下一张中，我讲针对<code>Cocoapods</code>的私有库以及在项目中的多模块管理进行一些讲解。如果有兴趣的小伙伴可以留心一下。好了，到这里小伙伴就可以退下了，朕要就寝了。</p>]]></content:encoded></item><item><title><![CDATA[Cocoapods系列教程(一)——入门]]></title><description><![CDATA[<h2 id="">开场</h2>

<p>对于这个东西估计绝大多数的童鞋应该都不陌生了，不过这里还是稍微对大家唠唠嗑，可能对于那些没用过的童鞋也有可能是一种帮助呢，我们要一视同仁对吧。
对于Cocoapods是个什么东西估计大家多少听过了，可能有些童鞋没有用过，但是多少也听过了。如果没听过的童鞋就听我唠一句嗑：</p>

<blockquote>
  <p><a href="https://cocoapods.org/">Cocoapods</a>是一个框架依赖管理的一个管理工具，主要是用来管理框架一些开源库在项目中的引用。简而言之就是用来管理你的项目中对开源框架或自己公司子模块的依赖。</p>
</blockquote>

<p>当然作为使用者来说，入门教程简单的要死。但是作为一个开发人员不能只满足于当前的步伐，而应该不断进取，要做到能想Cocoapods提交项目，并且在自己项目中熟练应用该工具的程度。特别是如果一个大的公司来说的话,就有可能进行模块化开发了。对于国内很多公司还是在用OC来说的话，Cocoapods应该还是一个不错的模块管理的工具。</p>

<p>当然第一篇文章来点简单的，主要是正对使用者来进行讲解的。</p>

<h2 id="">安装</h2>

<p>当然，刚开始说这说那也没什么卵用，不如直接开始。要使用这个工具，首先当然应该是安装这个工具啦。其实安装这个就一句话的事情：</p>

<pre><code class="language-shell">$ sudo gem install cocoapods
</code></pre>

<p>但是在Mac OS X EL Capitan(10.11)中就会出现<code>Operation not permitted - /usr/bin/</code></p>]]></description><link>http://www.pluto-y.com/cocoapods-getting-stared/</link><guid isPermaLink="false">e1d353e7-1e9c-44a5-93df-c484b6796f70</guid><category><![CDATA[Cocoapods]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Fri, 19 Feb 2016 04:22:39 GMT</pubDate><content:encoded><![CDATA[<h2 id="">开场</h2>

<p>对于这个东西估计绝大多数的童鞋应该都不陌生了，不过这里还是稍微对大家唠唠嗑，可能对于那些没用过的童鞋也有可能是一种帮助呢，我们要一视同仁对吧。
对于Cocoapods是个什么东西估计大家多少听过了，可能有些童鞋没有用过，但是多少也听过了。如果没听过的童鞋就听我唠一句嗑：</p>

<blockquote>
  <p><a href="https://cocoapods.org/">Cocoapods</a>是一个框架依赖管理的一个管理工具，主要是用来管理框架一些开源库在项目中的引用。简而言之就是用来管理你的项目中对开源框架或自己公司子模块的依赖。</p>
</blockquote>

<p>当然作为使用者来说，入门教程简单的要死。但是作为一个开发人员不能只满足于当前的步伐，而应该不断进取，要做到能想Cocoapods提交项目，并且在自己项目中熟练应用该工具的程度。特别是如果一个大的公司来说的话,就有可能进行模块化开发了。对于国内很多公司还是在用OC来说的话，Cocoapods应该还是一个不错的模块管理的工具。</p>

<p>当然第一篇文章来点简单的，主要是正对使用者来进行讲解的。</p>

<h2 id="">安装</h2>

<p>当然，刚开始说这说那也没什么卵用，不如直接开始。要使用这个工具，首先当然应该是安装这个工具啦。其实安装这个就一句话的事情：</p>

<pre><code class="language-shell">$ sudo gem install cocoapods
</code></pre>

<p>但是在Mac OS X EL Capitan(10.11)中就会出现<code>Operation not permitted - /usr/bin/pod</code>的错误，当我遇到这个错误的时候我整个人是拒绝的，可是遇到问题还是得解决嘛。后来发现了两个解决方案。</p>

<h4 id="">方案一</h4>

<pre><code class="language-shell">$ mkdir -p $HOME/Software/ruby
$ export GEM_HOME=$HOME/Software/ruby
$ gem install cocoapods
[...]
1 gem installed cocoapods  
$ export PATH=$PATH:$HOME/Sofware/ruby/bin
</code></pre>

<p>方案一中主要是将GEM的安装路径进行修改，将GEM的默认安装路径修改成了<code>$HOME/Software/ruby</code>这个目录，然后再进行安装，最后将安装路径添加到PATH下，这样可以不用带完整的路径进行运行命令。</p>

<h4 id="">方案二</h4>

<pre><code class="language-shell">$ gem install cocoapods -n ~/Software/ruby
</code></pre>

<p>方案二中使用了gem的自带参数-n—–即指明安装的路径，如果要使用这种的话，也需要将该路径加入PATH下才可以不用带完整的路径进行运行命令。</p>

<p>个人建议是使用第一种，将GEM的安装路径进行修改。这样不需要每次都用-n命令进行指定路径。 </p>

<p>就此，关于安装Cocoapods暂时应该就没有什么问题了。如果还有什么问题的童鞋可以留言。</p>

<h2 id="">初次使用</h2>

<p>关于使用我这里主要分为两个部分，一个部分是关于命令的层次来说的，一部分是关于<code>Podfile</code>的层次来说的。当然对于简单的使用用起来也非常简单。不过刚开始就来一个简单的吧。</p>

<p>就比如我们最常用的<code>AFNetworking</code>、<code>SDwebImage</code>以及<code>iOS-Echarts</code>(打个广告)来说的话，只需在<code>项目名.xcodeproj</code>的目录下创建一个Podfile的文件，然后打开文件，填入一下内容：</p>

<pre><code class="language-shell">platform:ios,'7.1'  
pod 'SDWebImage', '~&gt;3.7'  
pod 'AFNetworking'  
pod 'iOS-Echarts'  
</code></pre>

<p>然后保存，并且打开一个命令行，跳转到<code>Podfile</code>的目录直接运行<code>pod install</code>即可。
当然，你会看到你的目录下回多了目录<code>Pods</code>, 文件<code>Podfile.lock</code>以及文件<code>项目名.xcworkspace</code>。当然以后你打开项目就再也不是<code>xcodeproj</code>的文件了，而是<code>wxcworkspcace</code>的文件。然后你打开项目就会看到除了你的项目外，下面多了一个<code>Pods</code>的一个项目。就此，对于你项目需要依赖的<code>AFNetworking</code>、<code>SDwebImage</code>以及<code>iOS-Echarts</code>的依赖了。非常简单对吧？
当然如果你只是想简单的使用<code>Cocoapods</code>,估计到这一步就完全够了。</p>

<p>对于一个进步青年或者是对于一个使用复杂的场景来说的话，对于<code>Podfile</code>的深入了解还是有一定帮助的。</p>

<h2 id="podfile">深入至Podfile</h2>

<p>比如在你使用<code>AFNetworking</code>是在是2.6.3的版本情况下封好API，但是在你<code>Podfile</code>中对于<code>AFNetworking</code>的定义还是为<code>pod 'AFNetworking'</code>,突然有一天<code>AFNetworking</code>升级为3.0+了。那时候估计你的心里就是：当时我就懵逼了。因为在3.0中移除了<code>AFHTTPRequestOperationManager</code>等一些类，一旦你重新执行的<code>pod update</code>的话，你的程序估计就再也怎么编译不过了。如果你不熟悉<code>Cocoapods</code>的话,估计你就真的的懵逼了。</p>

<blockquote>
  <p>由于有Podfile.lock文件的保护下，在没有执行<code>pod update</code>命令的情况下，是不会讲已有的第三方依赖库进行升级的。所以运行<code>pod install</code>的情况下还是能编译通过的。（感谢<a href="http://weibo.com/2442165052">@星辉Sunny</a>小伙伴的提醒！）</p>
</blockquote>

<p>不过有了这篇教程的一些讲解，妈妈再也不用担心你的作业了。
这部分主要讲解一下<code>Podfile</code>的一些参数和使用的讲解，对于一些使用场景会非常有帮助的。</p>

<h3 id="pod">pod</h3>

<p>首先先来讲讲最核心的<code>pod</code>关键字吧。关于pod的使用在上面可以看得出来是<code>pod '框架名' 参数</code>。
当然对于前面两个<code>pod '框架名'</code>是固定的，没上面好说，参数的话，这里主要讲参数。
参数一： 版本号
从上面可以看得出SDWebImage就接了版本号。不过版本号可以是大于、小于、等于等。当然具体的可以是'> 3.7', '>= 3.7', '&lt; 3.7', '3.7'以及'~> 3.7'。关于普通的就没什么好说了，最后一个<code>~&gt;</code>指的是正对最后一位来说。如使用'~> 3.7.4',意味着'>= 3.7.4'并且'&lt; 3.8.0'的意思。
参数二：地址
Cocoapods可以指定某一个git的目录或者是本地的目录。有的时候我们希望一直用某一个版本最新的版本，即使没有打版本的话，我们可以直接后面接上:<code>git =&gt; 'https://github.com/gowalla/AFNetworking.git'</code>。 <br>
当然除了这种情况外，还有可能是如果是我们自己开发的私有库，并且在开发阶段的情况下，可能就希望开发模式进行引用，则可以使用path参数：<code>:path =&gt; '~/Documents/AFNetworking'</code>
参数三：tag、branche、commit
有的时候我们希望引用有一个tag，branch或者是comit的内容的话可以使用这个参数，分别用<code>:branch =&gt; 'branch名'</code>、<code>:tag =&gt; 'tag名'</code>、<code>:commit =&gt; '提交号'</code>。
参数四：inhibit<em>all</em>warnings!
对于博主来说，一个项目中很多warnings是非常难以忍受的。不知道小伙伴们会不会有这种感觉，自然这个参数是用来避免那些第三方框架中带来的warnings。博主很喜欢他。</p>

<h4 id="platform">platform</h4>

<p>这个参数是只依赖的库希望在哪个平台被编译。
一句话带过吧,直接使用<code>platform :ios, '7.0'</code>。说希望采用iOS7.0的进行编译。之前有个同事没有指定只写了<code>platform</code>后面都没有了，然后一直怎么编译都编译不通过。当时我不知道，一直以为他在某个环节出错了。后来才知道，如果在<code>platform</code>后面没有跟东西的话，默认是在<code>iOS4.3</code>,<code>OSX 10.6</code>,<code>tvOS 9.0</code>以及<code>watchOS 2.0</code>的情况下编译的。很多库都是从6.0或者7.0才开始支持的，自然编译不通过。所以小伙伴们记得要注意这点，后面要跟上具体的platform的参数。</p>

<h4 id="target">target</h4>

<p>这个是指定具体的配置是适配在哪个<code>target</code>，这里的target值得就是Xcode中的target。如果对于一些项目中你的不同target引用的框架不同的话，可以采用这个进行区分。</p>

<pre><code class="language-shell">target "ShowsApp" do  
  pod 'ShowsKit'
  target "ShowsTV" do
    pod "ShowTVAuth"
  end
end  
</code></pre>

<h4 id="use_frameworks">use_frameworks!</h4>

<p>这个指明编译成动态库，而不是静态库，特别是在使用Swift库的过程中，特别需要使用这句。不过他会把所有项目的编译动态库，这一点有点不好。不过在使用Swift库的过程中就没办法了。</p>

<h4 id="source">source</h4>

<p>这个参数是指<code>Cocoapods</code>从哪些仓库(<code>Spec</code>)中获得框架的源代码，如果在结合使用开源库以及自己私有库的情况下，这个参数还是非常有意义的。博主之前在用到自己私有库的情况下就不懂这点，最后查了好久资料才知道的。只需要在<code>Podfile</code>文件开头列出你需要引用库的所有仓库地址即可。</p>

<pre><code class="language-shell">source 'https://github.com/artsy/Specs.git'  
source 'https://192.168.0.90:8888/MySepcs/Specs.git'  
</code></pre>

<p>好了对于Podfile常用几个稍微拿出来说了一下，如果还有更深入不懂的情况下，可以查看<a href="https://guides.cocoapods.org/syntax/podfile.html#podfile">Cocoapods Guide</a>。
最后给一个官方的Demo吧.</p>

<pre><code class="language-shell"># open source
source 'https://github.com/CocoaPods/Specs.git'

# my work
source 'https://github.com/Artsy/Specs.git'

target 'App' do

  pod 'Artsy+UIColors'
  pod 'Artsy+UIButtons'

  pod 'FLKAutoLayout'
  pod 'ISO8601DateFormatter', '0.7'
  pod 'AFNetworking', '~&gt; 2.0'

  target 'AppTests' do
    pod 'FBSnapshotTestCase'
    pod 'Quick'
    pod 'Nimble'
  end
end  
</code></pre>

<h2 id="podinstallpodupdate">命令行pod install 和 pod update</h2>

<p>在使用Cocoapods的过程中，我们可能要更新或者是安装一个新的第三方框架。自然这里就避免不了要用这两个命令了。对于这两个命令来说，一个是在有新的第三方框架引入是运行，另一个是纯粹为了更新本地的第三框架。
对于二者的命令来说其参数都是大同小异的，这里大概讲解一下。</p>

<h4 id="norepoupdate">参数--no-repo-update</h4>

<p>这个参数应该是大家最常用的参数之一，其主要的作用是用于在执行<code>pod install</code>和<code>pod update</code>两条命令是而执行的<code>pod repo update</code>的操作。而<code>pod repo update</code>则是跟新本地已有的第三框框架。对于大多数情况下，我们只希望跟新当前项目的，并且这个操作执行的时间都是相对比较长，所以在执行这两条命令是，大多数人都添加了这个参数。</p>

<h4 id="verbosesilent">参数--verbose 和 --silent</h4>

<p>这两个参数是用来控制<code>pod</code>命令的,而有些人不希望看到输出的情况下可以选择<code>--silent</code>。而对于在执行这两条命令的情况下如果出错了，则可能添加<code>--verbose</code>的参数能看到具体的出错信息。<code>--verbose</code>则是用来输出这两条命令执行过程中所包含的所有信息。对于大多数的<code>Cocoapods</code>的命令行来说都带有着两个参数。</p>

<p>其他的细节就不具体说了，大家可以通过<code>help</code>命令来进行查看详情。</p>

<h2 id="">结论</h2>

<p>简单来说要使用<code>Cocoapods</code>只需通过三个步骤：安装、写Podfile和<code>pod install</code>命令即可。</p>

<p>到此关于<code>Cocoapods</code>作为使用者来说的大体内容就差不多这么多了。关于细节方面大家可以通过<a href="https://cocoapods.org/">Cocoapods官网</a>来进行查阅。在下一篇中，我将对<code>Cocoapods</code>作为一个贡献者或者制作一个私有库进行讲解。而可能在第三篇中会对在Cocoapods在项目进行模块化管理过程中的使用进行讲解，其中应该包含了在一个项目做同时使用私有库以及开源库之间引用以及如何进行管理各个模块进行大致描述。</p>]]></content:encoded></item><item><title><![CDATA[Health Kit教程（二）]]></title><description><![CDATA[<h2 id="">引言</h2>

<p>继续Health Kit的教程，上篇文章<a href="http://www.pluto-y.com/health-kit-totorial-1/">Health Kit教程（一）</a>中介绍了关于Health Kit的基础用法。而这篇文章的基础就是建立在上篇文章的基础上，如果对于Health Kit的基础还不是很熟悉的童鞋可以阅读上篇文章来学一些基础的Health Kit的基础用法，然后再来看这篇文章。这篇文章主要是针对在一次运动中的一系列数据进行处理的方法。我们都知道，在一次运动中不可能只是一个数据的变化，有可能有多个数据的变化。就用跑步来说，跑步过程中除了心率变化外，还有一些列的其他数据变化如距离、卡路里消耗等等。所以对于一次运动过程中则需要对于多个数据进行处理。</p>

<p>本片文章是在Xcode 7.1 / Swift 2.0的基础上进行开发的。</p>

<h2 id="">处理运动</h2>

<h3 id="">保存体能训练</h3>

<p>在一次运动中，我们需要对多种数据进行处理，而如果为每个数据都单独进行处理的话，没办法看出各个数据之间的关系。而Health Kit针对这点则提出了一个概念为Workout(体能训练)，即可以将多种数据的变化添加到一个Workout进行处理。就好像是一个购物清单一样，里面包含着各种数据指标的变化。同理，一个体能训练可以管理多种数据。
这里我们就举一个最简单的运动类型----跑步，这篇文章就针对跑步进行讲解。假设在跑步过程中我们只考虑一下几个指标：距离、消耗时间以及消耗的卡路里。
那么开始上代码吧，先再工具类中添加一个方法用于保存体能训练的数据：</p>]]></description><link>http://www.pluto-y.com/health-kit-totorial-2/</link><guid isPermaLink="false">0c4cf1e5-d214-4fb8-8855-39f72150da95</guid><category><![CDATA[Health Kit]]></category><category><![CDATA[Swift]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Mon, 21 Dec 2015 03:40:12 GMT</pubDate><content:encoded><![CDATA[<h2 id="">引言</h2>

<p>继续Health Kit的教程，上篇文章<a href="http://www.pluto-y.com/health-kit-totorial-1/">Health Kit教程（一）</a>中介绍了关于Health Kit的基础用法。而这篇文章的基础就是建立在上篇文章的基础上，如果对于Health Kit的基础还不是很熟悉的童鞋可以阅读上篇文章来学一些基础的Health Kit的基础用法，然后再来看这篇文章。这篇文章主要是针对在一次运动中的一系列数据进行处理的方法。我们都知道，在一次运动中不可能只是一个数据的变化，有可能有多个数据的变化。就用跑步来说，跑步过程中除了心率变化外，还有一些列的其他数据变化如距离、卡路里消耗等等。所以对于一次运动过程中则需要对于多个数据进行处理。</p>

<p>本片文章是在Xcode 7.1 / Swift 2.0的基础上进行开发的。</p>

<h2 id="">处理运动</h2>

<h3 id="">保存体能训练</h3>

<p>在一次运动中，我们需要对多种数据进行处理，而如果为每个数据都单独进行处理的话，没办法看出各个数据之间的关系。而Health Kit针对这点则提出了一个概念为Workout(体能训练)，即可以将多种数据的变化添加到一个Workout进行处理。就好像是一个购物清单一样，里面包含着各种数据指标的变化。同理，一个体能训练可以管理多种数据。
这里我们就举一个最简单的运动类型----跑步，这篇文章就针对跑步进行讲解。假设在跑步过程中我们只考虑一下几个指标：距离、消耗时间以及消耗的卡路里。
那么开始上代码吧，先再工具类中添加一个方法用于保存体能训练的数据：</p>

<pre><code class="language-swift">    class func saveRunWorkout(startDate:NSDate , endDate:NSDate , distance:Double, distanceUnit:HKUnit , kiloCalories:Double,
        completion: ( (Bool, NSError!) -&gt; Void)!) {

            // 1. 为距离和
            let distanceQuantity = HKQuantity(unit: distanceUnit, doubleValue: distance)
            let caloriesQuantity = HKQuantity(unit: HKUnit.kilocalorieUnit(), doubleValue: kiloCalories)

            // 2. 保存一个跑步记录
            let workout = HKWorkout(activityType: HKWorkoutActivityType.Running, startDate: startDate, endDate: endDate, duration: abs(endDate.timeIntervalSinceDate(startDate)), totalEnergyBurned: caloriesQuantity, totalDistance: distanceQuantity, metadata: nil)
            store.saveObject(workout, withCompletion: { (success, error) -&gt; Void in
                if( error != nil  ) {
                    // 保存错误的时候将错误传递到调用的闭包
                    completion(success,error)
                } else {
                    // 保存成功
                    completion(success,nil)
                }
            })
    }
</code></pre>

<p>然后再写一个界面进行添加，主要是针对几个数值进行添加：</p>

<ul>
<li>开始时间</li>
<li>持续时间</li>
<li>距离</li>
<li>卡路里消耗</li>
</ul>

<p>对于建立一个体能训练（<code>Workout</code>类型）的数据主要针对的就是这四个数值进行建立，可以除了上述代码中的方法还可以通过其他方法来进行创建<code>Workout</code>类型的保存点。通过上述方法即可在Health Kit中的<code>健身数据-体能训练</code>中保存一个数据点。大家可以通过保存好一个数据后进行查看是否保存成功。</p>

<h3 id="">保存与之相关的数据点</h3>

<p>在保存体能训练的过程中，从界面上我们好像是看到了卡路里以及距离的填写，可是到Health Kit中会发现找不到卡路里以及距离的数据点。保存体能训练只是会保存一个体能训练，需要对自己想要保存的相关联的数据点进行再一次保存才可以。感觉这里Healthk Kit应该添加一个bool类型的变量，如果需要保存数据点的话，应该自己动去保存。要不然开发者需要针对这个部分再进行保存一次。
那么接下来再上述方法上在进行改进一下吧：</p>

<pre><code class="language-swift">    class func saveRunWorkout(startDate:NSDate , endDate:NSDate , distance:Double, distanceUnit:HKUnit , kiloCalories:Double,
        completion: ( (Bool, NSError!) -&gt; Void)!) {

            // 1. 为距离和
            let distanceQuantity = HKQuantity(unit: distanceUnit, doubleValue: distance)
            let caloriesQuantity = HKQuantity(unit: HKUnit.kilocalorieUnit(), doubleValue: kiloCalories)

            // 2. 保存一个跑步记录
            let workout = HKWorkout(activityType: HKWorkoutActivityType.Running, startDate: startDate, endDate: endDate, duration: abs(endDate.timeIntervalSinceDate(startDate)), totalEnergyBurned: caloriesQuantity, totalDistance: distanceQuantity, metadata: nil)
            store.saveObject(workout, withCompletion: { (success, error) -&gt; Void in
                if( error != nil  ) {
                    // 保存错误的时候将错误传递到调用的闭包
                    completion(success,error)
                } else {
                /// --------------------------修改部分----------------------
                    // 如果保存成功的话，将建立新的相关数据到Health Kit，即存储距离和卡路里消耗的点数到Health Kit
                    let distanceSample = HKQuantitySample(type: HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)!, quantity: distanceQuantity, startDate: startDate, endDate: endDate)
                    let caloriesSample = HKQuantitySample(type: HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!, quantity: caloriesQuantity, startDate: startDate, endDate: endDate)

                    // 保存卡路里和距离，并且与之前的Workout相关联
                    store.addSamples([distanceSample,caloriesSample], toWorkout: workout, completion: { (success, error ) -&gt; Void in
                        // 保存成功
                        completion(success,nil)
                    })
                }
            })
    }
</code></pre>

<p>可以看到上述代码中的修改部分，可以看得出只需在保存<code>Workout</code>类型的数据点成功后，在添加对应的卡路里以及距离的数据点，保存的时候与<code>Workout</code>进行连接即可。这样单进入Health Kit后就会发现新保存的体能训练所带的卡路里消耗以及距离的数据点也保存下来了。且数据点是可以点击的，可以查看到与之相关联的内容。</p>

<h2 id="">获得运动数据</h2>

<p>既然保运动数据，那么有的时候我们需要对这些运动数据进行获取。那么该如何获取呢？其实也是非常简单的，跟读取普通的数据点也是差不多的，只需添加以下的方法：</p>

<pre><code class="language-swift">    class func readRunningWorkOuts(completion: (([AnyObject]!, NSError!) -&gt; Void)!) {

        // 1. 设置读取类型
        let predicate =  HKQuery.predicateForWorkoutsWithWorkoutActivityType(HKWorkoutActivityType.Running)
        // 2. 设置排序字段
        let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
        // 3. 创建一个查询的Query
        let sampleQuery = HKSampleQuery(sampleType: HKWorkoutType.workoutType(), predicate: predicate, limit: 0, sortDescriptors: [sortDescriptor])
            { (sampleQuery, results, error ) -&gt; Void in

                if let queryError = error {
                    print( "读取跑步数据的时候发生错误, Error: \(queryError.localizedDescription)")
                }
                completion(results,error)
        }
        // 4. 执行查询的Query
        store.executeQuery(sampleQuery)

    }
</code></pre>

<p>需要注意的是，这里获得的是体能训练的数据。也就是说即使不保存对应相关联的数据点也能从体能训练中获得对应的卡路里消耗以及距离等数据的信息，因为可以从保存体能训练中看的出，体能训练创建的时候也是保存了这些数据信息的。所以对于那些偷懒的小伙伴们，可以直接从体能训练中获得卡路里消耗以及距离的信息。最后添加一个对应的界面来查询即可。</p>

<h2 id="">总结</h2>

<p>从这两篇文章可以看出，其实Health Kit的简单数据保存和读取其实还是非常简单的。Health Kit更像是一个数据库，我们与Health Kit的交互就如是于数据库进行交互，读取和保存。而对于普通的App来说，以上说的内容也大体能用了，只需稍加进行变动和修改即可。如果小伙伴在使用的过程中还有遇到什么其他的问题，可以联系我的微博<a href="http://weibo.com/u/5690716723">叫什么都不如叫Pluto-Y</a>来联系我，或者去我的<a href="https://github.com/Pluto-Y/Demos/tree/master/HealthKitTotorial">Github</a>上下载代码来研究。好吧最后来看看运行的效果吧。</p>

<p><img src="http://www.pluto-y.com/content/images/2015/12/HealthKitDemo2-1.gif" alt="运行效果"></p>]]></content:encoded></item><item><title><![CDATA[Health Kit教程（一）]]></title><description><![CDATA[<h2 id="">引言</h2>

<p>Health Kit是在iOS8中才出现的一个新的特性，是用来提供存储和获取用户健康数据的一个苹果自带的数据中心。而本篇文章的核心就是提供一个Health Kit的教程性知识。而对于Health Kit的教程将用两篇文章的篇幅进行讲解。通过这两篇文章，小伙伴们将会学到关于Health Kit的一些基础使用。 <br>
而本片文章是在Xcode 7.1 / Swift 2.0的基础上进行开发的。</p>

<p>准备好开始Health Kit之旅了吗？那么开始吧！</p>

<h2 id="">开始</h2>

<p>那么在开始之前，首先对本片文章所要叙述的内容进行大体介绍一下。本篇文章主要讲解一下几个知识点：
* 获取Health Kit 的授权
* 读取用户属性
* 读取或写入用户特性
鉴于编程的学习都是需要建立在编码的基础上，那么小伙伴都新建好新的Health Kit的项目了吗？如果准备好的话，那我们就继续往下走吧。</p>

<h3 id="healthkit">获取Health Kit的授权</h3>

<h4 id="">授权之配置篇</h4>

<p>在开始编程之前，我们肯定要对项目中关于获得Health Kit的授权进行一些基础的配置，保证我们之后在写代码过程中畅通无阻。
那么首先先填好Bundle Identifier以及选择好对应的Team，具体如下图
<img src="http://www.pluto-y.com/content/images/2015/12/1.png" alt="Bundle Identifier And Team">
在这需要到target的General中填写好对应的渠道名以及选好对应的开发者
然后就是就是添加对应授权文件（即entitlement文件），其实不需要我们手动去添加，只需<code>Capabilities</code></p>]]></description><link>http://www.pluto-y.com/health-kit-totorial-1/</link><guid isPermaLink="false">58aa6a63-9807-4bf6-bb75-b24595ad3ee3</guid><category><![CDATA[Health Kit]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Sun, 06 Dec 2015 13:04:48 GMT</pubDate><content:encoded><![CDATA[<h2 id="">引言</h2>

<p>Health Kit是在iOS8中才出现的一个新的特性，是用来提供存储和获取用户健康数据的一个苹果自带的数据中心。而本篇文章的核心就是提供一个Health Kit的教程性知识。而对于Health Kit的教程将用两篇文章的篇幅进行讲解。通过这两篇文章，小伙伴们将会学到关于Health Kit的一些基础使用。 <br>
而本片文章是在Xcode 7.1 / Swift 2.0的基础上进行开发的。</p>

<p>准备好开始Health Kit之旅了吗？那么开始吧！</p>

<h2 id="">开始</h2>

<p>那么在开始之前，首先对本片文章所要叙述的内容进行大体介绍一下。本篇文章主要讲解一下几个知识点：
* 获取Health Kit 的授权
* 读取用户属性
* 读取或写入用户特性
鉴于编程的学习都是需要建立在编码的基础上，那么小伙伴都新建好新的Health Kit的项目了吗？如果准备好的话，那我们就继续往下走吧。</p>

<h3 id="healthkit">获取Health Kit的授权</h3>

<h4 id="">授权之配置篇</h4>

<p>在开始编程之前，我们肯定要对项目中关于获得Health Kit的授权进行一些基础的配置，保证我们之后在写代码过程中畅通无阻。
那么首先先填好Bundle Identifier以及选择好对应的Team，具体如下图
<img src="http://www.pluto-y.com/content/images/2015/12/1.png" alt="Bundle Identifier And Team">
在这需要到target的General中填写好对应的渠道名以及选好对应的开发者
然后就是就是添加对应授权文件（即entitlement文件），其实不需要我们手动去添加，只需<code>Capabilities</code>中打开<code>HealtKit</code>的开关为On即可，然后稍等片刻后即可。如下图：
<img src="http://www.pluto-y.com/content/images/2015/12/2.png" alt="Add HealtKit Entitlement"></p>

<blockquote>
  <p>注意：这里有一点强烈建议小伙伴们需要注意的一点就是，如果小伙伴的程序是兼容iOS 8一下或者需要支持iPad之类等不支持Health Kit的设备的话，需要到<code>Info.plist</code>文件中删除<code>Required device capabilities</code>下的<code>healthkit</code>的值,否则如果在iPad或者iOS7上安装不上，博主可是不负责的哟！</p>
</blockquote>

<p>好了关于配置方面的，就到此为止了，接下来小伙伴就可以开始愉快的编程了。</p>

<h4 id="">授权之代码篇</h4>

<p>在此之前，博主的Demo中是为HealthKit的操作添加了一个Util的工具类，命名为<code>HealthKitUtil.swift</code>。打开这个文件首先当然得<code>import HealthKit</code>了。然后关于Health Kit的数据处理都要用到<code>HKHealthStore</code>这个对象的，就如引言中所说的，Health Kit其实就是一个数据中心，主要用来存储和读取用户的健康信息。而<code>HKHealthStore</code>就像是一个数据库的数据库处理对象一样，所以在我们对Health Kit的数据进行操作的过程中都需要通过<code>HKHealthStore</code>的对象。所以在我们HealthKitUtil工具类里面就会存在一个其的属性。
那么接下来在获得Health Kit授权之前，我们首先得要知道我们对Health Kit的需要哪些读的权利以及哪些写的权利，毕竟我们有的时候是不需要对Health Kit的所有数据类型都进行操作的。例如对于一个智能秤相关的App可能只对体重，脂肪率以及BMI等一些跟身体相关的数据关心，而对于运动的一些数据则不进行关心。
而对于Health Kit的数据类型都是<code>HKObjectType</code>的子类,并且在<code>HKObjectType</code>中也提供了产生对应子类的方法，只需执行对应的方法即可获得对应的操作类型(如血型等)。具体方法如下：</p>

<pre><code class="language-swift">+ (nullable HKQuantityType *)quantityTypeForIdentifier:(NSString *)identifier;
+ (nullable HKCategoryType *)categoryTypeForIdentifier:(NSString *)identifier;
+ (nullable HKCharacteristicType *)characteristicTypeForIdentifier:(NSString *)identifier;
+ (nullable HKCorrelationType *)correlationTypeForIdentifier:(NSString *)identifier;
+ (HKWorkoutType *)workoutType;
</code></pre>

<p>可以看得出来，大部分的操作类型都是通过一个叫<code>identifier</code>的来进行产生的，而具体包含了那些操作类型呢，其实可以通过<a href="https://developer.apple.com/library/prerelease/watchos/documentation/HealthKit/Reference/HealthKit_Constants/#//apple_ref/doc/constant_group/Body_Measurements">Health Constant Reference</a>来查看。从中找到你需要读和写的操作类型。然后调用<code>HKHealthStore</code>的<code>requestAuthorizationToShareTypes(typesToShare: Set&lt;HKSampleType&gt;?, readTypes typesToRead: Set&lt;HKObjectType&gt;?, completion: (Bool, NSError?) -&gt; Void)</code>方法即可。具体代码如下：</p>

<pre><code class="language-swift">    class func authorizeHealthKit(completion: ((success:Bool, error:NSError!) -&gt; Void)!) {
        // 1. 想要读的操作类型
        let readTypes : Set&lt;HKObjectType&gt; = [
            HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth)!,
            HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBloodType)!,
            HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex)!,
            HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)!,
            HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)!,
            HKObjectType.workoutType()
            ]

        // 2. 想要写的操作类型
        let writeTypes : Set&lt;HKSampleType&gt; = [
            HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex)!,
            HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!,
            HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)!,
            HKQuantityType.workoutType()
            ]

        // 3. 查看在该设备上HealthKit是否可用
        if NSClassFromString("HKHealthStore") != nil &amp;&amp; !HKHealthStore.isHealthDataAvailable() {
            let error = NSError(domain: "com.pluto-y.healthkittotorial", code: 2, userInfo: [NSLocalizedDescriptionKey:"HealthKit在该设备上不可用"])
            if( completion != nil ) {
                completion(success:false, error:error)
            }
            return;
        }

        // 4. 获取授权
        store.requestAuthorizationToShareTypes(writeTypes, readTypes: readTypes) { (success, error) -&gt; Void in
            if completion != nil {
                completion (success: success, error: error)
            }
        }
    }
</code></pre>

<p>添加这个代码后，在视图中添加一个授权按钮进行调用该方法。至此就成功获得Health Kit的授权了。</p>

<h3 id="">读取用户特征</h3>

<p>在获取授权之后，当然先开始一个简单的就是获得用户的特征，关于用户特征就是在Health Kit中之允许读的数据，在完成这个教程的时候，关于这个特性包含了四项，分别是血型、性别、生日以及皮肤类型。而在这个教程中主要用到的就只有生日，血型和性别三个。如果在测试前小伙伴还没有填写过特征的话，可以去Health Kit中填写，保证后面的demo能正常运行。
回到工具类中，通过添加以下方法来进行读取特性：</p>

<pre><code class="language-swift">    // 读取特征
    class func readCharacteristics() -&gt; (age : Int?, biologicalSex : HKBiologicalSexObject?, booldType : HKBloodTypeObject?) {
        var age : Int? = 0
        // 读取生日
        if let birthday = try? store.dateOfBirth() {
            let today = NSDate()
            let differenceComponents = NSCalendar.currentCalendar().components(NSCalendarUnit.Year, fromDate: birthday, toDate: today, options: NSCalendarOptions(rawValue: 0))
            age = differenceComponents.year
        }
        // 读取性别
        let biologicalSex : HKBiologicalSexObject? = try? store.biologicalSex()
        // 读取血型
        let bloodType : HKBloodTypeObject? = try? store.bloodType()
        print("age : \(age), biologicalSex: \(biologicalSex), bloodType: \(bloodType)")
        return (age, biologicalSex, bloodType)
    }
</code></pre>

<p>从上面的代码可以看得出来对于用户的特征来说的话，读取是一件非常简单的事情，就好像是在读取一个实例的属性一样。而之后只需在视图中添加一个按钮和现实结果的视图进行演示这个结果即可。</p>

<h3 id="">读取和写入用户简单数据</h3>

<p>相对于用户特征来说，用户数据则是通过各种设备、第三方App或者用户手动输入测量数据。而这些数据一般都是连续的数据，不像是用户特征，在Health Kit中只存在一个。对于像身高、体重、BMI等一些数据可能随着时间的变化都会有不同的数据。而在这个小节中，博主给大家先讲述如果简单的读取最近的一个身高以及体重，并根据读取到的数据计算出对应的BMI，再将这个计算后的BMI存入Health Kit中(如果在Health Kit中没有身高和体重数据点的童鞋可以先去Health Kit中写入两个数据点)。</p>

<h4 id="">读取身高和体重</h4>

<p>首先先来读取Health Kit中的身高和体重数据，通过这节后小伙伴们可以读取对应的其他类型的数据。其实对于微信、QQ等社交工具中有个计步排行榜也是通过读取Health Kit来读取的，类似的小伙伴也可以通过这节学到如何读取其他数据的方法，只需在读取的时候选择对应的数据类型即可。废话不多说，上代码：</p>

<pre><code class="language-swift">    class func readMostRecentData(sampleType:HKSampleType , completion: ((HKSample!, NSError!) -&gt; Void)!) {

        // 1. 产生一个相对相对久远的时间到单签时间的时间段（用于读取数据的时间段）
        let timeBegin = NSDate.distantPast()
        let timeEnd   = NSDate()
        let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(timeBegin, endDate:timeEnd, options: .None)

        // 2. 产生一个排列的过滤器，即按照什么来排序
        let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
        // 3. 取的条目数，即我们要从Health Kit中取多少条数据
        let limit = 1

        // 4. 产生一个从Health Kit中获取数据的Query对象，HKHealthStore通过该对象进行取得符合条件的数据
        //    关于最后一个参数闭包是用于获取数据后的回调
        let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescriptor])
            { (sampleQuery, results, error ) -&gt; Void in

                if let _ = error { // 读取过程中出错
                    completion(nil,error)
                    return;
                }

                // 读取第一条数据
                let mostRecentSample = results!.first as? HKQuantitySample

                // 将第一条数据传给最后一个参数的闭包中
                if completion != nil {
                    completion(mostRecentSample,nil)
                }
        }
        // 5. 执行读取Health Kit的操作
        store.executeQuery(sampleQuery)
    }
</code></pre>

<p>上述代码是在Health Kit工具类添加的方法，用来读取指定类型数据最近时间的一条数据的工具方法。其中<code>HKSampleType</code>则为<code>HKObjectType</code>的子类,可以通过<code>HKObjectType</code>提供的根据identifier来获得具体需要获取数据的类型。而在获取数据时候需要三个参数，分别是：
* 获取数据的时间段，即需要获取数据时间过滤器，只有在这个时间段内的数据才能被取到。在该工具方法中，随机产生了一个相对当前时间往前相对比较久远的时间段，这样能保证去到一个有效的数据
* 获取数据的排序字段，即根据那个字段来进行排序，具体可以排序的字段可以通过<code>HKSample</code>类以及子类来查看可排序字段。在该工具方法中，都采用按照时间逆序来排序
* 获取条目数，即要获取的条目数，如果不希望指明条目数的话可以传递<code>HKObjectQueryNoLimit</code>即可。在该工具方法中，只获取距离当前时间最近的一条数据</p>

<p>既然有了数据的方法，可以通过一下方法来进行读取：</p>

<pre><code class="language-swift">    // 1. 指明读取类型
    let dataType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)!

    // 2. 调用工具类的方法来获取体重
    HealthKitUtil.readMostRecentData(dataType, completion: { (mostRecentWeight, error) -&gt; Void in

    if( error != nil ) { //获取体重过程中出现错误
      print("读取体重过程出错  error: \(error.localizedDescription)")
      return;
    }

    var weightLocalizedString = "Unknow";
    // 3. 将读取到的体重信息进行处理后再显示
    self.weight = mostRecentWeight as? HKQuantitySample
    if let kilograms = self.weight?.quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo)) {
      let weightFormatter = NSMassFormatter()
      weightFormatter.forPersonMassUse = true;
      weightLocalizedString = weightFormatter.stringFromKilograms(kilograms)
    }

    // 4. 更新界面上的显示
    dispatch_async(dispatch_get_main_queue(), { () -&gt; Void in
        self.yWeightLbl.text = weightLocalizedString
      });
    });
</code></pre>

<p>可以通过上方的方法进行读取体重与身高，并将获取到的数据进行基本处理后在显示到界面上。其中关于获取到的数据，其类型为<code>HKQuantitySample</code>的数据类型，而该数据类型包含了两个属性变量<code>HKQuantityType</code>和<code>HKQuantity</code>两个实例变量的属性，前者用来保存数据的类型信息，后者则用来保存数据的值以及单位信息。而其中上述代码中的主要是针对数值信息进行转换成本地单位后再进行显示。</p>

<p>而这里设置到了在Health Kit中添加的<code>HKUnit</code>类来进行单位的管理，其中包含了许多单位，如重量，长度、压力等等等。而我们可以通过具体需求来显示不同的单位，并且其中也内置了具体的转换方法，不需要我们自己去将单位进行转换。具体的内容可以查看HKUnit的头文件进行学习。</p>

<h4 id="bmi">保存BMI数值</h4>

<p>有了身高和体重之后就可以计算BMI了，并且在Health Kit中添加一个BMI的数据点。那么在工具类中添加以下代码来进行保存一个BMI的数据点，小二上代码：</p>

<pre><code class="language-swift">// 保存BMI数据点
class func saveBMISample(bmi:Double, date:NSDate ) {

    // 1. 创建数据点
    let bmiType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex)
    let bmiQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: bmi)
    let bmiSample = HKQuantitySample(type: bmiType!, quantity: bmiQuantity, startDate: date, endDate: date)

    // 2. 保存数据点
    store.saveObject(bmiSample, withCompletion: { (success, error) -&gt; Void in
        if( error != nil ) {
            print("报错BMI数据点出错 error:: \(error?.localizedDescription)")
        } else {
            print("保存BMI数据点成功!")
        }
    })
}
</code></pre>

<p>该工具方法的第一个参数BMI的数值，第二个参数则为保存在本地BMI值的时间。而可以看出来了在第一步骤中证明了上方说的<code>HKQuantitySample</code>包含了一个数据类型的和数据的值的信息的两个属性。而第二步中，则将数据点存入到Health Kit中去，并且根据是否成功输出不同的信息。</p>

<h4 id="">简单数据小结</h4>

<p>可以看出简单数据的读取和存入就很像我们平时操作数据库的习惯。但是跟数据库操作不同的地方在于数据库操作中我们需要知道数据库表的结构，要知道如何获取语法等。而对于Health Kit操作这些数据点的方法则是对数据库的底层进行封装，并且将封装后的可操作的接口和字段进行公开。这样我们就不需要知道具体的表结构以及语法了。我们只需根据API中提供的字段和方法进行操作即可。
在这里还可以告诉小伙伴们，其实对于iPhone中的微信和QQ的计步排行榜就是从Health Kit中读取的，那么小伙伴们当然可以通过Health Kit提供的API写入自己想要的Health Kit的步数咯。比如今天想写12345步，明天想写123456步都可以随心所欲，那么每天第一名不就是妥妥的么。然后你就义正言辞的发个说说:"你追我，如果你知道我，我就让你嘿嘿嘿"。每天都写超级多不，保证肯定没有人能追的上你。
<img src="http://wanzao2.b0.upaiyun.com/system/pictures/16/original/15.png" alt="嘿嘿嘿"></p>

<h2 id="">本文小结</h2>

<p>好了，最后来总结一下吧。这篇教程教了一些入门方面的操作和编程。对于一些简单的操作只要按照这篇博问里面进行操作即可。至于锻炼Workout这种类型的数据操作将在下篇博客中进行讲解，大家尽请期待。看看入门Demo的运行情况：
<img src="http://www.pluto-y.com/content/images/2015/12/HealthKitDemo1.gif" alt="HealthKitDemo1.gif"></p>

<p>至于Demo的话大家可以到我的<a href="https://github.com/Pluto-Y/Demos/tree/master/HealthKitTotorial">Github</a>上下载来研究研究，如果有什么问题或者有什么错误的地方麻烦大家指出。</p>]]></content:encoded></item><item><title><![CDATA[遭遇Crash文件战----教你如何搞定iOS崩溃日志]]></title><description><![CDATA[<h3 id="">请叫我背景</h3>

<p>最近在提交应用到App Store的时候，竟然被拒了两次。那时候心里的想法是，尼玛完蛋了，要被老板开除了，我是不是要失业了。于是乎那两周几乎毛脑子都是为什么Apple你这么狠心，我们明明相爱了那么多年，你竟然就这样抛弃了我。我不想活了，不要拦着我，我要分分钟切腹给你看。然后内心的纠结并没有什么卵用。而关于第一次被拒我这里就不说了，正对第二次被拒稍微进行拓展。</p>

<h3 id="">崩溃被拒</h3>

<p>第二次被拒这是因为一个崩溃而被拒的。在我刚看到崩溃的时候就想着，小case，崩溃的bug还是比较好解决的，至少好定位，相对第一次那种没有说明具体原因被拒好查多了。关键是Apple的审核人员还提供了crash的文件给我。在我洋洋得意的下载并且打开苹果方面放给我的崩溃日志的时候，尼玛我又蒙逼了，里面完全没有我熟悉的代码以及行数的显示什么的，跟我所熟知的类型（见下图）完全不一样啊,熟悉的配方，熟悉的味道明明在后面是有大概的错误方法调用的堆栈信息的说。</p>

<p><img src="http://www.pluto-y.com/content/images/2015/11/crash--.png" alt="crash正常文件"></p>

<p>特么苹果给我的是一对内存地址的样式（见下图）啊!!!!!!!特么只有一对地址的位置信息，特么你告诉我这怎么看，这怎么看，你过来看我打不打死你！</p>

<p><img src="http://www.pluto-y.com/content/images/2015/11/CrashFromApple.png" alt="Crash From Apple"></p>

<p>然后我特么自己想重新苹果审核人员重现的Bug特么一直就是重现不了，没办法最后只能去找找看下有没有办法反编译这种加密的崩溃日志。然后我在研究官方文档的时候我看到了一句话叫做：</p>

<blockquote>
  <p>Symbolication - resolving backtrace addresses to source</p></blockquote>]]></description><link>http://www.pluto-y.com/get-crash-file-done/</link><guid isPermaLink="false">3e52f872-814e-4e4d-8e31-3d852ca16038</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Tue, 01 Dec 2015 05:36:17 GMT</pubDate><content:encoded><![CDATA[<h3 id="">请叫我背景</h3>

<p>最近在提交应用到App Store的时候，竟然被拒了两次。那时候心里的想法是，尼玛完蛋了，要被老板开除了，我是不是要失业了。于是乎那两周几乎毛脑子都是为什么Apple你这么狠心，我们明明相爱了那么多年，你竟然就这样抛弃了我。我不想活了，不要拦着我，我要分分钟切腹给你看。然后内心的纠结并没有什么卵用。而关于第一次被拒我这里就不说了，正对第二次被拒稍微进行拓展。</p>

<h3 id="">崩溃被拒</h3>

<p>第二次被拒这是因为一个崩溃而被拒的。在我刚看到崩溃的时候就想着，小case，崩溃的bug还是比较好解决的，至少好定位，相对第一次那种没有说明具体原因被拒好查多了。关键是Apple的审核人员还提供了crash的文件给我。在我洋洋得意的下载并且打开苹果方面放给我的崩溃日志的时候，尼玛我又蒙逼了，里面完全没有我熟悉的代码以及行数的显示什么的，跟我所熟知的类型（见下图）完全不一样啊,熟悉的配方，熟悉的味道明明在后面是有大概的错误方法调用的堆栈信息的说。</p>

<p><img src="http://www.pluto-y.com/content/images/2015/11/crash--.png" alt="crash正常文件"></p>

<p>特么苹果给我的是一对内存地址的样式（见下图）啊!!!!!!!特么只有一对地址的位置信息，特么你告诉我这怎么看，这怎么看，你过来看我打不打死你！</p>

<p><img src="http://www.pluto-y.com/content/images/2015/11/CrashFromApple.png" alt="Crash From Apple"></p>

<p>然后我特么自己想重新苹果审核人员重现的Bug特么一直就是重现不了，没办法最后只能去找找看下有没有办法反编译这种加密的崩溃日志。然后我在研究官方文档的时候我看到了一句话叫做：</p>

<blockquote>
  <p>Symbolication - resolving backtrace addresses to source code methods and lines - requires the application binary that was uploaded to the App Store and the .dSYM file that was generated when that binary was built. This must be an exact match - otherwise, you might get a partially symbolicated crash report.</p>
</blockquote>

<p>里面有提到通过上传的二级制文件以及.dSYM文件可以标识化这种只含有追溯地址的崩溃日志，即将我看不懂的这种加密的崩溃日志，转换成我稍微熟悉一点的堆栈信息的崩溃日志。废话不多说，直接说如何做吧。</p>

<h3 id="">解密地址堆栈</h3>

<p>解密这种地址的崩溃日志只需几步就能搞定了。只要准备好对应的<code>.app</code>的二进制文件以及产生二进制文件的<code>dSYM</code>文件。将"加密"的崩溃日志与这两个文件放置在同一个目录，准备工作就做好了。</p>

<ul>
<li>第一步，找到<code>DTDeviceKitBase.framework</code>文件所在，不同版本的Xcode这个文件所放的目录都不大一样。下面提供两个参考的路径：</li>
</ul>

<pre><code>Xcode 6以前:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework  
Xcode 6以后(包括):/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework  
</code></pre>

<ul>
<li>第二步，建立命令别名,以Xcode 7为例，运行如下命令：</li>
</ul>

<pre><code class="language-shell">alias symbolicate="/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash -v"  
</code></pre>

<ul>
<li>第三步，更新DEVELOPER_DIR的环境变量，因为如果不跟新的话，在反编译的过程中会出现报错的情况</li>
</ul>

<pre><code class="language-shell">export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"  
</code></pre>

<ul>
<li>第四步，标识化"加密"的崩溃日志，让其变为非地址堆栈的崩溃日志。首先先用cd命令切换到到你准备的<code>.app</code>二进制文件以及<code>dSYM</code>文件所在目录，然后运行如下命令</li>
</ul>

<pre><code class="language-shell">symbolicate -o "反编译后文件名" "需要反编译的文件" "项目名.app"  
</code></pre>

<p>其中反编译后的文件后缀可以用.crash、.txt等文件名，不过我个人喜欢用.crash后缀名，因为可以用系统自带的控制台(Console)来查看，项目名则为你建立项目的时候取的名字。这样你反编译后的文件就产生了，至少崩溃日志是可以看懂了(见文章的第一张图)。</p>

<h3 id="">如何查看崩溃日志</h3>

<p>好了，获得是人类可读语言的崩溃日志后，或者是从别人手机到处崩溃日志后，下一步就是查看了。下面就正对一个程序猿该如何看稍微说说。</p>

<h4 id="">崩溃日志头</h4>

<pre><code>Incident Identifier: 635A20F0-BC79-4724-AE45-D49097085250  
CrashReporter Key:   21a348fcc69b56e9f74e9b0078c8d7bbc0ace04a  
Hardware Model:      xxx  
Process:             crashDemo [3131]  
Path:                /private/var/mobile/Containers/Bundle/Application/B2B0DDAE-2E1B-422E-AA4D-99C2578C99E6/crashDemo.app/crashDemo  
Identifier:          com.demo.crashDemo  
Version:             2443 (1.4.2)  
Code Type:           ARM-64 (Native)  
Parent Process:      launchd [1]  
Date/Time:           2015-11-24 17:57:00.00 -0800  
Launch Time:         2015-11-24 17:56:44.44 -0800  
OS Version:          iOS 9.1 (13B143)  
Report Version:      105  
Exception Type:  EXC_CRASH (SIGABRT)  
Exception Codes: 0x0000000000000000, 0x0000000000000000  
Exception Note:  EXC_CORPSE_NOTIFY  
Triggered by Thread:  0  
</code></pre>

<p>首先正对这个崩溃日志头，程序猿级别的童鞋只要关注几点就好了。
<code>Process</code>:是在<code>Info.plist</code>文件中key为<code>CFBundleExecutable</code>所填写的名字，首先先确认这个，别到时候发现尼玛这不是自己的崩溃日志
<code>Version</code>:这个则是要关注的第二个点，就是这个崩溃日志产生的版本，因为对于中大型公司来说的话，一般有可能多个版本并行的情况，针对崩溃日志一定要看清楚是哪个版本,这个是由<code>Info.plist</code>文件中的<code>CFBundleVersion</code>和<code>CFBundleVersionString</code>组成
<code>OS Version</code>:这个字段则是说我这个崩溃日志是在什么系统上面产生的，iOS 8还是iOS 9？这个总得知道吧，因为有可能你用到了被停用的接口啊，或者是太新的接口在老版本上不兼容啊等问题
'Exception Type':说明崩溃产生的原因，具体的详情可以查看苹果官网 <br>
'Triggered by Thread':这个说明在哪个线程上崩溃的，<strong>这个一定得看</strong>要不然下面一堆堆栈信息完全就不知道看哪个了</p>

<h4 id="">崩溃日志的堆栈信息</h4>

<p>然后就是找到对应的崩溃堆栈信息来说的话，去找对应的崩溃函数,还是用上方的第一张图来举例：</p>

<p><img src="http://www.pluto-y.com/content/images/2015/12/crash--.png" alt="崩溃日志"></p>

<p>其实如果是自己写的代码一眼就能看出来问题所在了，因为能从这个堆栈中找到问题所在。一般这个调用都是从上往下看，最上面的出现你熟悉的代码一般就是问题所在了，如果上图中<code>[JsonUtil dataRequest:Key:Delegate:Info:] (JsonUtil.m:166)</code>一眼就能看出来这个是我的代码，然后我去分析这行代码周围的代码很快就能找到问题所在了。至于其他的就没什么好看了。</p>

<h3 id="">结束语</h3>

<p>其实除了从苹果审核人员那里获得崩溃日志外，我们还可能通过从测试人员的手机里拷贝出来。一般通过iTunes的同步功能就能讲手机中的崩溃日志拷贝到电脑里面来查看（如下图）。如果是Mac的话目录应该是在<code>/Users/chipsea/Library/Logs/CrashReporter/MobileDevice</code>目录下能看到同步的到的崩溃日志，然后根据日志进行修改Bug吧。最后去屎吧八阿哥！</p>

<p><img src="http://www.pluto-y.com/content/images/2015/12/SyncCrashFromiPhone.png" alt="同步按钮"></p>]]></content:encoded></item><item><title><![CDATA[iOS-Echarts------定制性高的图表控件]]></title><description><![CDATA[<h2 id="">我是前言</h2>

<p>最近遇到一个非常严于自律的UI，那酸爽-------几个像素都要求的非常以及相当的严格。为此本王差点都跟她打起来了(开玩笑的，我这么绅士怎么可能打女生呢)。而在App中需要显示数据趋势什么的，那UI对界面要求，线条粗细，字体，字体大小，颜色，透明度各种各种。那时候我内心是崩溃的！
<img src="http://www.pluto-y.com/content/images/2015/11/1448351208_650x456-png-460-2.png?imageView/2/w/619/q/90" alt="崩溃图"></p>

<p>以我对她的了解来说如果她不满意随时都可能对图表进行改动她所谓的小改动。于是我开始踏山涉水，走亲访友寻觅着我的梦中图表。可是走了一全我感觉定制性都不够啊。
没办法，最后找来找去可能只能通过H5的画出来的图表效果什么的可能会好一点。最后将目光锁定在百度的Echarts的控件上来。</p>

<h2 id="echarts">Echarts</h2>

<p>关于这个H5的图表有多可控我就不进行累述的，具体的内容大家可以参考<a href="http://echarts.baidu.com/">Echart官网</a>进行查看。其实选这个还有一个目的就是，这个整体的空间还有一家大公司来维护，总感觉爽爽的。
然后我今开始了我的封装之旅，经过了几百年的封装，终于弄出了一个稍微可以用的控件，又兴趣的童鞋可以拿来用用或者研究下。不过还有好多内容还有没有封装好。具体的下载地址可以通过Github上来下载:<a href="https://github.com/Pluto-Y/iOS-Echarts">https://github.com/Pluto-Y/iOS-Echarts</a></p>

<h2 id="">如何使用</h2>

<p>有两种方法可以使用：</p>

<ul>
<li>通过<code>cocoapods</code>进行导入：<code>pod 'iOS-Echarts'</code></li>
<li>将项目下的<code>iOS-Echarts</code></li></ul>]]></description><link>http://www.pluto-y.com/ios-echarts-github/</link><guid isPermaLink="false">e3a642de-36eb-47e9-a41b-e78e7e38edfd</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Sat, 28 Nov 2015 16:39:35 GMT</pubDate><content:encoded><![CDATA[<h2 id="">我是前言</h2>

<p>最近遇到一个非常严于自律的UI，那酸爽-------几个像素都要求的非常以及相当的严格。为此本王差点都跟她打起来了(开玩笑的，我这么绅士怎么可能打女生呢)。而在App中需要显示数据趋势什么的，那UI对界面要求，线条粗细，字体，字体大小，颜色，透明度各种各种。那时候我内心是崩溃的！
<img src="http://www.pluto-y.com/content/images/2015/11/1448351208_650x456-png-460-2.png?imageView/2/w/619/q/90" alt="崩溃图"></p>

<p>以我对她的了解来说如果她不满意随时都可能对图表进行改动她所谓的小改动。于是我开始踏山涉水，走亲访友寻觅着我的梦中图表。可是走了一全我感觉定制性都不够啊。
没办法，最后找来找去可能只能通过H5的画出来的图表效果什么的可能会好一点。最后将目光锁定在百度的Echarts的控件上来。</p>

<h2 id="echarts">Echarts</h2>

<p>关于这个H5的图表有多可控我就不进行累述的，具体的内容大家可以参考<a href="http://echarts.baidu.com/">Echart官网</a>进行查看。其实选这个还有一个目的就是，这个整体的空间还有一家大公司来维护，总感觉爽爽的。
然后我今开始了我的封装之旅，经过了几百年的封装，终于弄出了一个稍微可以用的控件，又兴趣的童鞋可以拿来用用或者研究下。不过还有好多内容还有没有封装好。具体的下载地址可以通过Github上来下载:<a href="https://github.com/Pluto-Y/iOS-Echarts">https://github.com/Pluto-Y/iOS-Echarts</a></p>

<h2 id="">如何使用</h2>

<p>有两种方法可以使用：</p>

<ul>
<li>通过<code>cocoapods</code>进行导入：<code>pod 'iOS-Echarts'</code></li>
<li>将项目下的<code>iOS-Echarts</code>目录拷贝到项目中</li>
</ul>

<p>在需要使用的地方引入<code>PYEchartsView.h</code>和<code>PYOption.h</code>。
最后只需通过对图表的属性进行配置即可。</p>

<p>至于其中的参数与文档可以参考上方的<a href="http://echarts.baidu.com/">Echart官网</a>进行查看与配置。</p>

<h3 id="">举个栗子</h3>

<p>像我们的话一般来说就是一套UI对应一套对应的折线图、柱状图以及饼图。所以一般我只需在更换UI的情况下进行修改这个控件的初始化即可。比如这个版本里面我们对于界面需求就只需通过下面的代码进行对折线图进行初始化即可：</p>

<pre><code class="language-objectivec">#pragma mark 初始化图表
-(void)initEchartOption {
    option = [[PYOption alloc] init];
    option.calculable = NO;
    option.toolbox = [[PYToolbox alloc] init];
    // 坐标轴属性
    PYGrid *grid = [[PYGrid alloc] init];
    grid.x = @(-2);
    grid.y = @(0);
    grid.x2 = @(0);
    grid.y2 = @(30);
    grid.borderWidth = @(0);
    option.grid = grid;
    // X轴的属性
    xAxis = [[PYAxis alloc] init];
    xAxis.show = YES;
    xAxis.type = @"category";
    xAxis.boundaryGap = @(YES);
    xAxis.splitLine = [[PYSplitLine alloc] init];
    xAxis.splitLine.show = NO;
    xAxis.axisTick = [[PYAxisTick alloc] init];
    xAxis.axisTick.show = NO;
    xAxis.axisLine = [[PYAxisLine alloc] init];
    xAxis.axisLine.show = NO;
    xAxis.axisLabel = [[PYAxisLabel alloc] init];
    xAxis.axisLabel.textStyle = [[PYTextStyle alloc] init];
    xAxis.axisLabel.textStyle.color = PYRGBA(255, 255, 255, 1);
    option.xAxis = [[NSMutableArray alloc] initWithObjects:xAxis, nil];
    // Y轴的属性
    yAxis = [[PYAxis alloc] init];
    yAxis.type = @"value";
    yAxis.show = NO;
    yAxis.splitLine = [[PYSplitLine alloc] init];
    yAxis.splitLine.show = NO;
    yAxis.axisLine.show = NO;
    yAxis.axisTick = [[PYAxisTick alloc] init];
    yAxis.axisTick.show = NO;
    option.yAxis = [[NSMutableArray alloc] initWithObjects:yAxis, nil];
    // 折线图的线的属性
    NSMutableArray *serieses = [[NSMutableArray alloc] init];
    series = [[PYCartesianSeries alloc] init];
    series.symbol = @"image://trend_dot@3x.png";
    series.symbolSize = @(4);
    series.type = @"line";
    series.smooth = YES;
    series.showAllSymbol = YES;
    series.itemStyle = [[PYItemStyle alloc] init];
    series.itemStyle.normal = [[PYItemStyleProp alloc] init];
    series.itemStyle.normal.lineStyle = [[PYLineStyle alloc] init];
    series.itemStyle.normal.lineStyle.color = PYRGBA(255, 255, 255, 1);
    series.itemStyle.normal.lineStyle.width = @(1);
    int barWidth = [UIScreen mainScreen].bounds.size.width / 10;
    series.barWidth = @(barWidth);
    [serieses addObject:series];
    [option setSeries:serieses];
}
</code></pre>

<p>这里是针对对于折线图进行大体来说进行属性和展示的初始化。对于其中的数据的具体显示的属性，可以根据具体动态的情况下进行不同的情况进行不同的展示。比如展示的最大值、最小值的特别标示，比如平均的辅助线以及点击事件等等等。具体可以根据具体需求进行实现。而我项目中的实现效果如下：</p>

<p><img src="http://www.pluto-y.com/content/images/2015/11/-----1-1.png" alt="效果图"></p>

<h2 id="">打个广告</h2>

<p>由于还有好多内容没有封装，最近也有点忙，想说有没有人希望一起来封装的。有兴趣的童鞋可以微博联系我(<a href="http://weibo.com/5690716723/info">@叫什么都不如叫Pluto-Y</a>)，大家一起成长起来。</p>]]></content:encoded></item><item><title><![CDATA[WWDC2015 - Interface Builder技巧(IB 技巧)]]></title><description><![CDATA[<p>这篇文章可能对于IB控的童鞋了来说可能是一个福音，而其中的技巧都是相当有用的。如果觉得好的童鞋可以进行留言点个赞什么的，要不然实在佩服的情况下可以跪下唱征服进行膜拜朕，朕会在将来整理更多的好东西给大家。关于IB的技巧，这里就说说在WWDC15上提到的几个技巧吧，可能在以前有些技巧也是被大家所知道的，可是这里主要是给大家整理一下。方便大家在使用IB的时候加快布局的速度。如果对于英文还不错的童鞋可以去查看视频:
<a href="https://developer.apple.com/videos/play/wwdc2015-407/">https://developer.apple.com/videos/play/wwdc2015-407/</a>
废话不多说，直接开始那个技巧吧。</p>

<h3 id="stackview">技能点一:多使用<code>Stack View</code></h3>

<p>关于iOS9中新添加的API，<code>Stack View</code>是iOS9布局方面非常重要的一个工具。对于布局来说是非常有帮助的。他能快速的帮你布局，并且维护你的布局。而在IB中，关于StackView也提供了非常好用的快捷键。具体如下：
<img src="http://img.blog.csdn.net/20151018174100619" alt="Stack View">
具体分为两个步骤:
* 1. 选择需要所有需要添加到<code>Stack View</code>的视图
* 2. 点击IB右下方最左边的按钮即可将所选的视图添加到<code>Stack View</code>中</p>

<blockquote>
  <p>而如果对<code>Stack View</code>不熟的同学可以通过查看博主上一篇文章<a href="http://www.pluto-y.com/wwdc-2015-mystries-of-auto-layout/">解开AutoLayout的面纱</a></p></blockquote>]]></description><link>http://www.pluto-y.com/interface-builder-tips/</link><guid isPermaLink="false">44d0dae3-317a-4700-85b7-a7ec6f486ad1</guid><category><![CDATA[iOS]]></category><category><![CDATA[AutoLayout]]></category><category><![CDATA[Layout]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Sun, 18 Oct 2015 18:38:32 GMT</pubDate><content:encoded><![CDATA[<p>这篇文章可能对于IB控的童鞋了来说可能是一个福音，而其中的技巧都是相当有用的。如果觉得好的童鞋可以进行留言点个赞什么的，要不然实在佩服的情况下可以跪下唱征服进行膜拜朕，朕会在将来整理更多的好东西给大家。关于IB的技巧，这里就说说在WWDC15上提到的几个技巧吧，可能在以前有些技巧也是被大家所知道的，可是这里主要是给大家整理一下。方便大家在使用IB的时候加快布局的速度。如果对于英文还不错的童鞋可以去查看视频:
<a href="https://developer.apple.com/videos/play/wwdc2015-407/">https://developer.apple.com/videos/play/wwdc2015-407/</a>
废话不多说，直接开始那个技巧吧。</p>

<h3 id="stackview">技能点一:多使用<code>Stack View</code></h3>

<p>关于iOS9中新添加的API，<code>Stack View</code>是iOS9布局方面非常重要的一个工具。对于布局来说是非常有帮助的。他能快速的帮你布局，并且维护你的布局。而在IB中，关于StackView也提供了非常好用的快捷键。具体如下：
<img src="http://img.blog.csdn.net/20151018174100619" alt="Stack View">
具体分为两个步骤:
* 1. 选择需要所有需要添加到<code>Stack View</code>的视图
* 2. 点击IB右下方最左边的按钮即可将所选的视图添加到<code>Stack View</code>中</p>

<blockquote>
  <p>而如果对<code>Stack View</code>不熟的同学可以通过查看博主上一篇文章<a href="http://www.pluto-y.com/wwdc-2015-mystries-of-auto-layout/">解开AutoLayout的面纱</a>中找寻答案。</p>
</blockquote>

<p>而在WWDC15中建议多使用<code>Stack View</code>，主要是<code>Stack View</code>方便IB进行维护和扩展。
而在github上也有童鞋已经写了<a href="https://github.com/forkingdog/FDStackView">FDStackView</a>用来保证在低版本的情况下也能使用<code>StackView</code>，有兴趣的同学可以去研究一下。</p>

<h3 id="pinconstraintomarginsupdateframes">技能点二：善用Pin中的Constrain To Margins和Update Frames</h3>

<p>首先先看看这两个选项在哪吧:
<img src="http://img.blog.csdn.net/20151018180335206" alt="Pin">
可以看到这两个技能点都在IB中右下角按钮中的才当中。而这两个选项又有什么用处呢？
* Constrain To Margins
关于什么是Margins，即在一个<code>View</code>的四周都有一定的范围属于这个Margin的方位，而如果对html属性的同学可以明确的指导其实这里所指的Margins其实说的是padding，而对于iOS来说所谓的Margin是有一个默认值的，上下左右均为8个单位。即如果你添加了约束是针对Margins的话，如果为0的情况下，其实对于View来说的话就是<code>Leading to Space</code>为8的情况（可以通过上图上看到如果uncheck的情况下数值都变大了8）。而在WWDC15中建议使用<code>Contrain To Margins</code>，因为这种情况下视图会更具不同设备或视图布局的情况下进行自动适配。然而对于需要一张<code>UIImageView</code>当做背景图等类似情况下，官方还是建议还是使用与Leading等约束进行布局。
* Update Frames
而关于这点来说的话，其实这个按钮就是用于当添加约束后是否根据约束来重新对试图进行计算Frame并且对其位置与大小进行调整，这里面有两个选项，一个是<code>Item Of New Contrains</code>和<code>All Frame in Container</code>，对于前者来说的话就是是否更新添加约束的控件，后者则是除了更新该空间以外是否还需要更新其子控件。而对于IB控来说，对这个来说还是很有必要的，否则我们每次都需要手动再去选择更新frame。</p>

<h3 id="">技能点三：快速选择所需的视图</h3>

<p>在使用IB的适合，肯定有很多童鞋对于很多父视图子视图的情况下想要选择自己所需的铜件是一件特别苦恼的事情。可能很多童鞋首选的就是通过<code>Jump Bar</code>或者通过<code>Outline View</code>来进行选择自己所需的View，而在WWDC15上官方的开发人员又提供了一个更方便的方式，具体如下图：
<img src="http://img.blog.csdn.net/20151018183001437" alt="Select View">
其实这种操作非常简单，只要将鼠标放到所需选择然后按下<code>Alt + Shift + 鼠标右键</code>即可出现上图效果，然后再进行选择到自己所需要的视图。</p>

<h3 id="editorcanvas">技能点四：善于使用Editor -> Canvas菜单</h3>

<p><img src="http://img.blog.csdn.net/20151018183902662" alt="Editor->Canvas">
关于这个的话，大家多尝试里面的菜单。关于里面每个选项有什么作用，博主就不在这里叙述了，让大家多去尝试一下里面的菜单。这里面的菜单对于布局来说都是非常有帮助的（远处飘来：放屁，你明明自己也不会！）好吧，那位小伙伴尝试完之后码放告诉楼主。</p>

<h3 id="dynamictype">技能点五：Dynamic Type</h3>

<p>如果希望自己的字体在用户修改系统字体大小时候的进行变化的可以则可以使用IB中提供的动态类型的字体。而具体的字体包括一下几种：
* UIFontTextStyleHeadline
* UIFontTextStyleBody
* UIFontTextStyleSubheadline
* UIFontTextStyleFootnote
* UIFontTextStyleCaption1
* UIFontTextStyleCaption2
而在IB中可以通过一下方式来进行设置：
<img src="http://img.blog.csdn.net/20151018185349074" alt="Dynamic Type">
设置好后，用户可以通过设置里面进行设置你所在App中,关于这些字体的大小。</p>

<h3 id="designableinspectable">技能点六：Designable和Inspectable</h3>

<p>关于这个技巧对于IB控来说真的是一个大大的福利啊。对于IB控来说的话，希望所有关于布局的操作都能在IB中体现和实现。而这两个的利用真的能对IB控来说心里飘过一个字，爽！两个字，好爽！三个字，超级爽！
其实对于怎么用对于很多IB控来说应该很熟悉了，这里主要是为那些不懂怎么用的人稍微说说怎么用吧。
其实很简单，就是在自定义的UIView中对应的地方添加<code>IB_DESIGNABLE</code>和<code>IBInspectable</code>即可(Swift中用<code>@IBDesignable</code>和<code>@IBInspectable</code>)。看看Demo如何实现。
首先显示CustomView.h的文件：</p>

<pre><code class="language-objectivec">IB_DESIGNABLE  
@interface CustomView : UIView

@property (retain, nonatomic) IBInspectable UIColor *bgColor;

@end
</code></pre>

<p>然后是CustomView.m的文件:</p>

<pre><code class="language-objectivec">@implementation CustomView

-(void)setBgColor:(UIColor *)bgColor {
    _bgColor = bgColor;
    self.backgroundColor = bgColor;
}

@end
</code></pre>

<p>具体实现效果为：
<img src="http://img.blog.csdn.net/20151019002204964" alt="Designable And Inspector">
实现效果则为在右边的配置项中多了一个Bg Color的配置项可以配置<code>CustomView</code>的bgColor的属性，如此可说明，只需要在所需在IB中配置的属性上添加<code>IBInspectable</code>即可。而可通过上图查看到修改后能在IB中直接查看到具体的变化，则只需在CustomView的上方添加<code>IB_DESIGNABLE</code>即可实现在修改属性的时候会IB中查看到变化后的展示。</p>

<h3 id="hud">技能点七：Hud(视图导航器)</h3>

<p>可能很多人对这个都不大熟知，其实说到这个就不得不提<code>Assistant Editor</code>。对于这个大家应该就不陌生了吧。其实这里所指的Hud就是为了方便打开这个或者是打开新的<code>Window</code>而产生的。具体使用为选定某个文件，然后点击<code>Shift + Alt + 鼠标左键</code>(点击所需文件)即可，具体效果如下图：
<img src="http://img.blog.csdn.net/20151019014255501" alt="Hud">
可以看到图中除了可以创建新的<code>Assistant Editor</code>以外，还可以创建Window和Tab，具体产生的效果大家可以自己做做实验。
除了这种形式产生以外，其实还有一个快捷键为<code>Cmd + J</code>，该快捷键产生的效果可能没有上面所述的功能多，该快捷键主要用于创建和切换<code>Assitant Editor</code>。而上面的Hud主要用于将所点击的文件在<code>Assistant Editor</code>、<code>Tab</code>或<code>Window</code>中展示。</p>

<h3 id="simulatedmetrics">技能点八：Simulated Metrics</h3>

<p>关于这个技能点，主要适用于在布局的时候模拟多台设备类型或者一些其他情况的条件下，我们的布局的变化。具体位置如下图：
<img src="http://img.blog.csdn.net/20151019015226662" alt="Simulated Metrics">
除了设备类型以外，还有其他的如设备方向，导航栏情况等配置项，善于利用这个可以更方便我们查看不同情况下我们的布局。具体的配置项就让大家自己多去尝试了。
说到这里可能还有一个技能点需要提到的是，如何在IB中一次性查看多种尺寸类型情况下我们所布局的情况。当然就是用Xcode中的<code>Preview</code>的功能。具体如下图：
<img src="http://img.blog.csdn.net/20151019015637174" alt="Preview1">
<img src="http://img.blog.csdn.net/20151019015826746" alt="Preview2"></p>

<h3 id="storyboard">技能点九：分离StoryBoard</h3>

<p>关于这个技能点非常好用，即在一个StoryBoard上可能存在很多从rootView通过各种跳转而存在的多种分支View的情况。而对于一个StoryBoar上存在这么多的分支和视图其实是非常不方便管理的。而在WWDC15中提到一个可以分离StoryBoard上的分支的情况其实是非常有用的。不废话了，具体看如何做的吧。
<img src="http://img.blog.csdn.net/20151019021043300" alt="Seperate Storyboard">
其实步骤很简单，首先先选择所需分离的ViewController，然后点击<code>Editor</code>菜单，选择<code>Refector to Storyboard...</code>,然后给新的storyboard进行命名即可。上图所展示的是分离一个ViewController，其实除了能分离一个以外，还可以分离多个，只需通过选上多个ViewController然后进行分离即可。
其实一看到这里还用到了<code>Storyboard Reference</code>，其实我们平常在布局的情况下也可以通过IB中添加对应的<code>Storyboard Reference</code>即可。</p>

<h3 id="sizeclasses">技能点十：使用属性的Size Classes</h3>

<p>关于<code>Size Classes</code>不懂的童鞋可以资料，网络上有非常多的资料。而这里主要说的是如何设置属性的<code>Size Classes</code>，其实很多童鞋都已经知道了，或者细心的童鞋以及发现了这个特性。下面就以<code>Stack View</code>来做展示具体的设置方式如下：
<img src="http://img.blog.csdn.net/20151019023140295" alt="Property Size Classes"></p>

<h3 id="storyboarapi">技能点十一：多用关于Storyboar的Api</h3>

<p>关于Storyboard的Api这里给大家罗列一下，主要包括一下几个：</p>

<pre><code class="language-swift">UIViewController:  
    func prepareForSegue(_:sender:)
    func performSegueWithIdentifier(_:sender:)
    func shouldPerformSegueWithIdentifier(_:sender:) -&gt; Bool
    func unwindForSegue(_:towardsViewController:)
UIStoryboardSegue:  
   func perform()
</code></pre>

<p>对应OC的代码为：</p>

<pre><code class="language-objectivec">UIViewController:  
- (void)prepareForSegue:(UIStoryboardSegue *)segue
                 sender:(id)sender;
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier
                                  sender:(id)sender;
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier
                                  sender:(id)sender;
- (void)unwindForSegue:(UIStoryboardSegue *)unwindSegue
 towardsViewController:(UIViewController *)subsequentVC;
UIStoryboardSegue:  
- (void)perform;
</code></pre>

<p>具体的使用方法大家就去查查Api的文档吧，本文就不加以累述了。</p>

<h3 id="">总结</h3>

<p>对于想博主这种IB控来说，对于IB中的技巧真的是希望掌握越多越好。可是以前总是在摸索，收获一个技巧点就使用一个技巧点，而对于系统的整理也没有想过。这次WWDC15上给我们这些IB控整理和提供了这么多技巧对于之后我们的布局效率来说可以说是真的大大滴提升啊。正好最近公司的项目也开始了新UI的布局。马上就要开始用这些技巧了，想想就鸡冻。</p>]]></content:encoded></item><item><title><![CDATA[WWDC 2015 - 揭开AutoLayout的神秘面纱(Mysteries Of Auto Layout)]]></title><description><![CDATA[<p>在WWDC 2015上关于AutoLayout这堂课上为程序员提供了一些福利般的新特性，并且对于一些技巧性的使用技能，对于程序猿在开发工作中的布局以及布局的调试打了许多鸡血啊，碰巧博主最近的项目也赶上了UI的替换，搞得博主都想现在就在项目中大展手脚的感觉。想想还有点小激动呢。
<img src="http://wanzao2.b0.upaiyun.com/system/pictures/16/original/15.png" alt="激动状"></p>

<p>在WWDC 2015大会上，讲述了12个关于Auto Layout的技能点。如果英文不错的童鞋可以去查看WWDC上的视频:
* <a href="https://developer.apple.com/videos/play/wwdc2015-218/">Mysteries Of Auto Layout Part1</a>
* <a href="https://developer.apple.com/videos/play/wwdc2015-219/">Mysteries Of Auto Layout Part2</a></p>

<p>各位童鞋，想知道这十二个技能点么？你特么来求我啊，来求我啊。求我我就告诉你。开始吧，跪下唱征服。</p>

<h3 id="stackview">技能点一：Stack View(新)</h3>

<p><code>Stack View</code>是在iOS 9中推出的一个新的视图，对于程序猿来说是大大滴福利啊，不仅能使xib的可维护性增加而且由于其是轻量级的控件，也能保证程序运行流畅。其实这个视图有点像是Android里面的线性布局，不过它比线性布局好用，主要表现在他的属性方面。其中最常用的属性有4个，具体如下图：</p>

<p><img src="http://img.blog.csdn.net/20151013091731638" alt="属性截图">
* Axis(或Orientation在 OS X中):用于指明是水平线性布局还是垂直线性布局</p>]]></description><link>http://www.pluto-y.com/wwdc-2015-mystries-of-auto-layout/</link><guid isPermaLink="false">d90c11a8-f55f-41e6-bcf2-aaa9714dbfc4</guid><category><![CDATA[WWDC]]></category><category><![CDATA[WWDC2015]]></category><category><![CDATA[Layout]]></category><category><![CDATA[iOS]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Tue, 13 Oct 2015 04:55:12 GMT</pubDate><content:encoded><![CDATA[<p>在WWDC 2015上关于AutoLayout这堂课上为程序员提供了一些福利般的新特性，并且对于一些技巧性的使用技能，对于程序猿在开发工作中的布局以及布局的调试打了许多鸡血啊，碰巧博主最近的项目也赶上了UI的替换，搞得博主都想现在就在项目中大展手脚的感觉。想想还有点小激动呢。
<img src="http://wanzao2.b0.upaiyun.com/system/pictures/16/original/15.png" alt="激动状"></p>

<p>在WWDC 2015大会上，讲述了12个关于Auto Layout的技能点。如果英文不错的童鞋可以去查看WWDC上的视频:
* <a href="https://developer.apple.com/videos/play/wwdc2015-218/">Mysteries Of Auto Layout Part1</a>
* <a href="https://developer.apple.com/videos/play/wwdc2015-219/">Mysteries Of Auto Layout Part2</a></p>

<p>各位童鞋，想知道这十二个技能点么？你特么来求我啊，来求我啊。求我我就告诉你。开始吧，跪下唱征服。</p>

<h3 id="stackview">技能点一：Stack View(新)</h3>

<p><code>Stack View</code>是在iOS 9中推出的一个新的视图，对于程序猿来说是大大滴福利啊，不仅能使xib的可维护性增加而且由于其是轻量级的控件，也能保证程序运行流畅。其实这个视图有点像是Android里面的线性布局，不过它比线性布局好用，主要表现在他的属性方面。其中最常用的属性有4个，具体如下图：</p>

<p><img src="http://img.blog.csdn.net/20151013091731638" alt="属性截图">
* Axis(或Orientation在 OS X中):用于指明是水平线性布局还是垂直线性布局
* Alignment:用于指定<code>Stack View</code>中子视图对其方式
* Distribution:用于指定子视图展示方式
* Spacing:用于指定子视图之间的间距
文字叙述可能有点繁琐，接下来看看苹果官方给我们提供的截图：
<img src="http://img.blog.csdn.net/20151013092105910" alt="Alignment">
<img src="http://img.blog.csdn.net/20151013092136250" alt="Distribution">
通过修改其中的属性可以将<code>Stack View</code>中的子视图的布局进行自动排布(如果将线性布局从<code>Horizontal</code>修改成<code>Vertical</code>)，而不需要通过修改Constraint进行修改布局。
另外在<code>Interface Build</code>中提供了快速将几个视图添加到<code>Stack View</code>的快捷按钮,只需通过选中所有的视图，并且点击该按钮即可。
如下图所示：
<img src="http://img.blog.csdn.net/20151013092237788" alt="Operation Steps">
不仅能方便的将一些视图添加到<code>Stack View</code>中，并且还有两个神奇的店，一是在已经建立好的的<code>Stack View</code>可以插入新的视图，而不需要代码或者布局的调整，<code>Stack View</code>会自动调整子视图的布局；二是<code>Stack View</code>中如果将子视图进行隐藏的话，剩下的子视图会自动重新布局，如果加入动画的话，视图也会显示的非常友好和完美。而至于剩下的部分，就由小伙伴们自行探索了，我就不在这里讲解了。不理解的童鞋可以留言。</p>

<h3 id="constraints">技能点二：修改约束(Constraints)</h3>

<p>在WWDC2015大会上，官方里面是希望我们在修改约束的时候不需要在用Add或者Remove的方法进行修改约束，而是通过<code>NSLayoutConstraint</code>中的<code>activateConstraints</code>和<code>deactivateConstraints</code>的方法进行修改约束，而通过该方法可以使您的程序运行更加流畅。</p>

<blockquote>
  <p>Note: 在用永远不要去调用<code>[NSLayoutConstraint deactivateConstraints:self.view.constraints];</code>方法，因为self.view中的约束不一定都是自己的约束，有可能会包含其他控件的约束。如果调用该方法可能会导致布局错乱。</p>
</blockquote>

<p>而对于<code>activateConstraints</code>和<code>deactivateConstraints</code>方法来说同样可以添加到动画中，即可以通过Animations来缓慢的进行变化。
下面的做一个Demo试试看吧，首先进行简单的布局，并且先为视图初始化好基础的布局，并创建好对应的contraints。将视图在上方的约束都存在变量<code>_topConstrains</code>中，而将视图移动到下方的约束都存在<code>_bottomConstrains</code>，并给ViewController上添加一个点击事件，当点击时则将视图上下移动切换。而切换的代码如下：</p>

<pre><code class="language-objectivec">/// self.view的点击事件
-(IBAction)kViewClick:(id)sender {
    if ([_bottomConstrains.firstObject isActive]) {
        [UIView animateWithDuration:3 animations:^{
            [NSLayoutConstraint activateConstraints:_topConstrains];
            [NSLayoutConstraint deactivateConstraints:_bottomConstrains];
            [self.view layoutIfNeeded];
        }];
    } else {
        [UIView animateWithDuration:3 animations:^{
            [NSLayoutConstraint deactivateConstraints:_topConstrains];
            [NSLayoutConstraint activateConstraints:_bottomConstrains];
            [self.view layoutIfNeeded];
        }];
    }
}
</code></pre>

<p>运行效果如下：
<img src="http://img.blog.csdn.net/20151013100811651" alt="运行效果"></p>

<h3 id="intrinsiccontentsize">技能点三：intrinsicContentSize</h3>

<p>对于这个博主暂时想不到这个技能点用什么卵用，不过既然WWDC上提到了，博主就稍微总结一下吧。博主查了一下，对于这个属性是在<code>UIView (UIConstraintBasedLayoutLayering)</code>中声明的，而根据WWDC上的说法是有一些类的实例（如<code>UILabel</code>和<code>UIImage</code>）是有这个属性，这个博主就有点搞不清楚了。不过对于这个属性来说的话，其指的是真实内容的大小,而不是控件的大小，如下图中的<code>UILabel</code>,控件的大小为蓝色区域的大小，而<code>intrinsicContentSize</code>则是红色框左右的大小，其大小不跟随空间的大小而进行改变。如果用该大小去指定Label的话其实应该是最适合的大小，Label内部则不会有多余的空间。
<img src="http://img.blog.csdn.net/20151013123437669" alt="intrinsicContentSize"></p>

<p>另外对于<code>intrinsicContentSize</code>是由系统产生的，而对于这个属性来说，如果在一些特殊的情况下要使用到这个变量的话（如要重写绘制控件方法时），程序员必须记得要对这个属性进行重写。至于其他的内容博主就不是很懂了，如果有哪位同学对这个属性比较了解其用法的话，麻烦留言给我。小生在此谢过了。</p>

<blockquote>
  <p>注意:要充分了解该方法所指代的内容，因为之后的技能点五中所提到的<code>Content Hugging Priority</code>与<code>Content Compression Resistance Priority</code>是主要是针对该大小来说明的。</p>
</blockquote>

<h3 id="uitableviewcell">技能点四：自适应的UITableViewCell</h3>

<p>对于那些做过空间形式的留言或者是其他要求UITableViewCell的童鞋来说，要根据内容来<code>Cell</code>高度是多么痛苦的一件事。记得当初笔者做一个类似朋友圈的功能，要做评论点赞图片等合为一体的<code>Cell</code>，博主就高度这个Boss就整了好久才搞定的，没办法，小生不才，才刚出道。
而在这次的WWDC中，官方提供的技巧中就根据内容来调整<code>Cell</code>的高度做了优化和讲解，让根据内容来调整高度变得异常的简单。
然后其实在WWDC之前网上已经有了关于如何产生自适应的<code>UITableViewCell</code>的教程了，不过这次WWDC可能是起到总结的作用。具体的做法如下：
首先先正确适配<code>Cell</code>中的约束，然后再需要变化的内容又或者说跟需要其内容进行调整高度的空间上(可能是一个或两个)进行添加<code>Top Space to Container Margin</code>以及<code>Bottom Space to Container Margin</code>，而这里的Container一般就是<code>UITableViewCell</code>中的<code>contentView</code>。添加好约束之后只需在代码中添加<code>tableview.estimatedRowHeight = 60;tableview.rowHeight = UITableViewAutomaticDimension;</code>这两句代码即可，其中第一句设置<code>Tableview</code>的Cell的估计高度。而第二句话也是必须的。
如果还是不懂如何进行设置的童鞋可以通过留言或者在微博上<a href="http://weibo.com/5690716723/info">@叫什么都不如叫Pluto-Y</a>找我索要Demo的代码。</p>

<h3 id="priority">技能点五:善于用优先级(Priority)</h3>

<p>关于优先级，此处所说的优先级主要是针对的是<code>Content Hugging Priority</code>和<code>Content Compression Resistance Priority</code>,通过利用这两个优先级可以让控件的大小是否被拉伸或者压缩。
其实从名字来上看就知道了<code>Content Hugging Priority</code>是保证不被拉伸，而<code>Content Compression Resistance Priority</code>主要是为了保证其不被压缩，当然这里的拉伸和压缩都是相对于技能点三种的<code>intrinsicContentSize</code>来说的。简单来说的话，<code>Content Hugging Priority</code>是用来阻止其变大的，如果用表达式来表示的话，该优先级的作用是用来使<code>view.size &lt;= max(intrinsicContentSize, view.frame.size)</code>。相反的，<code>Content Compressiong Resistance Priority</code>是用来阻止其变小的，同样的用表达式来表示的话就是<code>view.size &gt;= min(intrinsicContentSize, view.frame.size)</code>，值得注意的是view.size指的是通过改变约束后控件的Size，而view.frame.size则是。而经过博主测试后发现如果在Contraints约束下且Priority为默认值的时候，可以分为两种情况:</p>

<ul>
<li>如果<code>intrinsicContentSize &gt; view.frame.size</code>的情况下<code>Content Compression Resistance Priority</code>才会请作用。</li>
<li>如果<code>intrinsicContentSize &lt; view.frame.size</code>的情况下<code>Content Hugging Priority</code>才会请作用。</li>
</ul>

<p>可以查看下图的Demo
情况1：
<img src="http://img.blog.csdn.net/20151013123523461" alt="Situation A">
PS:图中的Label的水平方向上的<code>Content Hugging Priority = 251</code></p>

<p>当<code>view.frame.size.width &gt; intrinsicContentSize.width</code>的情况下，设置<code>Trailing Space</code>的优先级小于<code>Content Hugging Priority</code>的情况下会使<code>view.size.width = intrinsicContentSize.width</code>，而即使<code>Content Compression Resistance Priority</code>设置成多大都没用。
情况2：
<img src="http://img.blog.csdn.net/20151013123552594" alt="Situation B">
PS:图中的Label在水平方向上的<code>Content Compression Resistance Priority = 750</code></p>

<p>当当<code>view.frame.size.width &lt; intrinsicContentSize.width</code>的情况下，设置<code>Trailing Space</code>的优先级小于<code>Content Compression Resistance Priority</code>的情况下会使<code>view.size.width = intrinsicContentSize.width</code>，而即使<code>Content Hugging Priority</code>设置成多大都没用。</p>

<p>而在WWDC15上举得例子是另一个，而在我的Demo代码中也有实现。</p>

<h3 id="alignment">技能点六：对齐方式(Alignment)</h3>

<p>关于对齐方式在WWDC15上主要提到的就是两种方式需要注意，接下来我就用两点来给大家说说吧。</p>

<ul>
<li>关于BaseLine</li>
</ul>

<p>关于BaseLine来说除了之前系统中就有就有的<code>Baseline</code>对齐来说，在iOS9中还添加了<code>firstBaseline</code>和<code>lastBaseline</code>的对齐，而这两个的对齐方式主要是针对文本的对其，例如Label、Button之类的控件。而想要知道这之间又是面区别的话，就看下面的两个简单的Demo:
第一个Demo是让一个Label的firstBaseline等于Button的firstBaseline，具体代码和效果图如下：</p>

<pre><code class="language-objectivec">    //_kBaseLineLabel是左边的Label，_kBaseLineBtn是右边的Button
    NSLayoutConstraint *kContrains = [_kBaseLineLabel.firstBaselineAnchor constraintEqualToAnchor:_kBaseLineBtn.firstBaselineAnchor];
    [NSLayoutConstraint activateConstraints:@[kContrains]];
</code></pre>

<p>效果图如下:
<img src="http://img.blog.csdn.net/20151013123616776" alt="Alignment效果图1">
效果就是Label的第一行文字的下端与Button文字的下端对其。</p>

<p>第二个Demo是让Label的lastBaseline等于Button的lastBaseline，具体代码和效果图如下：</p>

<pre><code class="language-objectivec">    //_kBaseLineLabel是左边的Label，_kBaseLineBtn是右边的Button
    NSLayoutConstraint *kContrains = [_kBaseLineLabel.lastBaselineAnchor constraintEqualToAnchor:_kBaseLineBtn.lastBaselineAnchor];
    [NSLayoutConstraint activateConstraints:@[kContrains]];
</code></pre>

<p>效果图如下：
<img src="http://img.blog.csdn.net/20151013123642127" alt="Alignment效果图2">
效果就是Label的最后一行文字的下端与Button文字的下端对其。而不管Label怎么变化，都会保证这两个控件文字的下端对其。这个在很多场景中都能用到。</p>

<ul>
<li>关于Leading和Trailing</li>
</ul>

<p>关于这点很多人都会觉得Leading就是左对齐，而Trailing就是右对齐。这也是为什么博主不直接下左对齐右对齐而是写Leading和Trailing的意思。对于中文中当然Leading可以认为是左对齐，同理Trailing。毕竟中文是从左往右读的习惯。然后对于一些国家来说，他们的习惯是从右往左读，那么这么来说的话Leading就是右对齐，而Trailing就是左对齐了。所以WWDC中建议一定要用Leading和Trailing而不是用左对齐右对齐。当然在国内也有人用前边后边来表示Leading和Trailing，不过博主还是觉得没有Leading和Trailing来说更加能体现其中的精髓。</p>

<p>到此为止Part1就好了。接下来就准备开始说Part2的内容了。</p>

<h3 id="layout">技能点七：Layout的周期</h3>

<p>关于布局周期这里主要是针对修改布局的情况下，其实主要就分为以下三个步骤，并且这三个步骤之间形成一个循环。具体如下：
<img src="http://img.blog.csdn.net/20151013130216349" alt="Layout Cycle">
先是约束变化，然后会产生一个延迟的布局事件，最后到达App的循环。而其中App的循环又会继续等待约束变化的过程，从而产生一个新的周期。</p>

<h4 id="">约束变化</h4>

<p>针对这里的约束变化主要包括以下几种:</p>

<ul>
<li>Activating和Deactivating约束</li>
<li>设置约束的constant或者priority</li>
<li>添加或者删除视图
以上这几种的约束都会产生一个新的布局周期。会根据以下的变化重新计算布局，Layout的引擎则会根据计算的结果接收到新的布局变量，而视图就会自动去调用<code>superview.setNeedsLayout()</code>。这些步骤就组成了约束变化的过程。</li>
</ul>

<h4 id="">延迟的布局事件</h4>

<p>延迟布局的通过主要包括以下几个步骤：</p>

<ul>
<li>重新放置那些错放的视图</li>
<li>更新约束并且重新给视图的frame赋值</li>
</ul>

<p>当然除了约束变化会导致延迟布局的事件以外，还可以通过<code>setNeedsUpdateConstraints()</code>来触发布局事件。</p>

<p>并且在苹果官网还是强烈建议使用<code>Activating Contraints</code>和<code>Deactivating Contraints</code>而不是去<code>Change Contraints</code>（即Add 或者Remove约束），毕竟前者比后者来说说速度快多了。小伙伴们以后要习惯用Activating 和 Deactivating来修改约束。</p>

<p>关于<code>layoutSubviews</code>(在Mac OX中叫做<code>layout</code>)的方法的作用主要在于让布局引擎接受到新的布局的约束值，而不是重新排布各个视图的位置。并且将新的值设定到各个子视图中(在iOS中用的是<code>setBounds</code>和<code>setCenter</code>在Mac OX中用的是<code>setFrame</code>)
而如果在重写<code>layoutSubviews</code>的过程中需要注意以下几点：</p>

<ul>
<li>必须调用<code>[super layoutSubviews]</code>的方法</li>
<li>修改视图的布局需要在调用<code>[super layoutSubviews]</code>之前进行修改</li>
<li>别调用<code>setNeedsUpdateConstraints</code></li>
<li>别修改不是属于该视图的子视图</li>
<li>别随意的修改约束</li>
</ul>

<p>然而这个技能点主要是讲解了一些理论上的知识，而博主对此也不是有非常深刻的理解，所以可能讲解的有些不是特别好，望大家见谅。不过一些注意点是大家都需要注意的，麻烦大家记一下。</p>

<h4 id="">技能点八：与之前的适配进行交互</h4>

<p>对于此来说，iOS关于布局最先是通过<code>setFrame</code>来进行布局，之后在此基础上添加了一个<code>autoresizingMask</code>来进行根据父视图的修改来调整子视图的布局,最后在近几个版本中添加了一个<code>Auto Layout</code>的布局机制，主要是通过约束来进行布局。而技能点八主要是针对那些就的布局机制与AutoLayout共存情况下的一些情况的注意点。</p>

<p>当然在Auto Layout下，小伙伴们依然可以用<code>setFrame</code>来进行布局，但是在之后可能会有个延迟布局的事件来回调的情况下，可能您的<code>setFrame</code>会出现失效的情况。然后对于那些小伙伴们真的希望通过<code>setFrame</code>来进行布局的控件来说苹果有提供一个变量进行处理该种情况。这个变量就是<code>translatesAutoresizingMaskIntoConstraints</code>，如果在创建某个视图是通过IB的情况下，它的默认值就是<code>NO</code>，而如果在创建某个视图是通过代码的情况下，它的默认值则就是<code>YES</code>。而如果小伙伴们想在代码中添加或声明约束给代码生产的视图的话，即的将这个变量设置为NO，否会出现报错的情况,如下图的情况下：
<img src="http://img.blog.csdn.net/20151013130327843" alt="translatesAutoresizingMaskIntoConstraints Demo"></p>

<p>其实只需要将<code>translatesAutoresizingMaskIntoConstraints</code>设置成<code>NO</code>即可。正确的代码如下：</p>

<pre><code class="language-objectivec">    UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    //别忘了将这个变量设置成NO
    btn.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:btn];
    NSLayoutConstraint *kTopContraints = [NSLayoutConstraint constraintWithItem:btn attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:10];
    NSLayoutConstraint *kLeadingContraints = [NSLayoutConstraint constraintWithItem:btn attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:10];
    [NSLayoutConstraint activateConstraints:@[kTopContraints, kLeadingContraints]];
</code></pre>

<p>然而其实将这个变量设置成<code>YES</code>后,对于程序来说，可以做以下操作：</p>

<ul>
<li>任意的通过<code>setFrame</code>进行布局</li>
<li>可以用<code>autoresizingMask</code>来实现约束</li>
<li>其他的视图可以与该视图建立约束</li>
</ul>

<h3 id="">技能点九：约束的创建</h3>

<p>关于约束的创建来说（针对代码创建），其实在iOS 9 中主要是优化了之前方法的可读性。简化了之前繁琐的创建方法，使方法更加简洁与可读。废话不多说，上代码。
对于以前来说要创建约束需要用一下的方法：</p>

<pre><code class="language-objectivec">    [NSLayoutConstraint constraintWithItem:self.saveButton attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0 constant:100];
    [NSLayoutConstraint constraintWithItem:self.saveButton attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:100];
</code></pre>

<p>其实从代码层面上来说，这种方式过于复杂与繁琐。而在新的sdk中添加了简化的方法，如下：</p>

<pre><code class="language-objectivec">    [self.saveButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:100];
    [self.saveButton.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:100];
</code></pre>

<p>相对上面的方法来说一目了然。当然在iOS9中添加了很多Anchor的属性，大部分针对<code>NSLayoutAttribute</code>都有其对应的属性。
然而对于这种新的SDK来说也有一些需要注意的地方：</p>

<ul>
<li>不要给位置的<code>Anchor</code>设置成一个常量
如：[v1.leadingAnchor constraintEqualToConstant:100];这种做法是不允许的。</li>
<li>不要给位置的<code>Anchor</code>设置成一个Size的<code>Anchor</code>
如: [v1.leadingAnchor constraintEqualToAnchor:v2.widthAnchor];同样是不被允许的。</li>
</ul>

<h3 id="layoutguide">技能点十：Layout Guide</h3>

<p>首先先举一个例子，例如在一个场景下，我们需要3个Button，而三个Button中间的距离需要保持相等的距离。而在以往的情况下需要在Button中间建立View并且用约束来保证Button与Button之间的距离保持一致。如下图所示：
<img src="http://img.blog.csdn.net/20151013130400399" alt="LayoutGuide所需效果图">
然后其实对于中间的两个View来说，其实这两个View是完全没有实际的意义，只是为了保证Button与Button之间的距离是一致的作用。
而对于新的SDK中添加了新的类用于处理该功能，即UILayoutGuide（Mac OX中为NSLayoutGuide）。UILayoutGuide在官方文档中说了，他是定义了一个正方形的区域用于Auto Layout的机制。其用法也相当简单，可以将其看做上图中的无意义的视图。并且其也拥有跟视图一样的约束的操作。或许我们就可以将UILayoutGuide看做是一个特殊的视图即可。
具体实现代码如下：(博主用Save、Cancel和Clear三个按钮用来替换)</p>

<pre><code class="language-objectivec">    UILayoutGuide *space1 = [[UILayoutGuide alloc] init];
    [self.view addLayoutGuide:space1];
    UILayoutGuide *space2 = [[UILayoutGuide alloc] init];
    [self.view addLayoutGuide:space2];
    [space1.widthAnchor constraintEqualToAnchor:space2.widthAnchor].active = YES;
    [self.saveButton.trailingAnchor constraintEqualToAnchor:space1.leadingAnchor].active = YES;
    [self.cancelButton.leadingAnchor constraintEqualToAnchor:space1.trailingAnchor].active = YES;
    [self.cancelButton.trailingAnchor constraintEqualToAnchor:space2.leadingAnchor].active = YES;
    [self.clearButton.leadingAnchor constraintEqualToAnchor:space2.trailingAnchor].active = YES;
</code></pre>

<p>具体效果如下：(左图为IB中的图，右图为模拟器的运行效果)
<img src="http://img.blog.csdn.net/20151013130424667" alt="代码运行效果图"></p>

<h3 id="">技能点十一：布局的调试技巧</h3>

<p>关于布局的调试功能，博主就准备用于官方类似的Demo进行讲解。具体如下截图的布局：
<img src="http://img.blog.csdn.net/20151013130500497" alt="Debug效果图">
然而在该例子中由于配置不当会导致运行是出现Layout的错误，而利用该问题中导致的错误来说如何调试布局中出现的问题。而在运行过程中导致的问题的输出如下：</p>

<pre><code>2015-10-13 00:57:46.025 WWDC15AutoLayoutMysteries[13103:4654603] Unable to simultaneously satisfy constraints.  
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "&lt;NSLayoutConstraint:0x7fb999710a70 H:[animalImg]-(122)-|   (Names: animalImg:0x7fb999659790, '|':UIView:0x7fb999659630 )&gt;",
    "&lt;NSLayoutConstraint:0x7fb999710ac0 H:|-(123)-[animalImg]   (Names: animalImg:0x7fb999659790, '|':UIView:0x7fb999659630 )&gt;",
    "&lt;NSLayoutConstraint:0x7fb999659950 'imgAspectRatio' animalImg.width == 1.5*animalImg.height   (Names: animalImg:0x7fb999659790 )&gt;",
    "&lt;NSLayoutConstraint:0x7fb999710cf0 'imgTopMargin' V:|-(21)-[animalImg]   (Names: animalImg:0x7fb999659790, '|':UIView:0x7fb999659630 )&gt;",
    "&lt;NSLayoutConstraint:0x7fb999710d60 'labelTopMargin' V:|-(100)-[questionLbl]   (Names: questionLbl:0x7fb99965ec60, '|':UIView:0x7fb999659630 )&gt;",
    "&lt;NSLayoutConstraint:0x7fb999710dd0 'labelTopToImg' V:[animalImg]-(8)-[questionLbl]   (Names: questionLbl:0x7fb99965ec60, animalImg:0x7fb999659790 )&gt;",
    "&lt;NSLayoutConstraint:0x7fb999587c60 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7fb999659630(320)]&gt;"
)

Will attempt to recover by breaking constraint  
&lt;NSLayoutConstraint:0x7fb999659950 'imgAspectRatio' animalImg.width == 1.5*animalImg.height   (Names: animalImg:0x7fb999659790 )&gt;

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.  
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in &lt;UIKit/UIView.h&gt; may also be helpful.  
</code></pre>

<p>对于大部分人来说看到这么多的输出马上会感到害怕，甚至会不假思索的马上将结果复制到百度或者google中去搜索，希望直接从别人的历史记录中得到答案，然而本王想说的是，不要害怕，让本王告诉你如何快速定位问题。</p>

<p>遇到这种问题，首先先去确定该问题是不是由于前面所说的<code>translatesAutoresizingMaskIntoConstraints</code>没有设置成正确的值所导致的，具体设置查看上面的教程，毕竟很多情况下有人会忘记将其设置成正确的值。
如果在确认了不是由于上述问题所导致的话，接下来就直接去查看Log的最后一句，即<code>Will attempt to recover by breaking constraint 
&lt;NSLayoutConstraint:0x7fb999659950 'imgAspectRatio' animalImg.width == 1.5*animalImg.height   (Names: animalImg:0x7fb999659790 )&gt;</code>,可以看出肯定是由于哪个约束与这个约束产生了冲突而导致。接下来就对Log中的每个约束进行绘制到纸上，查看可能哪个约束与其冲突。这里就采用一下官方PDF中的截图，与Demo中的布局类似，大家可以借鉴一下。 <br>
<img src="http://img.blog.csdn.net/20151013130533662" alt="绘制约束">
可以看得出来Label的TopMargin与ImgTopMargin+ImgHeight+ImgToLabelMargin的之间会产生冲突，而其实应该将Label与父视图的上边距应该进行去除。</p>

<p>然而如果是细心的朋友可以看到在上面的输出语句中可以看到<code>animalImg</code>、<code>labelTopMargin</code>、<code>imgAspectRatio</code>等一些一看就知起意的命名，而如果正常情况下的话应该不会出现有这些命名的存在。因此这里需要提一个技巧，即给对应的视图和约束设置identifier，而这些identifier则会在出现错误输出更友好的输出，保证程序猿们更好的读懂错误的Log。
而对于identifier在IB上也可以设置，也可以通过identifier的属性进行设置。官方推荐给视图、约束以及LayoutGuide设置identifier。而对于在VFL的约束，则需要通过遍历来设置identifier。
同时如果在约束过多的情况下，可以通过<code>constraintsAffectingLayoutForAxis</code>(Max OX中方法名为<code>constraintsAffectingLayoutForOrientation</code>)该方法来进行过滤，该方法是用来显示一个方向的约束。<code>0</code>则显示水平方向的约束，而<code>1</code>的情况下显示垂直方向的约束。看看我们的苹果是如此的贴心。</p>

<p>这里就总结一下，主要的技巧包括以下几个：</p>

<ul>
<li>如果出现错误的Log，则从Log的底部进行开始读</li>
<li>确认<code>translatesAutoresizingMaskIntoConstraints</code>的变量是否设置正确</li>
<li>给所有的视图、约束以及LayoutGuide的设置对应的identifier，方便之后出现错误日志是方便阅读</li>
<li>利用<code>constraintsAffectingLayoutForAxis</code>进行过滤过多的约束</li>
</ul>

<h3 id="">技能点十二:处理模糊的布局</h3>

<p>关于出现模糊的布局的可能情况主要包括以下几种：</p>

<ul>
<li>约束不足或者可以说是缺少约束</li>
<li>优先级的冲突，即可能都在使用默认的优先级(而关于其中的优先级的详细内容可以查看上面的技能点)</li>
</ul>

<p>而如果想要解决布局的模糊可以通过一下的工具进行处理：</p>

<ul>
<li>在IB中出现红色和黄色的图标，即出现布局上的Error和Warmming</li>
<li>使用<code>_autolayoutTrace</code>方法进行查看视图的视图是否有Ambigous的Layout，即是否出现模糊的布局，该方法需要在LLDB中进行使用</li>
<li>而其中最后的方法则是通过菜单中的<code>Debug-&gt;ViewDebug-&gt;Show Alignment Rectangles</code>来进行查看视图</li>
<li>其中在设置断点的情况，请断点暂停了，则可以通过<img src="http://img.blog.csdn.net/20151013130558077" alt="icon button" title="">按钮来进行查看视图的层次</li>
<li>除了上述的<code>_autolayoutTrace</code>方法以外，还有另一个方法<code>exerciseAmbiguityInLayout</code>进行多次运行则可看到由于模糊的情况下Layout Engine可以给我们提供的多种情况中来判断是由于什么原因到只的视图模糊</li>
</ul>

<p>关于调试的细节这里就希望各位童鞋自己去多尝试尝试了，如果有什么问题的话可以通过留言、<a href="mailto:kuaileainits@163.com">邮件</a>以及通过<a href="http://weibo.com/u/5690716723">@叫什么都不如叫Pluto-Y</a>来进行提问。博主会尽其所能为您解答。</p>

<p>Github代码的地址为:<a href="https://github.com/Pluto-Y/WWDC15Demos">https://github.com/Pluto-Y/WWDC15Demos</a>,有需要的同学可以前往下载查看。不懂的可以留言。</p>]]></content:encoded></item><item><title><![CDATA[WWDC 2015 - 压缩App的大小（App Thining in Xcode）]]></title><description><![CDATA[<p>最近在研究WWDC2015,主要研究一下iOS 9和Xcode 7中的特性，方便之后学习，顺带可以装装逼。最近看了关于压缩App大小的视频，主要看苹果在这方面都做了哪些调整以及一些新的内容。
参考视频：<a href="https://developer.apple.com/videos/play/wwdc2015-404/">App Thining in Xcode</a>, 英文不错的童鞋或者想看原生资源的童鞋可以去看看。</p>

<p>下面开始讲述一下我学习到的内容。如果不想看理论内容的人可以直接查看程序员所关注的细节。</p>

<h3 id="">原理</h3>

<h4 id="app">关于App的内容</h4>

<p>一般对于App来说就是分为两个部分</p>

<ul>
<li>可运行代码</li>
<li>资源文件等</li>
</ul>

<p>而其中的根据每个部分所占的比例可分为两种类型的，一种为可运行代码比例比较大的App，另一种为资源文件比例比较大的App。而对于大部分情况来说都是后者比较多，即如图所示：
<img src="http://img.blog.csdn.net/20151006024319005" alt="App Contents"></p>

<p>而对于资源文件来说，可以根据不同的设备类型，不同的内存，不同的编译指令可以进行不同的分配不同的资源，通过对资源文件的分类可以使App在不同的设备上运行的更加流畅与完美。具体的分类条件可以根据下图进行分类：
<img src="http://img.blog.csdn.net/20151006024403566" alt="Resources Classify"></p>

<p>而根据不同的设备可能会去加载不同的资源文件，如下图的例子：
<img src="http://img.blog.csdn.net/20151006024445402" alt="Resources Classify Example 1">
对于iPad Mini的设备来说(如果添加了不同分类的资源的话)，设备自动去加载armv7，1x iPad以及其他跟iPad Mini有关的配置相关的资源文件，从而使App在iPad Mini上运行的更加流畅与完美。</p>

<h4 id="">关于缩减的原理</h4>

<p>而对于为了优化而言，即对App的资源文件进行优化，对于苹果服务器来说即根据不同的设备类型，让其下载带对应设备所需资源文件的ipa，而与该设备的无关的资源文件则不应让该设备下载，从而减小用户下载的ipa的大小。</p>]]></description><link>http://www.pluto-y.com/wwdc-2015-app-thining-in-xcode/</link><guid isPermaLink="false">cf3199f1-cacc-493d-8127-1bd72fdd7dc0</guid><category><![CDATA[WWDC]]></category><category><![CDATA[WWDC2015]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Mon, 05 Oct 2015 19:04:44 GMT</pubDate><content:encoded><![CDATA[<p>最近在研究WWDC2015,主要研究一下iOS 9和Xcode 7中的特性，方便之后学习，顺带可以装装逼。最近看了关于压缩App大小的视频，主要看苹果在这方面都做了哪些调整以及一些新的内容。
参考视频：<a href="https://developer.apple.com/videos/play/wwdc2015-404/">App Thining in Xcode</a>, 英文不错的童鞋或者想看原生资源的童鞋可以去看看。</p>

<p>下面开始讲述一下我学习到的内容。如果不想看理论内容的人可以直接查看程序员所关注的细节。</p>

<h3 id="">原理</h3>

<h4 id="app">关于App的内容</h4>

<p>一般对于App来说就是分为两个部分</p>

<ul>
<li>可运行代码</li>
<li>资源文件等</li>
</ul>

<p>而其中的根据每个部分所占的比例可分为两种类型的，一种为可运行代码比例比较大的App，另一种为资源文件比例比较大的App。而对于大部分情况来说都是后者比较多，即如图所示：
<img src="http://img.blog.csdn.net/20151006024319005" alt="App Contents"></p>

<p>而对于资源文件来说，可以根据不同的设备类型，不同的内存，不同的编译指令可以进行不同的分配不同的资源，通过对资源文件的分类可以使App在不同的设备上运行的更加流畅与完美。具体的分类条件可以根据下图进行分类：
<img src="http://img.blog.csdn.net/20151006024403566" alt="Resources Classify"></p>

<p>而根据不同的设备可能会去加载不同的资源文件，如下图的例子：
<img src="http://img.blog.csdn.net/20151006024445402" alt="Resources Classify Example 1">
对于iPad Mini的设备来说(如果添加了不同分类的资源的话)，设备自动去加载armv7，1x iPad以及其他跟iPad Mini有关的配置相关的资源文件，从而使App在iPad Mini上运行的更加流畅与完美。</p>

<h4 id="">关于缩减的原理</h4>

<p>而对于为了优化而言，即对App的资源文件进行优化，对于苹果服务器来说即根据不同的设备类型，让其下载带对应设备所需资源文件的ipa，而与该设备的无关的资源文件则不应让该设备下载，从而减小用户下载的ipa的大小。虽然是为了压缩App的大小，可是为了让不同设备进行不同的资源的加载，所以在上传App的时候还是需要对App所支持的所有设备的资源进行上传，即上传一个包含所有支持设备类型的资源文件的App。而这样的文件可能会特别大，而苹果在新的Xcode以及App Store的服务器上进行了对应的优化，保证每个客户只需下载自己设备类型所需要的资源文件即可。而对于iOS9中，则对于大部分不经常用的资源文件不进行加载，只有在需要用到时才去加载对应的资源文件。
* App Store和Xcode 7部分的优化
例如Apple自带开发的<code>DemoBots</code>的App，如果需要上传所有的资源文件的App，则该App大小大概为74 MB的大小，而通过App Store的优化后，则每个设备所下载的平均大小在22 MB左右。而其中对于代码部分则是大家都必须下载的部分大约在14 MB左右，而资源文件根据不同设备则下载的内容不同，大概在5~11MB左右，平均大约在8 MB左右。</p>

<ul>
<li><p>而如果需要生成一个通用的<code>Ad hoc</code>或者<code>Enterprise</code>的ipa，只需程序员进行简单的配置即可。之后Xcode Server会产生一个<code>Manifest</code>的Plist文件，而不同设备在下载时都会通过这个<code>Manifest</code>的Plist文件去找对应设备下载的ipa的具体地址，然后下载对应的ipa。
具体原理如下图：
<img src="http://img.blog.csdn.net/20151006024606949" alt="Manifest Plist File"></p></li>
<li><p>iOS9部分的优化
例如一个游戏的App来说，资源文件大约必须的资源文件以及不经常使用的资源文件，例如一个有等级的资源文件来说，对于一个等级为1的玩家来说，其中关于等级为1的资源文件才是常用的资源文件，而对于等级2以上的资源文件则为不经常使用的文件。而在iOS9中，会对等级为1的资源文件会加载到内存中，而对于等级2以上的资源文件则会在需要使用的时候才进行加载。并且在App长时间没用的情况下，则会将内存中关于这个App的资源文件进行释放，而当App再次启动的时候才会重新加载所需要的资源文件。</p></li>
</ul>

<h3 id="">程序员所关注的细节</h3>

<h4 id="assetcatalogs">Asset Catalogs中的新内容</h4>

<ul>
<li>新的图片特性
关于Asset Catalogs的基础楼主就不进行累述了，对于Xcode 7来说，Asset Catalogs中关于资源文件的分类所依据的特性进行了添加，其中添加了一个根据<code>Memory</code>和<code>Graphics</code>进行分类，即资源文件可以根据<code>Memory</code>和<code>Graphics</code>进行不同的资源文件的加载。
例如当内存为1G时加载红色的图片，内存为2G时加载蓝色的图片，只需进行一下的配置即可：</li>
</ul>

<p><img src="http://img.blog.csdn.net/20151006024655795" alt="新的设备属性"></p>

<p>对于需要根据不同的内存进行适配只需对内存需要适配的内存进行勾选，并且在左边中对应的1G放上红色的图片，在2G上放上蓝色的图片即可。
* 新的资源文件的类型-----Data Set 和 Sprite Atlas
对于Asset Catalogs中，之前只支持图片类型的资源文件。而在Xcode 7中添加了<code>Data Set</code> 和<code>Sprite Atlas</code>的类型，即之后的对于除了图片类型的资源文件以外，还可以对其他类型的资源文件根据不同的设备属性进行使用不同的资源文件。而需要添加<code>Data Set</code>只需通过添加按钮即可，如下图：</p>

<p><img src="http://img.blog.csdn.net/20151006024739788" alt="添加新的类型的资源文件"></p>

<p>当然也可以通过右键来进行添加。</p>

<h4 id="assetcatalogs">旧文件的导入(未用Asset Catalogs进行管理的文件)</h4>

<p>对于之前没有使用<code>Asset Catalogs</code>进行管理的资源文件来说，在XCode 7中也对这种情况下进行了特殊处理。只需将同一个资源，但根据不同特性(如设备类型、分辨率等)所不同的资源放到一个后缀名为<code>imageset</code>下目录下，并且在目录下存放一个<code>Contents.json</code>的文件，并在文件中对该资源属于哪些特性组成的进行详细说明，最后将该文件拉入项目中即可。然后对于其中的每个特性组成的文件名可以不对其进行限制。具体如下图：
<img src="http://img.blog.csdn.net/20151006024813106" alt="Image Set Directory">
<img src="http://img.blog.csdn.net/20151006024856121" alt="Contents.json Content">
而对于该图中各个文件名可以随意取名，只需在<code>Contens.json</code>中进行指明其文件名即可。
而对于<code>Contents.json</code>中的文件的格式以及对于其中的都包含哪一些属性行可以根据<a href="https://developer.apple.com/library/prerelease/ios/documentation/Xcode/Reference/xcode_ref-Asset_Catalog_Format/ImageSetType.html">https://developer.apple.com/library/prerelease/ios/documentation/Xcode/Reference/xcode<em>ref-Asset</em>Catalog<em>Format/ImageSetType.html</em></a>去查看。
对于新添加的<code>Data Set</code>的类型同样也有该文件，其参考文档在:<a href="https://developer.apple.com/library/prerelease/watchos/documentation/Xcode/Reference/xcode_ref-Asset_Catalog_Format/DataSetType.html">https://developer.apple.com/library/prerelease/watchos/documentation/Xcode/Reference/xcoderef-Asset<em>Catalog</em>Format/DataSetType.html</a>去查看。</p>

<h4 id="xcode">Xcode的新特性</h4>

<ul>
<li>关于Xcode中添加了一个行的Build Settings的参数<code>Enable Building Only Active Resources</code>，如下图：</li>
</ul>

<p><img src="http://img.blog.csdn.net/20151006024938665" alt="Xcode Build Settings"></p>

<p>只需将这个参数设置为<code>YES</code>之后在，在之后编译运行过程中，打包的ipa只会包含跟所运行的设备有关资源文件，如在模拟器上运行，则会打包模拟器所需的资源文件。如此做可以加快打包的速度，从而保证测试效率。
* 关于Xcode的Distribution,即关于Xcode中的<code>Ad hoc</code>与<code>Enterprise</code>的打包中，添加了可以根据设备类型进行打包对应的ipa，而打包的ipa中只包含对应设备所需的资源文件。从而可以对测试人员的测试效率进行提高，并且减少测试人员手机中所占的大小。如下图：</p>

<p><img src="http://img.blog.csdn.net/20151006025027097" alt="Ad hoc or Enterprise">
* 关于Xcode中产生通用类型的ipa有了新的内容，可以将产生的通用类型的ipa部署到自己的服务器上，并且提供给其他人下载。具体的配置可以参考：<a href="http://help.apple.com/deployment/ios/#/apda0e3426d7">http://help.apple.com/deployment/ios/#/apda0e3426d7</a>
对于其中的详情博主也没有测试过，有兴趣的童鞋可以去试试，然后方便的话麻烦发一个结果告诉给博主。博主在此感谢了！</p>

<h3 id="">总结</h3>

<p>到此关于缩小App的资源的打包和使用教程就到此为止了，关于Apple压缩App的大小对于程序员的开发以及测试人员的测试上的效率肯定都会有大大的提高，毕竟在开发人员或测试人员打包测试的过程可以减少时间。而对于用户来说，特别是对于低配置的设备来说也是福音，在下载App的时候可以减少App所占的物理内存，而对于在运行过程中的内存分配也会更加合理，能保证内存的合理利用性。对此像博主这种屌丝只用16G的设备来说真的是拯救了我们。感谢天，感谢地，感谢Apple压缩程序。</p>]]></content:encoded></item><item><title><![CDATA[iOS9 - NSAppTransportSecurity]]></title><description><![CDATA[<p>App Transport Security（以下均称<code>ATS</code>）是iOS9提供的一个新特性，主要是保证app和web服务之间的安全。如果不想开启的话，可以关闭这个特性。</p>

<p>所有用到<code>NSURLConnection</code>、<code>CFURL</code>以及<code>NSURLSession</code>API都会触发<code>ATS</code>(使用iOS9的SDK编译)验证, 所以在iOS9中需要符合一些配置才可以使<code>ATS</code>正常运行。</p>

<h3 id="">配置要求</h3>

<p>关于App Transport Security 的一些进本配置要求：</p>

<ul>
<li><p>服务器只要支持TLS协议1.2</p></li>
<li><p>加密算法也是有限制，需要在以下列表中</p></li>
</ul>

<pre><code>    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    TLS_ECDHE_ECDSA_WITH_</code></pre>]]></description><link>http://www.pluto-y.com/ios9-nsapptransportsecurity/</link><guid isPermaLink="false">ad488b78-97bf-4963-8812-1fdc5b11f13e</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Thu, 24 Sep 2015 10:09:29 GMT</pubDate><content:encoded><![CDATA[<p>App Transport Security（以下均称<code>ATS</code>）是iOS9提供的一个新特性，主要是保证app和web服务之间的安全。如果不想开启的话，可以关闭这个特性。</p>

<p>所有用到<code>NSURLConnection</code>、<code>CFURL</code>以及<code>NSURLSession</code>API都会触发<code>ATS</code>(使用iOS9的SDK编译)验证, 所以在iOS9中需要符合一些配置才可以使<code>ATS</code>正常运行。</p>

<h3 id="">配置要求</h3>

<p>关于App Transport Security 的一些进本配置要求：</p>

<ul>
<li><p>服务器只要支持TLS协议1.2</p></li>
<li><p>加密算法也是有限制，需要在以下列表中</p></li>
</ul>

<pre><code>    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
</code></pre>

<ul>
<li>必须是用至少是HA256的算法，用至少是2048位的RSA的key 或至少是256位的Elliptic-Curve(ECC)的key所产生的证书</li>
</ul>

<h3 id="">属性结构</h3>

<p>如果你的服务器不是HTTPS的协议，想要添加例外可以在程序中的<code>info.plist</code>文字间 添加例外。甚至是你不想开启<code>ATS</code>的话，都可以在配置文件中进行配置。关于配置项可以参考下表：
<img src="http://www.pluto-y.com/content/images/2015/09/ATS_table.png" alt="NSAppTransportSecurity表-由于没办法用markdown的语法，暂时使用图片"></p>

<p>属性的目录结构如下:</p>

<ul>
<li>NSAppTransportSecurity
<ul><li>NSAllowsArbitraryLoads</li>
<li>NSExceptionDomains
<ul><li>&lt; domain-name-for-exception-as-string >
<ul><li>NSExceptionMinimumTLSVerion</li>
<li>NSExceptionRequiresForwardSecrecy</li>
<li>NSExceptionAllowsInsecureHTTPLoads</li>
<li>NSIncludesSubdomains</li>
<li>NSThirdPartyExceptionMinimumTLSVersion</li>
<li>NSThirdPartyExceptionRequiresForwardSecrecy</li>
<li>NSThirdPartyExceptionAllowsInsecureHTTPLoads</li></ul></li></ul></li></ul></li>
</ul>

<h3 id="">属性详解</h3>

<ul>
<li><strong><code>NSAppTransportSecurity</code></strong> : 一个用于配置App Transport Security行为的属性，在<code>Info.plist</code>中是于<code>Bundle Identifier</code>同一级别的属性。</li>
<li><strong><code>NSAllowsArbitraryLoads</code></strong> : 一个用于针对不在<code>NSExceptionDomains</code>中的配置项。如果设置成<code>YES</code>，则对于那些不在<code>NSExceptionDomains</code>的域则不需要通过<code>ATS</code>的验证。默认值是<code>No</code>。</li>
<li><strong><code>NSExceptionDomains</code></strong> : 用于配置例外的配置项，即在该配置项中的域不需要通过<code>ATS</code>的验证。</li>
<li><strong><code>&lt; domain-name-for-exception-as-string &gt;</code></strong> : 需要添加例外的域名字符串，如:<code>www.baidu.com</code></li>
<li><strong><code>NSExceptionMinimumTLSVerion</code></strong> : 用于指定例外域名的TSL的版本号，可用的配置有<code>TLSv1.0</code>、<code>TLSv1.1</code>以及<code>TLSv1.2</code>三个配置项。</li>
<li><strong><code>NSExceptionRequiresForwardSecrecy</code></strong> ： 用于指定所配置的域协议是否在<code>ATS</code>的所要求的第三点中。如果是<code>YES</code>,则说明所配置的域符合要求的第三点,如果是<code>NO</code>， 则加密算法必须是以下这几种。默认值是<code>YES</code>。</li>
</ul>

<pre><code>   TLS_RSA_WITH_AES_256_GCM_SHA384
   TLS_RSA_WITH_AES_128_GCM_SHA256
   TLS_RSA_WITH_AES_256_CBC_SHA256
   TLS_RSA_WITH_AES_256_CBC_SHA
   TLS_RSA_WITH_AES_128_CBC_SHA256
   TLS_RSA_WITH_AES_128_CBC_SHA
</code></pre>

<ul>
<li><strong><code>NSExceptionAllowsInsecureHTTPLoads</code></strong> : 用于指明所配置的域是否个HTTPS的服务器。用这个配置可用访问那些没有证书、自签名证书、过期证书以及证书与域名匹配不上的服务器。默认值是<code>NO</code>。</li>
<li><strong><code>NSIncludesSubdomains</code></strong> : 用于指明子域名是否使用同样的配置。默认值是<code>NO</code>。</li>
<li><strong><code>NSThirdPartyExceptionMinimumTLSVersion</code></strong> : 该变量在如果是域名为第三的域名，且开发人员无法控制的情况下进行配置。</li>
<li><strong><code>NSThirdPartyExceptionRequiresForwardSecrecy</code></strong> : 该变量在如果是域名为第三的域名，且开发人员无法控制的情况下进行配置。</li>
<li><strong><code>NSThirdPartyExceptionRequiresForwardSecrecy</code></strong> : 该变量在如果是域名为第三的域名，且开发人员无法控制的情况下进行配置。</li>
</ul>

<h3 id="">事例讲解</h3>

<h4 id="https">只有HTTPS</h4>

<p>如果你的APP所访问的所有的服务器都只有HTTPS的话，那么恭喜你，完全不需要配置ATS你的App即可使用，也就是说iOS 9中的ATS对你来说并没有什么卵用。</p>

<h4 id="atsats">所有网址都不经过ATS(关闭ATS)</h4>

<p>这个配置最简单，只要通过配置<code>NSAllowsArbitraryLoads</code>，将其设置成No即可。具体如下：
<img src="http://www.pluto-y.com/content/images/2015/09/ATS-close.png" alt="关闭ATS"></p>

<h4 id="ats">所有网址都经过ATS，添加一些例外（白名单）</h4>

<p>如果你想App中的网址都通过ATS验证，只有少数一些网址例外的话，你可以为少数的网址添加<code>NSExceptionDomains</code>,并且在下面添加你需要添加的网址即可。然后对每个网址进行分别的设置，其中将<code>NSIncludeSubdomains</code>以及<code>NSExceptionAllowsInsecureHttpLoads</code>设置成YES，即可不通过ATS验证。具体如下：
<img src="http://www.pluto-y.com/content/images/2015/09/ATS-for-all---with-some-exception.png" alt="部分添加例外的配置"></p>

<h4 id="ats">所有网址都不经过ATS，添加例外</h4>

<p>如果你想App大部分的网址都不经过ATS，少数几个网址通过ATS验证的话，你可以先将<code>NSAllowsArbitraryLoads</code>设置成YES，这样就可以使所有网址都不经过ATS，然后将少数一些需要经过ATS验证的网址添加到<code>NSExceptionDomains</code>中，并将<code>NSExceptionAllowsInsecureHTTPLoads</code>设置成NO即可。具体如下：
<img src="http://www.pluto-y.com/content/images/2015/09/ATS-disabled--with-some-exceptions.png" alt="部分通过ATS配置"></p>

<h4 id="tls">使用低等级版本的TLS协议</h4>

<p>如果你使用的服务器没办法支持TLS1.2的协议的话，也不大要紧，可以通过配置来支持低版本的TLS协议。将对应没办法进行高版本的服务器在<code>NSExceptionDomains</code>下建立一个的对应的域名，在对应域名下添加<code>NSExceptionRequiresForwardSecrecy</code>设置成NO并且将<code>NSExceptionMinimumTLSVersion</code>设置成对应的TLS的版本号即可。具体如下：
<img src="http://www.pluto-y.com/content/images/2015/09/dowgraded-ATS.png" alt="低等级协议配置"></p>

<h3 id="">如何配置</h3>

<p>其实在Mac OS X 10.11中，苹果给我们提供了个超牛逼的功能，简单来说就是提供了一个命令行的工具来产生对应的配置项给我们。但是由于我没有升级到Mac OS X10.11X所以我就没办法进行测试了。至于其中的奥秘，等更新了系统之后再进行补充。</p>]]></content:encoded></item><item><title><![CDATA[iOS 自动集成单元测试 - Jenkins与xctool整合]]></title><description><![CDATA[xctool与Jenkins的搭配使用]]></description><link>http://www.pluto-y.com/ios-unit-test-jenkins-xctool/</link><guid isPermaLink="false">da9f2f5f-a69f-4707-8ded-2eba7e88c6bd</guid><category><![CDATA[Jenkins]]></category><category><![CDATA[XCtool]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Thu, 17 Sep 2015 10:34:55 GMT</pubDate><content:encoded><![CDATA[<p>我胡汉三又回来啦，继上次说完Jenkins和XCode的整合后，劳资又想把单元测试交给Jenkins了，于是乎又开始寻寻觅觅寻，好聪明的中国人，好优美的中国话，扁担宽 板凳长
扁担想绑在板凳上.....不好意思，忘记次药了，不知道为什么突然脑袋里面被人插入这段旋律了。</p>

<p>好了言归正传，关于这篇文章是建立在Jenkins集成好Xcode的基础上，因为如果没有集成的话，可能会有一些差别。如果不知道如何整合Xcode的话，可以看<a href="http://www.pluto-y.com/jenkins-xcode-configuration/">Jenkins整合XCode详解</a>这篇文章，里面有惊喜哟。</p>

<p>关于xctool不了解的童鞋可以到<a href="https://github.com/facebook/xctool">xctool在Github</a>上查看具体的文档，本文就不对其基础用法进行累述了。如果没有用上xctool的同学那就真的可惜了，那我们就真的没办法愉快的么么哒了，毕竟连工具都用的不一样还怎么谈恋爱。
<img src="http://www.pluto-y.com/content/images/2015/09/2015916221637.png" alt="被揍图"></p>

<h3 id="">好吧，先上两张效果图：</h3>

<p><img src="http://www.pluto-y.com/content/images/2015/09/D-B-JOW1-54W5T-B-_KVNQY.png" alt="效果图1">
测试报告的结果趋势</p>

<p><img src="http://www.pluto-y.com/content/images/2015/09/XOOMNBWXVFZADYLKJZ-V5GO.png" alt="效果图2">
每次构建后的报告(红线框部分)</p>

<h3 id="">好了，看完了效果图，接下来就开始说怎么做了，其实很简单的两步走</h3>

<ul>
<li>在Xcode构建后加一条Execute Shell的构建，在其中加入语句/usr/local/bin/xctool -workspace <code>***.xcworkspace</code> -scheme <code>schemeName</code> -sdk iphonesimulator -reporter <code>junit:report/****-report.xml</code> <code>run-tests</code>,如图</li>
</ul>

<p><img src="http://www.pluto-y.com/content/images/2015/09/---1-1.png" alt="配置图1"></p>

<pre><code>聪明的童鞋一看就知道这个和xcodebuild命令的参数类似
***.xcworkspace:即你的workspace的名称（如果是xcodeproj文件，则改成-project projectName.xcodeproj）
schemeName:即你项目中的scheme(如果不懂填写，查整合xcode的文章)  
junit:report/****-report.xml:即以junit的报告格式进行输入，并存到report目录下名字为****-report.xml的文件，目录名和文件名都可以进行修改  
run-tests:即只执行测试不执行clean和编译的过程(如果您在Jenkins中所见的job为只用来单元测试的话，可以将'run-tests'改为'clean build test',这样就会为这个job进行clean、编译和测试)  
</code></pre>

<ul>
<li>接着在构建后添加一个Publish JUnit test result report的项目，并且填写之前写好的目录名好文件名，如图</li>
</ul>

<p><img src="http://www.pluto-y.com/content/images/2015/09/---2-1.png" alt="配置图2"></p>

<ul>
<li>最后构建项目-------大功告成！</li>
</ul>

<p><img src="http://www.pluto-y.com/content/images/2015/09/--.png" alt="搞定"></p>]]></content:encoded></item></channel></rss>