ios怎样快速的熟悉别人写的代码快速创建ui ios

实时显示iOS编写UI代码效果 - 简书
下载简书移动应用
写了54993字,被2522人关注,获得了1821个喜欢
实时显示iOS编写UI代码效果
编写iOS应用UI的方式大概有两种,一种是Storyboard/Xib,另一种是手写代码。采用Storyboard/Xib方式组织UI,由于提供可视化的特性,只要从UI库中拖动UI控件,便可以显示结果,极大地提高开发速度。但面临一个问题就是多人协作开发,由于所有的UI都放在同一个Storyboard文件中,使用Git/SVN合并代码就会出现冲突。多人协作开发还不是主要问题,有人提出可以创建多个Storyboard来分开UI编写,而Storyboard/Xib最主要问题是代码复用性比较差。所以有些人就选择手写UI代码,这样不仅可以解决多人协作开发问题,而且通过自定义控件在多个View使用。但每次手写UI代码后都要编译、构建和运行,最后在模拟器显示,这样会拖慢开发速度。如果每次修改UI控件后,保存修改便实时在模拟器显示修改后结果,就可以极大的提高编写UI的速度。
Live Change.gif
Auto Layout
Auto Layout是什么
是一个基于constraint(约束)的布局系统,它根据UI元素之间约束关系来调整UI元素的位置和大小。
Auto Layout解决什么问题
更容易适配不同分辨率设备的屏幕(iPhone 6 Plus, iPhone 6, iPhone 5s/5, iPhone 4s/4)
当设备旋转时不需要做额外处理
使用constraint来描述布局逻辑,更利于理解和清晰
如何使用Auto Layout
Auto Layout中约束的类对应是, 而创建NSLayoutConstraint对象主要有两种方式,第一种是
+ (id)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attribute1
relatedBy:(NSLayoutRelation)relation
toItem:(id)view2
attribute:(NSLayoutAttribute)attribute2
multiplier:(CGFloat)multiplier
constant:(CGFloat)
上面方法主要意思是,某个view1的attribute1等于(小于或等于/大于或等于)某个view2的attribute2的multiplier倍加上constant。而attribute主要由表示位置(上/下/左/右)和大小(宽/高)的以下几个值:
typedef enum: NSInteger {
NSLayoutAttributeLeft = 1,
NSLayoutAttributeRight,
NSLayoutAttributeTop,
NSLayoutAttributeBottom,
NSLayoutAttributeLeading,
NSLayoutAttributeTrailing,
NSLayoutAttributeWidth,
NSLayoutAttributeHeight,
NSLayoutAttributeCenterX,
NSLayoutAttributeCenterY,
NSLayoutAttributeBaseline,
NSLayoutAttributeNotAnAttribute = 0
} NSLayoutA
简化一下,使用公式可以表达为:
view1.attribute1 = view2.attribute2 * multiplier + constant
第二种方式是:
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format
options:(NSLayoutFormatOptions)opts
metrics:(NSDictionary *)metrics
views:(NSDictionary *)
这种方式主要是采用(可视化格式语言)来描述约束布局,虽然语法比较简洁,但是可读性比较差和容易出错。
Auto Layout存在问题
虽然Auto Layout在布局view方面是非常强大和灵活,但是创建constraint的语法过于繁杂,引用一个例子:
UIView *superview =
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],
如此简单的一个例子都要编写这么多行代码,想象一下如果创建多个view的constraint时会多么痛苦啊。另一个方式是采用Visual Format Language (VFL),虽然语法比较简洁,但是可读性比较差和容易出错。
为什么使用Masonry
是采用链式来封装NSLayoutConstraint,通过这种方式编写Auto Layout布局代码更加易读和简洁。使用Masonry的MASConstraintMaker来表达相同constraint
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);
甚至可以更短
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
使用Masonry创建constraint来定义布局的方式有三种:mas_makeConstraints,mas_updateConstraints,mas_remakeConstraints。
1. mas_makeConstraints
使用mas_makeConstraints创建constraint后,你可以使用局部变量或属性来保存以便下次引用它;如果创建多个constraints,你可以采用数组来保存它们。
// in public/private interface
@property (nonatomic, strong) MASConstraint *topC
// 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];
2. mas_updateConstraints
有时你需要更新constraint(例如,动画和调试)而不是创建固定constraint,可以使用mas_updateConstraints方法
// 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];
3. mas_remakeConstraints
mas_remakeConstraints与mas_updateConstraints比较相似,都是更新constraint。不过,mas_remakeConstraints是删除之前constraint,然后再添加新的constraint(适用于移动动画);而mas_updateConstraints只是更新constraint的值。
- (void)changeButtonPosition {
[self.button mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.equalTo(self.buttonSize);
if (topLeft) {
make.top.and.left.offset(10);
make.bottom.and.right.offset(-10);
想了解以上三个代码片段的更多细节,可以下载Masonry iOS Examples工程查阅。
Classy简介和特性
是一个能与UIKit无缝结合stylesheet(样式)系统。它借鉴的思想,但引入新的语法和命名规则。
灵活内嵌的语法
{ } : ; 这些语法符号是可选的,你可以选择适合自己的风格来表达stylesheet。
你可以使用{ } : ; 来限定stylesheet
$main-color = #e1e1e1;
MYCustomView {
background-color: $main-
title-insets: 5, 10, 5, 10;
& UIProgressView.tinted {
progress-tint-color:
track-tint-color:
^UIButton.warning, UIView.warning ^UIButton {
title-color[state:highlighted]: #e3e3e3;
或者你使用空格来限定stylesheet
$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
Classy在应用程序Bundle默认查找文件名为stylesheet.cas的样式文件。如果你采用这个文件名,你可以不用做任何东西就能加载样式文件。但如果你想指定其他file path(样式文件名),你可以创建[CASStyler defaultStyler]
[CASStyler defaultStyler].filePath = [[NSBundle mainBundle] pathForResource:@"myStyles.cas" ofType:nil];
如果你还想当发生错误时,获取错误信息以便于调试,可以使用-(void)setFilePath:error:
NSError *error =
NSString filePath = [[NSBundle mainBundle] pathForResource:@"myStyles.cas" ofType:nil];
[[CASStyler defaultStyler] setFilePath:filePath error:&error];
如果你是使用Storyboard/Xib组织UI界面,那就需要在main.m的int main(int argc, char * argv[])方法设置 filePath,这样可以确保在创建UIWindow之前加载stylesheet。否则(采用手写UI代码),你在 AppDelegate.m的- (BOOL)application:didFinishLaunchingWithOptions:方法设置filePath
Live Reload
Live Reload是实时显示编写UI代码效果的关键特性,它能够实时检查stylesheet文件变化,无需重新编译、构建和运行模拟器,从而极大提高开发速度。为了启用Live Reload,你需要指定stylesheet路径,并且只运行在模拟器上。
#if TARGET_IPHONE_SIMULATOR
NSString *absoluteFilePath = CASAbsoluteFilePath(@"../Styles/stylesheet.cas");
[CASStyler defaultStyler].watchFilePath = absoluteFileP
Style Selectors是指定哪个view使用哪种样式的方式。主要有三种方法来指定目标view:
Object Class
View Hierarchy
Style Class
你可以混合使用三种方法,例子如下:
/* match views
* where class is UIButton or UIButton subclass
* and styleClass is "large"
* and superview class is UITabBar
UITabBar & ^UIButton.large { }
想了解具体如何使用,请查阅官网章节
为了避免与Objective-C的message selectors混淆,术语style selectors表示Classy stylesheets的selectors
Properties
Classy支持所有,也支持与UIAppearance无关的很多属性。Classy使用与UIKit相同属性命名,所以你不必考虑如何将style property映射到Objective-C的property。UIPageControl类的属性如下:
@property (nonatomic,retain) UIColor *pageIndicatorTintC
@property (nonatomic,retain) UIColor *currentPageIndicatorTintC
style property的名字采用与objective-c一样的名字
UIPageControl {
pageIndicatorTintColor black
currentPageIndicatorTintColor purple
style property的命名规则采用
UIPageControl {
page-indicator-tint-color black
current-page-indicator-tint-color purple
想了解具体如何使用,请查阅官网章节
Keep it DRY(Don't Repeat Yourself)
在编程中一个很重要的原则就是避免重复,这不仅可以大量减少重复代码,并且使得代码更加容易复用和维护。Classy提供三种方式避免代码重复:grouping, nesting,variables
如果有两个以上的style selectors共用相同的属性时
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
我们可以提取相同的属性到分组style selector中
<, UISlider.error {
minimum-track-tint-color black
maximum-track-tint-color purple
UISlider.error {
thumb-tint-color red
如果两个以上style selectors共用相同的view hierarchy时
UICollectionView {
background-color #a2a2a2
UICollectionView & UICollectionViewCell {
clips-to-bounds NO
UICollectionView & UICollectionViewCell UILabel {
text-color purple
UICollectionView & UICollectionViewCell UILabel.title {
我们通过nesting方式将view hierarchies表达成这样方式
UICollectionView {
background-color #a2a2a2
& UICollectionViewCell {
clips-to-bounds NO
text-color purple
Classy让你通过定义variables来将多个相同的style property值存储以便共享。Variable命名规则如下:
必须以大小写字母或$符号开头
可以包含_,-或任何字母数字
// prefix with ' $ ' to help distinguish variables
$brand-color = #e1e1e1
insets = 5, 10, 5, 10
UIButton {
background-color $brand-color
contentEdgeInsets insets
background-image[state:selected] bg_button insets
最后官方还提供一个实例来解释具体如何使用:
ClassyLiveLayout
通过结合Classy stylesheets与Masonry一起使用,能够在运行的模拟器中微调Auto Layout约束实时显示效果的工具。
ClassyLiveLayout一个核心category:UIView+ClassyLayoutProperties,在UIView定义以下属性:
@property(nonatomic, assign) UIEdgeInsets cas_
@property(nonatomic, assign) CGSize cas_
// shorthand properties for setting only a single constant value
@property(nonatomic, assign) CGFloat cas_sizeW
@property(nonatomic, assign) CGFloat cas_sizeH
@property(nonatomic, assign) CGFloat cas_marginT
@property(nonatomic, assign) CGFloat cas_marginL
@property(nonatomic, assign) CGFloat cas_marginB
@property(nonatomic, assign) CGFloat cas_marginR
cas_margin和cas_size分别表示UI元素的位置和大小,而其余的属性都是对两个属性进一步细分。我们可以从stylesheets中访问style properties来定义constraints布局,做到将数据与代码分离,有利于修改和复用代码。
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
我们可以在updateConstraints或updateViewConstrains定义布局时引用style properties
- (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);
当定义view layouts时,将Auto Layout的constraints都放在stylesheets中实时加载(Live reload)。如果你修改constraints,无需重新编译、构建和运行模拟器便能实时看到修改后的效果。
由于需要引用Masonry,Classy和ClassyLiveLayout,Podfile配置如下:
pod 'Masonry', '~& 0.6.1'
pod 'Classy', '~& 0.2.4'
pod 'ClassyLiveLayout', '~& 0.6.0'
1. 添加stylesheet.cas文件到工程
当安装好Masonry,Classy和ClassyLiveLayout后,第一次运行项目会出现没有stylesheet.cas文件错误:
No stylesheet.cas file error.png
只要向工程添加空的stylesheet.cas文件即可。
Create stylesheet.cas file.png
2. 创建LiveView类,该类继承SHPAbstractView。
Create LiveView inherit SHPAbstractView.png
在ViewController创建LiveView对象,然后被self.view引用。
Setup root view in ViewController.png
当编译运行时,在SHPAbstractView.h由于找不到UIView出现编译错误。
SHPAbstractView Compile error.png
只需引入UIKit便可以解决,但运行一下应用程序,出现一下错误:
Must override methods.png
主要原因是任何自定义UIView继承SHPAbstractView都需要override两个方法:- (void)addSubviews和- (void)defineLayout,我们可以查看SHPAbstractView的源码可知:
SHPAbstractView Source Code .png
所以只要在LiveView.m文件覆盖两个方法即可
#pragma mark - Add subviews and define layout
- (void)addSubviews
- (void)defineLayout
3. LiveView类设计
LiveView主要由包含redBoxView和blueBoxView两个属性,redBoxView表示红色方块,blueBoxView表示蓝色方块。
#import "SHPAbstractView.h"
@interface LiveView : SHPAbstractView
@property (strong, nonatomic) UIView *redBoxV
@property (strong, nonatomic) UIView *blueBoxV
4. LiveView类实现
由于SHPAbstractView类如何初始化View已经做了处理,暴露两个接口- (void)addSubviews和-(void)defineLayout分别处理构建view hierarchy和定义布局,子类只要覆盖SHPAbstractView这两个方法就可以创建LiveView了。但是我们将Auto Layout的constraints都放在stylesheets中实时加载(Live reload),即放在本工程的stylesheet.cas文件,将布局数据和布局代码分离。
UIView.redBox {
cas_marginTop 50
cas_marginLeft 20
cas_size 100 100
UIView.blueBox {
cas_marginTop 50
cas_marginRight -20
cas_size 100 100
有了constraints数据后,便可以在代码布局:
@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 _redBoxV
- (UIView*)blueBoxView
if (!_blueBoxView) {
_blueBoxView = [UIView new];
_blueBoxView.cas_styleClass = @"blueBox";
_blueBoxView.backgroundColor = [UIColor blueColor];
return _blueBoxV
5. 模拟器支持Live Reload
为了启用Live Reload,你需要指定stylesheet路径,并且只运行在模拟器上。
Support Live Reload.png
此时效果:
Live Change.gif
6. 分离样式文件
由于有网友提出这样一个问题:如果所有view的样式都放在同一个stylesheet.cas文件,会让stylesheet.cas文件繁杂,并且当多个人协同开发时,不易于合并代码,所以有必要将样式文件分离到多个文件中。
创建variable.cas文件,并将redBox对应UIView的样式放在variable.cas文件中。
variable.cas file.png
在stylesheet.cas样式文件使用@import指令引用variable.cas文件
stylesheet.cas file.png
Live Change 1.gif
Live Change 2.gif
示例代码存放地址:
之前手写UI代码每次更改一般都要重新编译、构建和运行模拟器才能看到效果,但结合使用Masonry,Classy和ClassLiveLayout之后,告别这个费时过程,极大地提高开发速度;不仅如此,我们将Auto Layout的constraints都放在stylesheets中实时加载(Live reload),将布局数据和布局代码分离,使得代码更加复用和维护。Classy还提供三种避免重复方法:Grouping, Nestting和Variable,尽可能复用样式数据。这是本人第一次编写技术博客,可能有很多错误和漏洞,希望大家多多指点,也希望这篇文章能够帮助到大家。
Storyboard/XIB与手写代码的选择
Storyboard可视化开发
AutoLayout与Masonry
Auto Layout WWDC 视频集合
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
简书程序员大本营
投稿须知:
1.本专题仅收录与程序有关的文章。
2.请在代码框里写代码,尽量保证可看性。
关注简书官...
· 101384人关注
分享 iOS 开发的知识,解决大家遇到的问题,讨论iOS开发的前沿,欢迎大家投稿~
· 21208人关注
专题内容主要包括OC、swift等涉及到iOS开发进阶的内容。
swift可以关注下我的另一个专题:
swift开发...
· 16613人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:& & &&本文由海水的味道编译整理,转载请注明译者和出处,请勿用于商业用途!
属性和幂等方法(多次调用和一次调用返回的结果相同)使用点标记语法访问,其他的情况使用方括号标记语法。
良好的风格:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].
不良的风格:
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.
二元运算符和参数之间需要放置一个空格,一元运算符、强制类型转换和参数之间不放置空格。关键字之后圆括号之前需要放置一个空格。
void *ptr = &value + 10 * 3;
NewType a = (NewType)b;
for (int i = 0; i & 10; i++) {
&&& doCoolThings();
数组和字典类型的字面值的方括号两边各放置一个空格。
NSArray *theShit = @[ @1, @2, @3 ];
字典字面值的键和冒号之间没有空格,冒号和值之间有一个空格。
NSDictionary *keyedShit = @{ GHDidCreateStyleGuide: @YES };
C函数声明中,左括号的前面不保留空格,并且函数名应该像类一样带有命名空间标识。
良好的风格:
void RNCwesomeFunction(BOOL hasSomeArgs);
长的字面值应被拆分为多行。
良好的风格:
NSArray *theShit = @[
&&& @"Got some long string objects in here.",
&&& [AndSomeModelObjects too],
&&& @"Moar strings."
NSDictionary *keyedShit = @{
&&& @"this.key": @"corresponds to this value",
&&& @"otherKey": @"remoteData.payload",
&&& @"some": @"more",
&&& @"JSON": @"keys",
&&& @"and": @"stuff",
每一行代码使用4个空格缩进。不使用tab缩进。下图是在Xcode的Preferences进行缩进设置的截图。&&
方法签名以及其他关键字(if/else/switch/while等)后面跟随的左花括号总是和语句出现于同一行,而右花括号独占一行。
良好的风格:
if (user.isHappy) {
//Do something
//Do something else
如果一个方法内有多个功能区域,可以使用空行分隔功能区域。
每一行代码不要超过100个字符。
每一个方法之前都有一个99字符宽的注释行,注释行相对于使用空行更能提高代码的辨识度,当一行代码很长的时候,注释行也起到了越界检测的作用。注释行:
///////////////////////////////////////////////////////////////////////////////////////////////////
当需要满足一定条件时才执行某项操作时,最左边缘应该是代码。不要将愉快路径代码内嵌到if语句中。多个return是正常合理的。良好的风格做法:
- (void)someMethod {
& if (![someOther boolValue]) {
& //Do something important
不良的风格:
- (void)someMethod {
& if ([someOther boolValue]) {
&&& //Do something important
& & & & 所有的逻辑块必须使用花括号包围,即使条件体只需编写一行代码也必须使用花括号。良好的风格做法:
if (!error) {
}不良的风格:
if (!error)
if (!error)
长的三元运算符应使用圆括号括起来。三元运算符仅用于赋值和做参数。
Blah *a = (stuff == thing ? foo : bar);
合并的nil三元运算符应该尽量避免。
不良的风格:
Blah *b = thingThatCouldBeNil ?: defaultV
多分支条件应该使用if语句或重构为实例变量。
良好的风格:
result = a & b ? x :
不良的风格:
result = a & b ? x = c & d ? c : d :
不要在流控制语句中使用异常(NSException)。
异常仅用于表明程序员的错误。
为了表明一个错误,使用NSError *。
当一个方法通过引用返回一个错误参数,应该检测返回值的状态,而不是错误参数的状态。
良好的风格:
if (![self trySomethingWithError:&error]) {
&&& // Handle Error
不良的风格:
[self trySomethingWithError:&error];
if (error) {
&&& // Handle Error
在方法执行成功的情况下赋值非Null值给错误参数,会使路径跳转到假条件分支(随后程序奔溃)。
除了继承一个类或实现一个协议,否则在头文件中仅使用类声明@class指令,不用#import导入类头文件。
如果一个delegate只有几个方法,比如只是提交和取消,推荐使用block编写动作响应代码。使用block还是delegate编写回调代码遵循以下几点:(详见参考链接[8])&#216;如果对象存在多个不同事件,则应该使用代理模式编写各事件的处理代码
&#216;如果对象是单例,应该使用block,而不是代理。
&#216;如果对象是为了其他的信息而进行回调,则使用代理。
&#216;代理更多的是面向于过程,而block则更多的面向于结果。
由于代理方法的声明一般都很长,所以必须将代理对象和其他的协议对象放在实例变量定义的下面,否则实例变量定义的对齐方式将会被打乱掉。
当需要实现多个协议的时候,将每一个协议名拆分到单独的行。
良好的风格:
@interface CustomModelViewController : TTViewController &
& TTModelDelegate,
& TTURLRequestDelegate
一个方法的命名首先描述返回什么,接着是什么情况下被返回。方法签名中冒号的前面描述传入参数的类型。以下类方法和实例方法命名的格式语法:
[object/class thing+condition];
[object/class thing+input:input];
[object/class thing+identifer:input];
Cocoa命名举例:
realPath&&& = [path&&&& stringByExpandingTildeInPath];
fullString& = [string&& stringByAppendingString:@"Extra Text"];
object&&& &&= [array&&& objectAtIndex:3];
newString&& = [NSString stringWithFormat:@"%f",1.5];
newArray&&& = [NSArray& arrayWithObject:newString];
良好的自定义方法命名风格:
recipients& = [email&&& recipientsSortedByLastName];
newEmail&&& = [CDCEmail emailWithSubjectLine:@"Extra Text"];
emails&&&&& = [mailbox& messagesReceivedAfterDate:yesterdayDate];
当需要获取对象值的另一种类型的时候,方法命名的格式语法如下:
[object adjective+thing];
[object adjective+thing+condition];
[object adjective+thing+input:input];
良好的自定义方法命名风格:
capitalized = [name&&& capitalizedString];
rate&&&&&&& = [number& floatValue];
newString&& = [string& decomposedStringWithCanonicalMapping];
subarray&&& = [array&& subarrayWithRange:segment];
方法签名尽量做到含义明确。
不良的风格:
-sortInfo& // 是返回排序结果还是给info做排序
-refreshTimer& // 返回一个用于刷新的定时器还是刷新定时器
-update& // 更新什么,如何更新
良好的风格:
-currentSortInfo&&&&& // "current" 清楚地修饰了名词SortInfo
-refreshDefaultTimer& // refresh是一个动词。
-updateMenuItemTitle& // 一个正在发生的动作
方法类型修饰符+/-后要放置一个空格,各参数名之间也要放置一个空格。
良好的风格:
- (void)setExampleText:(NSString *)text image:(UIImage *)
如果方法的命名特别长,将方法名拆分成多行。
良好的风格:
color = [NSColor colorWithCalibratedHue: 0.10
&&&&&&&&&&&&&&&&&&&&&&&&&&& &&&saturation: 0.82
&&&&&&&&&&&&&&&&&&&&&&&&&&&& &brightness: 0.89
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &&alpha: 1.00];
不要将私有的实例变量和方法声明在头文件中,应将私有变量和方法声明在实现文件的类扩展内。
不良的风格:
//MyViewController.h文件
@interface MyViewController : UIViewController&
&UITalbeViewDataSource,
&UITableViewDelegate& {
&@private:
& UITableView *_myTableV& // 私有实例变量
// 内部使用的属性
@property (nonatomic,strong) NSNumber *variableUsedI
- (void)sortN& // 只用于内部使用的方法
良好的风格:
//MyViewController.m文件使用类扩展
@interface MyViewController()&
&UITalbeViewDataSource,
&UITableViewDelegate& {
& UITableView *_myTableV
// 外部需要访问的实例变量声明为属性,不需要外部访问的声明为实例变量
& NSNumber * variableUsedI
// 从Xcode4.3开始,可以不写方法的前置声明,Interface Builder和Storyboard仍然可以找到方法的定义
构造函数通常应该返回实例类型而不是id类型
方法参数名前一般使用的前缀包括&#8220;the&#8221;、&#8220;an&#8221;、&#8220;new&#8221;。
良好的风格:
- (void)&&&&&& setTitle:&&&&&&&&&& (NSString *)&& aT
- (void)&&&&&& setName:&&&&&&&&&&& (NSString *)&& newN
- (id)&&&&&&&& keyForOption:&&&&&& (CDCOption *)& anOption
- (NSArray *)& emailsForMailbox:&& (CDCMailbox *) theM
- (CDCEmail *) emailForRecipients: (NSArray *)&&& theR
变量的命令应尽量做到自描述。除了在for()循环语句中,单字母的变量应该避免使用(如i,j,k等)。一般循环语句的当前对象的命名前缀包括&#8220;one&#8221;、&#8220;a/an&#8221;。对于简单的单个对象使用&#8220;item&#8221;命名。
良好的风格:
for (i = 0; i & i++) {
&&& oneObject = [allObjects objectAtIndex: i];
&&& NSLog (@"oneObject: %@", oneObject);
NSEnumerator *e = [allObjects objectEnumerator];
while (item = [e nextObject])
&&&&& NSLog (@"item: %@", item);
指针变量的星号指示符应该紧靠变量,比如NSString *text,而不是NSString* text或NSString * text。
尽量的使用属性而非实例变量。除了在初始化方法(init,initWithCoder:等)、dealloc方法以及自定义setter与getter方法中访问属性合成的实例变量,其他的情况使用属性进行访问。
良好的风格:
@interface RNCSection: NSObject
@property (nonatomic) NSString *
不良的风格:
@interface RNCSection : NSObject {
&&& NSString *
当你使用@synthesize指令时,编译器会自动为你创建一个下划线_开头的的实例变量,所以不需要同时声明实例变量和属性。
不良的风格:
@interface RNCSection : NSObject {
&&& NSString *
@property (nonatomic) NSString *
良好的风格:
@interface RNCSection: NSObject
@property (nonatomic) NSString *
协议中的@optional可选属性必须被显式地使用@synthesize指令合成属性。
虽然方法命名不应使用缩略词,然而有些缩略词在过去被反复的使用,所以使用这些缩略词能更好的的表达代码的含义。下表列出了Cocoa可接受的缩略词。
含义和备注
分配,拨出
轮流,交替
应用程序。比如NSApp表示全局程序对象。
销毁、析构
Builder文档
黏贴板(仅对常量)
临时、暂时
以下是一些常用的首字母缩略词
方法和变量的命令应该尽可能做到自描述。
良好的风格:
UIButton *settingsB
不良的风格:
UIButton *setB
对于NSString、NSArray、NSNumber或BOOL类型,变量的命名一般不需要表明其类型。
良好的风格:
NSString&&&&&& *accountN
NSMutableArray *
NSArray&&&&&&& *defaultH
BOOL&&&&&&&&&&&& userInputWasU
不良的风格:
NSString&&&&& &&*accountNameString;
NSMutableArray *mailboxArray;
NSArray&&&&&&& *defaultHeadersArray;
BOOL&&&&&&&&&&&& userInputWasUpdatedBOOL;
如果变量不是以上基本常用类型,则变量的命名就应该反映出自身的类型。但有时仅需要某些类的一个实例的情况下,那么只需要基于类名进行命名。
NSImage&&&&&&&&&& &&&&*previewPaneImage;&
NSProgressIndicator &*uploadIndicator;&
NSFontManager&&&&& &&*fontManager;&&&&&& // 基于类名命名
大部分情况下,NSArray或NSSet类型的变量只需要使用单词复数形式(比如mailboxes),不必在命名中包含&#8220;mutable&#8221;。如果复数变量不是NSArray或NSSet类型,则需要指定其类型。
良好的风格:
NSDictionary * keyedAccountN
NSDictionary * messageDictionary;
NSIndexSet&& * selectedMailboxesIndexSet;
由于Objective-C不支持名字空间,为了防止出现命名空间的冲突,在类名和常类型变量名前添加一个由三个大写的字母组成的前缀(如RNC),对于Core Data实体名则可以忽略此规则。如果你子类化了标准的Cocoa类,将前缀和父类名合并是一个很好的做法。如继承UITableView的类可命名为RNCTableView。
常类型变量名的书写风格采用驼峰式大小写(第一个单词的首字母小写,其余单词的第一个字母大写。如firstName而不是first_name或firstname。),并使用关联的类名作为其命名前缀,
推荐的做法:
static const NSTimeInterval RNCArticleViewControllerNavigationFadeAnimationDuration = 0.3;
不推荐的做法:
static const NSTimeInterval fadetime = 1.7;
使用属性的时候,实例变量应该使用self.进行访问和设值。局部变量的命令不要包含下划线。实例变量的命名必须使用下划线_作为前缀,这样可以缩小Xcode自动完成的选项取值范围。
在需要的时候,注释可对代码做必要的解释。更新代码时一定要更新注释,防止对代码造成误解。
使用javadoc风格的文档注释语法。注释的第一行是对注释API的总结,随后的注释行是对代码更多细节的解释。
良好的风格:
&* The maximum size of a download that is allowed.
&* If a response reports a content length greater than the max * will be cancelled. This is helpful for preventing excessive memory usage.
&* Setting this to zero will allow all downloads regardless of size.
&* @default 150000 bytes
@property (nonatomic) NSUInteger maxContentL
initdealloc
dealloc方法应该被放置在实现方法的顶部,直接在@synthesize或@dynamic语句之后。init方法应该被放置在dealloc方法的下面。
init方法的结构看上去应该像这样:
- (instancetype)init {
&&& self = [super init]; // or call the designated initalizer
&&& if (!self) {
& & &&// Custom initialization
对于NSString,NSDictionary,NSArray和NSNumber类,当需要创建这些类的不可变实例时,应该使用这些类的字面值表示形式。使用字面值表示的时候nil不需要传入NSArray和NSDictionary中作为字面值。这种语法兼容老的iOS版本,因此可以在iOS5或者更老的版本中使用它。
良好的风格:
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
不良的风格:
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018];
如非必要,避免使用特定类型的数字(相较于使用5.3f,应使用5.3)。
相较于使用结构体辅助函数(如CGRectMake()函数),优先使用C99结构体初始化语法。
& CGRect rect = {.origin.x = 3.0, .origin.y = 12.0, .size.width = 15.0, .size.height = 80.0 };
当访问CGRect结构体的x、y、width、height成员时,应使用CGGeometry函数,不直接访问结构体成员。苹果对CGGeometry函数的介绍:
functions described in this reference that take CGRect data structures as
inputs implicitly standardize those rectangles before calculating their
results. For this reason, your applications should avoid directly reading and
writing the data stored in the CGRect data structure. Instead, use the
functions described here to manipulate rectangles and to retrieve their
characteristics.
良好的风格:
CGRect frame = self.view.
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
不良的风格:
CGRect frame = self.view.
CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
CGFloat width = frame.size.
CGFloat height = frame.size.
&优先使用常类型变量,而不是内嵌的字符串字面值或数字,因为常类型变量能很容易的复用常用的变量值(如&#960;),同时可以快速地修改值而无需查找替换。常类型变量应该声明为static类型,不要使用#define,除非常类型变量被作为宏使用。
良好的风格:
static NSString * const RNCAboutViewControllerCompanyName = @"The New York Times Company";
static const CGFloat RNCImageThumbnailHeight = 50.0;
不良的风格:
#define CompanyName @"The New York Times Company"
#define thumbnailHeight 2
当使用enum关键字时,推荐使用苹果最新引入的固定基础类型语法,因为这将获得强类型检查与代码完成功能。SDK现在包含了一个固定基础类型的宏&#8212;&#8212;NS_ENUM()。
NS_ENUM是在iOS6中开始引入的,为了支持之前的iOS版本,使用简单的内联方法:
#ifndef NS_ENUM
#define NS_ENUM(_type, _name) enum _name : _type _ enum _name : _type
良好的风格:
typedef NS_ENUM(NSInteger, RNCAdRequestState) {
&&& RNCAdRequestStateInactive,
&&& RNCAdRequestStateLoading
私有属性应该被声明在实现文件的类扩展中(即匿名的category)。不要将私有属性声明在命名的category(如RNCPrivate或private),除非是扩展其他类。
良好的风格:
@interface NYTAdvertisement ()
@property (nonatomic, strong) GADBannerView *googleAdV
@property (nonatomic, strong) ADBannerView *iAdV
@property (nonatomic, strong) UIWebView *adXWebV
图片的命名应该保持一致,以图片的用途描述作为图片文件名。文件名的命名使用驼峰式大小写风格,文件名后可跟随一个自定义的类名或者是自定义的属性名(如果有属性名)、也可以再跟上颜色描述以及/或者位置、图片的最终状态。
良好的风格:
RefreshBarButtonItem / RefreshBarButtonItem@2x 和 RefreshBarButtonItemSelected / RefreshBarButtonItemSelected@2x
ArticleNavigationBarWhite / ArticleNavigationBarWhite@2x 和 ArticleNavigationBarBlackSelected / ArticleNavigationBarBlackSelected@2x.
被用作相似用途的图片应该使用一个图片文件夹进行分开管理。
因为nil被解析为了NO,所以和nil作比较没有任何的必要。不要将变量和YES直接比较,因为YES被定义为1而BOOL类型是8位的unsigned int,即BOOL的值不仅仅是1或0。
良好的风格:
if (!someObject) {
不良的风格:
if (someObject == nil) {
对于一个BOOL值:两种最佳实践:
if (isAwesome)
if (![someObject boolValue])
不良的风格:
if ([someObject boolValue] == NO)
if (isAwesome == YES) // Never do this.
如果一个BOOL类型的属性名是一个形容词,忽略属性名的&#8220;is&#8221;前缀是允许的,但需要为访问器指定约定的方法名,比如:
@property (assign, getter=isEditable) BOOL
应该使用线程安全的模式创建共享的单例实例。
+ (instancetype)sharedInstance {
&& static id sharedInstance =
&& static dispatch_once_t onceT
&& dispatch_once(&onceToken, ^{
&&&&& sharedInstance = [[self alloc] init];
&& return sharedI
& &单例的另一种做法,利用+ 方法。(JSONModel源码43行:):
static JSONAPI*
sharedInstance =
+ (void)initialize {
&&& static dispatch_once_
&&& dispatch_once(&once, ^{
&&&&&&& sharedInstance = [[JSONAPI alloc]
大部分的开发者都使用Xcode默认的字体颜色主题,其实好的主题不仅能提高源代码的辨识度,同时也增添了编码的乐趣。以下是二款Xcode字体颜色主题链接:
/tursunovic/xcode-themes
熟练使用代码片段库可以提高编码的速度。Xcode4中,打开一个项目并让右侧编辑区可视,然后点击右侧底部面板的第四个{}图标,打开代码片段库,你可以将常用的代码拖入其中。以下是一个最新的开源代码片段库链接:
/mattt/Xcode-Snippets 在code snippet library新建如下代码,设定一个类似vci(view controller initialize含义)自动提示快捷键。当开始编写ViewController或View时,键入vci,将相应的代码填入对应的位置。
#pragma mark - init
填入init,initWithFrame等方法
#pragma mark- View
Life Cycle
填入viewdidload,viewdidappear等方法
#pragma mark-
Override Parent Methods
填入updateViewConstraints,updateConstraint, prepareForSegue等方法
#pragma mark-
SubViews Configuration
填入configureSubViews,configureTableView等方法,这里的方法在init方法或view
life cycle被调用
#pragma mark- Actions
填入-(IBAction)action:(id)sender和[self
addtarget:self action:@selector(action:)]动作指向的方法
#pragma mark- Public
填入在.h外暴露的方法
#pragma mark- Private
填入.m文件内部调用的方法
#pragma mark-
Delegate,DataSource, Callback Method
填入tableview,scrollview等代理方法
#pragma mark- Getter
填入对@property初始化的方法
#pragma mark- Helper
填入一些帮助方法,如果使用扩展实现帮助方法不合适,则将帮助方法填在这里
#pragma mark
Temporary Area
填入一些你需要删除,或者不确定后面需不需要用,或者写一写备注之类的,类似代码回收站含义
[1] 《NYTimes Objective-C Style Guide》
[2] 《Coding Guidelines for Cocoa》
[3] 《iOS-view-frame-builder》
[5] 《Cocoa Style for Objective-C: Part II》
[6] 《objective-c-conventionsI》
Objective-C style guide.》
[8] 《Blocks or Delegation》
&如果喜欢此文,记得点击文章下方的推荐,以让更多的人有所收获。
文章中如有错误或不当之处望不吝指出,谢谢!
我的邮箱和微博: , 本文的。
阅读(...) 评论()

我要回帖

更多关于 快速熟悉代码 的文章

 

随机推荐