实时显示iOS编写UI代码效果

mmp7 10年前
   <div>     <p> 编写iOS应用UI的方式大概有两种,一种是Storyboard/Xib,另一种是手写代 码。采用Storyboard/Xib方式组织UI,由于提供可视化的特性,只要从UI库中拖动UI控件,便可以显示结果,极大地提高开发速度。但面临一 个问题就是多人协作开发,由于所有的UI都放在同一个Storyboard文件中,使用Git/SVN合并代码就会出现冲突。多人协作开发还不是主要问 题,有人提出可以创建多个Storyboard来分开UI编写,而Storyboard/Xib最主要问题是代码复用性比较差。所以有些人就选择手写UI 代码,这样不仅可以解决多人协作开发问题,而且通过自定义控件在多个View使用。但每次手写UI代码后都要编译、构建和运行,最后在模拟器显示,这样会 拖慢开发速度。如果每次修改UI控件后,保存修改便实时在模拟器显示修改后结果,就可以极大的提高编写UI的速度。 </p>     <div href="https://simg.open-open.com/show/9193ff629a9c086fcb1b76e437afddb6.gif">      <img src="https://simg.open-open.com/show/9193ff629a9c086fcb1b76e437afddb6.gif" alt="Live Change.gif" width="700" height="560.5433376455369" />      <br />      <div>       Live Change.gif      </div>     </div>     <h1> Auto Layout </h1>     <h3> Auto Layout是什么 </h3>     <p> <a href="/misc/goto?guid=4959631006879443036" target="_blank">Auto Layout</a>是一个基于constraint(约束)的布局系统,它根据UI元素之间约束关系来调整UI元素的位置和大小。 </p>     <h3> Auto Layout解决什么问题 </h3>     <ul>      <li> 更容易适配不同分辨率设备的屏幕(iPhone 6 Plus, iPhone 6, iPhone 5s/5, iPhone 4s/4) </li>      <li> 当设备旋转时不需要做额外处理 </li>      <li> 使用constraint来描述布局逻辑,更利于理解和清晰 </li>     </ul>     <h3> 如何使用Auto Layout </h3>     <p> Auto Layout中约束的类对应是<a href="/misc/goto?guid=4959631006978551924" target="_blank">NSLayoutConstraint</a>, 而创建NSLayoutConstraint对象主要有两种方式,第一种是 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">+ (id)constraintWithItem:(id)view1                attribute:(NSLayoutAttribute)attribute1                relatedBy:(NSLayoutRelation)relation                   toItem:(id)view2                attribute:(NSLayoutAttribute)attribute2               multiplier:(CGFloat)multiplier                 constant:(CGFloat)constant;</pre>     <p> 上面方法主要意思是,某个view1的attribute1等于(小于或等于/大于或等于)某个view2的attribute2的multiplier倍加上constant。而attribute主要由表示位置(上/下/左/右)和大小(宽/高)的以下几个值: </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">typedef enum: NSInteger {    NSLayoutAttributeLeft = 1,    NSLayoutAttributeRight,    NSLayoutAttributeTop,    NSLayoutAttributeBottom,    NSLayoutAttributeLeading,    NSLayoutAttributeTrailing,    NSLayoutAttributeWidth,    NSLayoutAttributeHeight,    NSLayoutAttributeCenterX,    NSLayoutAttributeCenterY,    NSLayoutAttributeBaseline,    NSLayoutAttributeNotAnAttribute = 0 } NSLayoutAttribute;</pre>     <p> 简化一下,使用公式可以表达为: </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">view1.attribute1 = view2.attribute2 * multiplier + constant</pre>     <p> 第二种方式是: </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">+ (NSArray *)constraintsWithVisualFormat:(NSString *)format                                   options:(NSLayoutFormatOptions)opts                                   metrics:(NSDictionary *)metrics                                     views:(NSDictionary *)views;</pre>     <p> 这种方式主要是采用<a href="/misc/goto?guid=4959631007059711603" target="_blank">Visual Format Language</a>(可视化格式语言)来描述约束布局,虽然语法比较简洁,但是可读性比较差和容易出错。 </p>     <h3> Auto Layout存在问题 </h3>     <p> 虽然Auto Layout在布局view方面是非常强大和灵活,但是创建constraint的语法过于繁杂,引用<a href="/misc/goto?guid=4958877303436721101" target="_blank">Masonry</a>一个例子: </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">UIView *superview = self;  UIView *view1 = [[UIView alloc] init]; view1.translatesAutoresizingMaskIntoConstraints = NO; view1.backgroundColor = [UIColor greenColor]; [superview addSubview:view1];  UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);  [superview addConstraints:@[      //view1 constraints     [NSLayoutConstraint constraintWithItem:view1                                  attribute:NSLayoutAttributeTop                                  relatedBy:NSLayoutRelationEqual                                     toItem:superview                                  attribute:NSLayoutAttributeTop                                 multiplier:1.0                                   constant:padding.top],      [NSLayoutConstraint constraintWithItem:view1                                  attribute:NSLayoutAttributeLeft                                  relatedBy:NSLayoutRelationEqual                                     toItem:superview                                  attribute:NSLayoutAttributeLeft                                 multiplier:1.0                                   constant:padding.left],      [NSLayoutConstraint constraintWithItem:view1                                  attribute:NSLayoutAttributeBottom                                  relatedBy:NSLayoutRelationEqual                                     toItem:superview                                  attribute:NSLayoutAttributeBottom                                 multiplier:1.0                                   constant:-padding.bottom],      [NSLayoutConstraint constraintWithItem:view1                                  attribute:NSLayoutAttributeRight                                  relatedBy:NSLayoutRelationEqual                                     toItem:superview                                  attribute:NSLayoutAttributeRight                                 multiplier:1                                   constant:-padding.right],   ]];</pre>     <p> 如此简单的一个例子都要编写这么多行代码,想象一下如果创建多个view的constraint时会多么痛苦啊。另一个方式是采用Visual Format Language (VFL),虽然语法比较简洁,但是可读性比较差和容易出错。 </p>     <h1> Masonry </h1>     <h3> 为什么使用Masonry </h3>     <p> <a href="/misc/goto?guid=4958877303436721101" target="_blank">Masonry</a>是采用链式<a href="/misc/goto?guid=4958540863061401371" target="_blank">DSL(Domain-specific language)</a>来封装NSLayoutConstraint,通过这种方式编写Auto Layout布局代码更加易读和简洁。<br /> 使用Masonry的MASConstraintMaker来表达相同constraint </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);  [view1 mas_makeConstraints:^(MASConstraintMaker *make) {     make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler     make.left.equalTo(superview.mas_left).with.offset(padding.left);     make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);     make.right.equalTo(superview.mas_right).with.offset(-padding.right); }];</pre>     <p> 甚至可以更短 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">[view1 mas_makeConstraints:^(MASConstraintMaker *make) {     make.edges.equalTo(superview).with.insets(padding); }];</pre>     <h3> 如何使用 </h3>     <p> 使用Masonry创建constraint来定义布局的方式有三种:mas_makeConstraints,mas_updateConstraints,mas_remakeConstraints。 </p>     <h5> 1. mas_makeConstraints </h5>     <p> 使用mas_makeConstraints创建constraint后,你可以使用局部变量或属性来保存以便下次引用它;如果创建多个constraints,你可以采用数组来保存它们。 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">// in public/private interface @property (nonatomic, strong) MASConstraint *topConstraint;  ...  // when making constraints [view1 mas_makeConstraints:^(MASConstraintMaker *make) {     self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);     make.left.equalTo(superview.mas_left).with.offset(padding.left); }];  ... // then later you can call [self.topConstraint uninstall];</pre>     <h5> 2. mas_updateConstraints </h5>     <p> 有时你需要更新constraint(例如,动画和调试)而不是创建固定constraint,可以使用mas_updateConstraints方法 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">// this is Apple's recommended place for adding/updating constraints // this method can get called multiple times in response to setNeedsUpdateConstraints // which can be called by UIKit internally or in your code if you need to trigger an update to your constraints - (void)updateConstraints {     [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {         make.center.equalTo(self);         make.width.equalTo(@(self.buttonSize.width)).priorityLow();         make.height.equalTo(@(self.buttonSize.height)).priorityLow();         make.width.lessThanOrEqualTo(self);         make.height.lessThanOrEqualTo(self);     }];      //according to apple super should be called at end of method     [super updateConstraints]; }</pre>     <h5> 3. mas_remakeConstraints </h5>     <p> mas_remakeConstraints与mas_updateConstraints比较相似,都是更新constraint。不过,mas_remakeConstraints是删除之前constraint,然后再添加新的constraint(适用于移动动画);而mas_updateConstraints只是更新constraint的值。 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">- (void)changeButtonPosition {     [self.button mas_remakeConstraints:^(MASConstraintMaker *make) {         make.size.equalTo(self.buttonSize);          if (topLeft) {             make.top.and.left.offset(10);         } else {             make.bottom.and.right.offset(-10);         }     }]; }</pre>     <p> 想了解以上三个代码片段的更多细节,可以下载<strong>Masonry iOS Examples</strong>工程查阅。 </p>     <h1> Classy </h1>     <h3> Classy简介和特性 </h3>     <p> <a href="/misc/goto?guid=4958968153249471079" target="_blank">Classy</a>是一个能与UIKit无缝结合stylesheet(样式)系统。它借鉴<a href="/misc/goto?guid=4958987543899070110" target="_blank">CSS</a>的思想,但引入新的语法和命名规则。 </p>     <blockquote>      <h5> 灵活内嵌的语法 </h5>      <p> {}:;这些语法符号是可选的,你可以选择适合自己的风格来表达stylesheet。 </p>     </blockquote>     <p> 你可以使用{}:;来限定stylesheet </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">$main-color = #e1e1e1;  MYCustomView {   background-color: $main-color;   title-insets: 5, 10, 5, 10;   > UIProgressView.tinted {     progress-tint-color: black;     track-tint-color: yellow;   } }  ^UIButton.warning, UIView.warning ^UIButton {   title-color[state:highlighted]: #e3e3e3; }</pre>     <p> 或者你使用<strong>空格</strong>来限定stylesheet </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">$main-color = #e1e1e1  MYCustomView    background-color $main-color   title-insets 5, 10, 5, 10   > UIProgressView.tinted      progress-tint-color black     track-tint-color yellow  ^UIButton.warning, UIView.warning ^UIButton    title-color[state:highlighted] #e3e3e3</pre>     <h5> 默认样式 </h5>     <p> Classy在应用程序Bundle默认查找文件名为<strong>stylesheet.cas</strong>的样式文件。如果你采用这个文件名,你可以不用做任何东西就能加载样式文件。<br /> 但如果你想指定其他<strong>file path</strong>(样式文件名),你可以创建[CASStyler defaultStyler] </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">[CASStyler defaultStyler].filePath = [[NSBundle mainBundle] pathForResource:@"myStyles.cas" ofType:nil];</pre>     <p> 如果你还想当发生错误时,获取错误信息以便于调试,可以使用-(void)setFilePath:error: </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">NSError *error = nil; NSString filePath = [[NSBundle mainBundle] pathForResource:@"myStyles.cas" ofType:nil]; [[CASStyler defaultStyler] setFilePath:filePath error:&error];</pre>     <p> 如果你是使用Storyboard/Xib组织UI界面,那就需要在<strong>main.m</strong>的int main(int argc, char * argv[])方法设置<strong> filePath</strong>,这样可以确保在创建UIWindow之前加载stylesheet。否则(采用手写UI代码),你在<strong> AppDelegate.m</strong>的- (BOOL)application:didFinishLaunchingWithOptions:方法设置<strong>filePath</strong> </p>     <h5> Live Reload </h5>     <p> Live Reload是实时显示编写UI代码效果的关键特性,它能够实时检查stylesheet文件变化,无需重新编译、构建和运行模拟器,从而极大提高开发速度。<br /> 为了启用Live Reload,你需要指定stylesheet路径,并且只运行在模拟器上。 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">#if TARGET_IPHONE_SIMULATOR     NSString *absoluteFilePath = CASAbsoluteFilePath(@"../Styles/stylesheet.cas");     [CASStyler defaultStyler].watchFilePath = absoluteFilePath; #endif</pre>     <h3> Selectors </h3>     <p> <em>Style Selectors</em>是指定哪个view使用哪种样式的方式。主要有三种方法来指定目标view: </p>     <ol>      <li> Object Class </li>      <li> View Hierarchy </li>      <li> Style Class </li>     </ol>     <p> 你可以混合使用三种方法,例子如下: </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">/* match views  * where class is UIButton or UIButton subclass  * and styleClass is "large"  * and superview class is UITabBar  */  UITabBar > ^UIButton.large { }</pre>     <p> 想了解具体如何使用,请查阅官网<a href="/misc/goto?guid=4959631007271534760" target="_blank">Selectors</a>章节 </p>     <blockquote>      <p> 为了避免与Objective-C的message selectors混淆,术语<strong>style selectors</strong>表示Classy stylesheets的selectors </p>     </blockquote>     <h3> Properties </h3>     <p> Classy支持所有<a href="/misc/goto?guid=4959631007356635393" target="_blank">UIAppearance的属性和方法</a>,也支持与UIAppearance无关的很多属性。Classy使用与UIKit相同属性命名,所以你不必考虑如何将<strong>style property</strong>映射到Objective-C的<strong>property</strong>。<br /> UIPageControl类的属性如下: </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">@property (nonatomic,retain) UIColor *pageIndicatorTintColor; @property (nonatomic,retain) UIColor *currentPageIndicatorTintColor;</pre>     <p> <strong>style property</strong>的名字采用与<strong>objective-c</strong>一样的名字 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">UIPageControl {   pageIndicatorTintColor black   currentPageIndicatorTintColor purple }</pre>     <p> <strong>style property</strong>的命名规则采用<a href="/misc/goto?guid=4958975612889673918" target="_blank"><strong>kebab case</strong></a> </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">UIPageControl {   page-indicator-tint-color black   current-page-indicator-tint-color purple }</pre>     <p> 想了解具体如何使用,请查阅官网<a href="/misc/goto?guid=4959631007477789511" target="_blank">Properties</a>章节 </p>     <h3> Keep it DRY(Don't Repeat Yourself) </h3>     <p> 在编程中一个很重要的原则就是<strong>避免重复</strong>,这不仅可以大量减少重复代码,并且使得代码更加容易复用和维护。Classy提供三种方式避免代码重复:<strong>grouping</strong>, <strong>nesting</strong>,<strong>variables</strong> </p>     <h5> Grouping </h5>     <p> 如果有两个以上的style selectors共用相同的属性时 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">UISlider.info {   minimum-track-tint-color black   maximum-track-tint-color purple }  UISlider.error {   minimum-track-tint-color black   maximum-track-tint-color purple   thumb-tint-color red }</pre>     <p> 我们可以提取相同的属性到分组style selector中 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">UISlider.info, UISlider.error {   minimum-track-tint-color black   maximum-track-tint-color purple }  UISlider.error {   thumb-tint-color red }</pre>     <h5> Nesting </h5>     <p> 如果两个以上style selectors共用相同的view hierarchy时 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">UICollectionView {   background-color #a2a2a2 }  UICollectionView > UICollectionViewCell {   clips-to-bounds NO }  UICollectionView > UICollectionViewCell UILabel {   text-color purple }  UICollectionView > UICollectionViewCell UILabel.title {   font 20 }</pre>     <p> 我们通过nesting方式将view hierarchies表达成这样方式 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">UICollectionView {   background-color #a2a2a2    > UICollectionViewCell {     clips-to-bounds NO      UILabel {       text-color purple        &.title {         font 20       }     }   } }</pre>     <h5> Variables </h5>     <p> Classy让你通过定义variables来将多个相同的style property值存储以便共享。Variable命名规则如下: </p>     <ul>      <li> 必须以<strong>大小写字母</strong>或$符号开头 </li>      <li> <p> 可以包含_,-或任何字母数字 </p> <pre class="brush:cpp; toolbar: true; auto-links: false;">// prefix with ' $ ' to help distinguish variables $brand-color = #e1e1e1  // OR not insets = 5, 10, 5, 10  UIButton {   background-color $brand-color   contentEdgeInsets insets   background-image[state:selected] bg_button insets }</pre> <p> 最后官方还提供一个实例来解释具体如何使用:<a href="/misc/goto?guid=4959631007556491384" target="_blank">Custom Views Example</a> </p> </li>     </ul>     <h1> ClassyLiveLayout </h1>     <p> <a href="/misc/goto?guid=4958968153340291200" target="_blank">ClassyLiveLayout</a>通过结合Classy stylesheets与Masonry一起使用,能够在运行的模拟器中微调Auto Layout约束实时显示效果的工具。 </p>     <p> ClassyLiveLayout一个核心category:UIView+ClassyLayoutProperties,在UIView定义以下属性: </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">@property(nonatomic, assign) UIEdgeInsets cas_margin; @property(nonatomic, assign) CGSize cas_size;  // shorthand properties for setting only a single constant value @property(nonatomic, assign) CGFloat cas_sizeWidth; @property(nonatomic, assign) CGFloat cas_sizeHeight;  @property(nonatomic, assign) CGFloat cas_marginTop; @property(nonatomic, assign) CGFloat cas_marginLeft; @property(nonatomic, assign) CGFloat cas_marginBottom; @property(nonatomic, assign) CGFloat cas_marginRight;</pre>     <p> cas_margin和cas_size分别表示UI元素的位置和大小,而其余的属性都是对两个属性进一步细分。我们可以从stylesheets中访问style properties来定义constraints布局,做到将数据与代码分离,有利于修改和复用代码。 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">UIView.blue-box {     cas_size: 80 100     cas_margin-top: 60     cas_margin-left: 50 }  UIView.red-box {     cas_size-width: 120     cas_margin-left: 20 }</pre>     <p> 我们可以在updateConstraints或updateViewConstrains定义布局时引用style properties </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">- (void)updateViewConstraints {   [super updateViewConstraints];    [_blueBoxView mas_updateConstraints:^(MASConstraintMaker *make) {       make.width.equalTo(@(_blueBoxView.cas_size.width));       make.height.equalTo(@(_blueBoxView.cas_size.height));       make.top.equalTo(@(_blueBoxView.cas_margin.top));       make.left.equalTo(@(_blueBoxView.cas_margin.left));   }];    [_redBoxView mas_updateConstraints:^(MASConstraintMaker *make) {       make.width.equalTo(@(_redBoxView.cas_size.width));       make.height.equalTo(_blueBoxView);       make.top.equalTo(_blueBoxView);       make.left.equalTo(_blueBoxView.mas_right).with.offset(_redBoxView.cas_margin.left);   }]; }</pre>     <p> 当定义view layouts时,将Auto Layout的constraints都放在stylesheets中<strong>实时加载</strong>(Live reload)。如果你修改constraints,无需重新编译、构建和运行模拟器便能实时看到修改后的效果。 </p>     <h1> 示例工程 </h1>     <h3> 配置工程 </h3>     <p> 由于需要引用Masonry,Classy和ClassyLiveLayout,<strong>Podfile</strong>配置如下: </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">pod 'Masonry', '~> 0.6.1' pod 'Classy', '~> 0.2.4' pod 'ClassyLiveLayout', '~> 0.6.0'</pre>     <h3> 编写代码 </h3>     <h5> 1. 添加stylesheet.cas文件到工程 </h5>     <p> 当安装好Masonry,Classy和ClassyLiveLayout后,第一次运行项目会出现没有stylesheet.cas文件错误: </p>     <div href="https://simg.open-open.com/show/0fbfec004a7eae8dc0ebb749a65562bf.png">      <img src="https://simg.open-open.com/show/0fbfec004a7eae8dc0ebb749a65562bf.png" alt="No stylesheet.cas file error.png" height="50.239234449760765" width="700" />      <br />      <div>       No stylesheet.cas file error.png      </div>     </div>     <p> 只要向工程添加空的stylesheet.cas文件即可。 </p>     <div href="https://simg.open-open.com/show/ea59abe600866916d97092b96a1a767a.png">      <img src="https://simg.open-open.com/show/ea59abe600866916d97092b96a1a767a.png" alt="Create stylesheet.cas file.png" width="514" height="656" />      <br />      <div>       Create stylesheet.cas file.png      </div>     </div>     <h5> 2. 创建LiveView类,该类继承SHPAbstractView。 </h5>     <div href="https://simg.open-open.com/show/379043df9f0dde2f194dc7e7367ae49b.png">      <img src="https://simg.open-open.com/show/379043df9f0dde2f194dc7e7367ae49b.png" alt="Create LiveView inherit SHPAbstractView.png" height="221.28227960819237" width="700" />      <br />      <div>       Create LiveView inherit SHPAbstractView.png      </div>     </div>     <p> 在ViewController创建LiveView对象,然后被self.view引用。 </p>     <div href="https://simg.open-open.com/show/08249f51b72e19e2d912b18aa11ade77.png">      <img src="https://simg.open-open.com/show/08249f51b72e19e2d912b18aa11ade77.png" alt="Setup root view in ViewController.png" height="258.5191793041927" width="700" />      <br />      <div>       Setup root view in ViewController.png      </div>     </div>     <p> 当编译运行时,在SHPAbstractView.h由于找不到UIView出现编译错误。 </p>     <div href="https://simg.open-open.com/show/8b862baa89308c5b65a4ac7dca092de2.png">      <img src="https://simg.open-open.com/show/8b862baa89308c5b65a4ac7dca092de2.png" alt="SHPAbstractView Compile error.png" height="417.0603674540682" width="700" />      <br />      <div>       SHPAbstractView Compile error.png      </div>     </div>     <p> 只需引入UIKit便可以解决,但运行一下应用程序,出现一下错误: </p>     <div href="https://simg.open-open.com/show/2a0994e356de26aa79b16120073db5aa.png">      <img src="https://simg.open-open.com/show/2a0994e356de26aa79b16120073db5aa.png" alt="Must override methods.png" width="700" height="68.25" />      <br />      <div>       Must override methods.png      </div>     </div>     <p> 主要原因是任何自定义UIView继承SHPAbstractView都需要override两个方法:- (void)addSubviews和- (void)defineLayout,我们可以查看SHPAbstractView的源码可知: </p>     <div href="https://simg.open-open.com/show/201ecc8d769e3159d556d3974e012e0c.png">      <img src="https://simg.open-open.com/show/201ecc8d769e3159d556d3974e012e0c.png" alt="SHPAbstractView Source Code .png" height="211.62790697674419" width="700" />      <br />      <div>       SHPAbstractView Source Code .png      </div>     </div>     <p> 所以只要在LiveView.m文件覆盖两个方法即可 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">#pragma mark - Add subviews and define layout - (void)addSubviews { }  - (void)defineLayout { }</pre>     <h5> 3. LiveView类设计 </h5>     <p> LiveView主要由包含redBoxView和blueBoxView两个属性,redBoxView表示红色方块,blueBoxView表示蓝色方块。 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">#import "SHPAbstractView.h"  @interface LiveView : SHPAbstractView  @property (strong, nonatomic) UIView *redBoxView; @property (strong, nonatomic) UIView *blueBoxView;  @end</pre>     <h5> 4. LiveView类实现 </h5>     <p> 由于SHPAbstractView类如何初始化View已经做了处理,暴露两个接口- (void)addSubviews和-(void)defineLayout分别处理构建view hierarchy和定义布局,<strong>子类</strong>只要覆盖SHPAbstractView这两个方法就可以创建LiveView了。<br /> 但是我们将Auto Layout的constraints都放在stylesheets中实时加载(Live reload),即放在本工程的stylesheet.cas文件,将布局数据和布局代码分离。 </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">UIView.redBox {     cas_marginTop 50     cas_marginLeft 20      cas_size 100 100 }  UIView.blueBox {     cas_marginTop 50     cas_marginRight -20      cas_size 100 100 }</pre>     <p> 有了constraints数据后,便可以在代码布局: </p>     <pre class="brush:cpp; toolbar: true; auto-links: false;">@implementation LiveView  #pragma mark - Add subviews and define layout - (void)addSubviews {     self.backgroundColor = [UIColor whiteColor];     [self addSubview:self.redBoxView];     [self addSubview:self.blueBoxView]; }  - (void)defineLayout {     [self.redBoxView mas_updateConstraints:^(MASConstraintMaker* make){         make.top.equalTo(@(self.redBoxView.cas_marginTop));         make.left.equalTo(@(self.redBoxView.cas_marginLeft));         make.width.equalTo(@(self.redBoxView.cas_sizeWidth));         make.height.equalTo(@(self.redBoxView.cas_sizeHeight));     }];      [self.blueBoxView mas_updateConstraints:^(MASConstraintMaker *make){         make.top.equalTo(@(self.blueBoxView.cas_marginTop));         make.right.equalTo(@(self.blueBoxView.cas_marginRight));         make.width.equalTo(@(self.blueBoxView.cas_sizeWidth));         make.height.equalTo(@(self.blueBoxView.cas_sizeHeight));     }]; }  #pragma mark - Lazy initialization - (UIView*)redBoxView {     if (!_redBoxView) {         _redBoxView = [UIView new];         _redBoxView.cas_styleClass = @"redBox";         _redBoxView.backgroundColor = [UIColor redColor];     }      return _redBoxView; }  - (UIView*)blueBoxView {     if (!_blueBoxView) {         _blueBoxView = [UIView new];         _blueBoxView.cas_styleClass = @"blueBox";         _blueBoxView.backgroundColor = [UIColor blueColor];     }      return _blueBoxView; }</pre>     <h5> 5. 模拟器支持Live Reload </h5>     <p> 为了启用Live Reload,你需要指定stylesheet路径,并且只运行在模拟器上。 </p>     <div href="https://simg.open-open.com/show/85abd94f673999f7a4bf6efd73b8050c.png">      <img src="https://simg.open-open.com/show/85abd94f673999f7a4bf6efd73b8050c.png" alt="Support Live Reload.png" height="271.49532710280374" width="700" />      <br />      <div>       Support Live Reload.png      </div>     </div>     <h3> 最后效果 </h3>     <div href="https://simg.open-open.com/show/9193ff629a9c086fcb1b76e437afddb6.gif">      <img src="https://simg.open-open.com/show/9193ff629a9c086fcb1b76e437afddb6.gif" alt="Live Change.gif" height="560.5433376455369" width="700" />      <br />      <div>       Live Change.gif      </div>     </div>     <p> <strong>示例代码</strong>存放地址:<a href="/misc/goto?guid=4959631007672463102" target="_blank">LiveAutoLayout</a> </p>     <h1> 总结 </h1>     <p> 之前手写UI代码每次更改一般都要重新编译、构建和运行模拟器才能看到效果,但结合使用Masonry,Classy和 ClassLiveLayout之后,告别这个费时过程,极大地提高开发速度;不仅如此,我们将Auto Layout的constraints都放在stylesheets中实时加载(Live reload),将布局数据和布局代码分离,使得代码更加复用和维护。Classy还提供三种<strong>避免重复</strong>方法:Grouping, Nestting和Variable,尽可能复用样式数据。<br /> 这是本人第一次编写技术博客,可能有很多错误和漏洞,希望大家多多指点,也希望这篇文章能够帮助到大家。 </p>     <h1> 扩展阅读 </h1>     <ul>      <li> <p> <strong>Storyboard/XIB与手写代码的选择</strong><br /> <a href="/misc/goto?guid=4958855270046018655" target="_blank">代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧</a><br /> <a href="/misc/goto?guid=4958870578500644667" target="_blank">iOS 开发中的争议(二)</a> </p> </li>      <li> <p> <strong>Storyboard可视化开发</strong><br /> <a href="/misc/goto?guid=4959631007832793491" target="_blank">Adaptive Layout Tutorial: Getting Started</a><br /> <a href="/misc/goto?guid=4959631007941756269" target="_blank">WWDC 2014 Session笔记 - 可视化开发,IB 的新时代</a> </p> </li>      <li> <p> <strong>AutoLayout与Masonry</strong><br /> <a href="/misc/goto?guid=4959630027826027070" target="_blank">iOS 开发实践之 Auto Layout</a><br /> <a href="/misc/goto?guid=4958879510733920857" target="_blank">Masonry介绍与使用实践</a> </p> </li>      <li> <p> <strong>Auto Layout WWDC 视频集合</strong><br /> <a href="/misc/goto?guid=4959631008202454936" target="_blank">WWDC 2012: Introduction to Auto Layout for iOS and OS X</a><br /> <a href="/misc/goto?guid=4959631008316316445" target="_blank">WWDC 2012: Best Practices for Mastering Auto Layout</a><br /> <a href="/misc/goto?guid=4959631008440269674" target="_blank">WWDC 2012: Auto Layout by Example</a><br /> <a href="/misc/goto?guid=4959631008563005389" target="_blank">WWDC 2013: Taking Control of Auto Layout in Xcode 5</a> </p> </li>     </ul>    </div> 来自:   <a target="_blank" href="/misc/goto?guid=4959631008677806009">http://www.jianshu.com/p/2ed5f7444900#</a>