编写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是一个基于constraint(约束)的布局系统,它根据UI元素之间约束关系来调整UI元素的位置和大小。
Auto Layout中约束的类对应是NSLayoutConstraint, 而创建NSLayoutConstraint对象主要有两种方式,第一种是
1
2
3
4
5
6
7
|
+
(
id
)
constraintWithItem
:
(
id
)
view1
attribute
:
(
NSLayoutAttribute
)
attribute1
relatedBy
:
(
NSLayoutRelation
)
relation
toItem
:
(
id
)
view2
attribute
:
(
NSLayoutAttribute
)
attribute2
multiplier
:
(
CGFloat
)
multiplier
constant
:
(
CGFloat
)
constant
;
|
上面方法主要意思是,某个view1的attribute1等于(小于或等于/大于或等于)某个view2的attribute2的multiplier倍加上constant。而attribute主要由表示位置(上/下/左/右)和大小(宽/高)的以下几个值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
typedef
enum
: NSInteger
{
NSLayoutAttributeLeft
=
1
,
NSLayoutAttributeRight
,
NSLayoutAttributeTop
,
NSLayoutAttributeBottom
,
NSLayoutAttributeLeading
,
NSLayoutAttributeTrailing
,
NSLayoutAttributeWidth
,
NSLayoutAttributeHeight
,
NSLayoutAttributeCenterX
,
NSLayoutAttributeCenterY
,
NSLayoutAttributeBaseline
,
NSLayoutAttributeNotAnAttribute
=
0
}
NSLayoutAttribute
;
|
简化一下,使用公式可以表达为:
1
|
view1
.
attribute1
=
view2
.
attribute2
*
multiplier
+
constant
|
第二种方式是:
1
2
3
4
|
+
(
NSArray
*
)
constraintsWithVisualFormat
:
(
NSString
*
)
format
options
:
(
NSLayoutFormatOptions
)
opts
metrics
:
(
NSDictionary
*
)
metrics
views
:
(
NSDictionary
*
)
views
;
|
这种方式主要是采用Visual Format Language(可视化格式语言)来描述约束布局,虽然语法比较简洁,但是可读性比较差和容易出错。
虽然Auto Layout在布局view方面是非常强大和灵活,但是创建constraint的语法过于繁杂,引用Masonry一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
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
]
,
]
]
;
|
如此简单的一个例子都要编写这么多行代码,想象一下如果创建多个view的constraint时会多么痛苦啊。另一个方式是采用Visual Format Language (VFL),虽然语法比较简洁,但是可读性比较差和容易出错。
Masonry是采用链式DSL(Domain-specific language)来封装NSLayoutConstraint,通过这种方式编写Auto Layout布局代码更加易读和简洁。
使用Masonry的MASConstraintMaker
来表达相同constraint
1
2
3
4
5
6
7
8
|
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
)
;
}
]
;
|
甚至可以更短
1
2
3
|
[
view1
mas_makeConstraints
:
^
(
MASConstraintMaker
*make
)
{
make
.
edges
.
equalTo
(
superview
)
.
with
.
insets
(
padding
)
;
}
]
;
|
使用Masonry创建constraint来定义布局的方式有三种:mas_makeConstraints
,mas_updateConstraints
,mas_remakeConstraints
。
使用mas_makeConstraints
创建constraint后,你可以使用局部变量或属性来保存以便下次引用它;如果创建多个constraints,你可以采用数组来保存它们。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 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
]
;
|
有时你需要更新constraint(例如,动画和调试)而不是创建固定constraint,可以使用mas_updateConstraints
方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 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
]
;
}
|
mas_remakeConstraints
与mas_updateConstraints
比较相似,都是更新constraint。不过,mas_remakeConstraints
是删除之前constraint,然后再添加新的constraint(适用于移动动画);而mas_updateConstraints
只是更新constraint的值。
1
2
3
4
5
6
7
8
9
10
11
|
-
(
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
)
;
}
}
]
;
}
|
想了解以上三个代码片段的更多细节,可以下载Masonry iOS Examples工程查阅。
Classy是一个能与UIKit无缝结合stylesheet(样式)系统。它借鉴CSS的思想,但引入新的语法和命名规则。
灵活内嵌的语法
{
}
:
;
这些语法符号是可选的,你可以选择适合自己的风格来表达stylesheet。
你可以使用{
}
:
;
来限定stylesheet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
$
main
-
color
=
#e1e1e1;
MYCustomView
{
background
-
color
:
$
main
-
color
;
title
-
insets
:
5
,
10
,
5
,
10
;
&
amp
;
gt
;
UIProgressView
.
tinted
{
progress
-
tint
-
color
: black
;
track
-
tint
-
color
: yellow
;
}
}
^
UIButton
.
warning
,
UIView
.
warning
^
UIButton
{
title
-
color
[
state
:highlighted
]
:
#e3e3e3;
}
|
或者你使用空格来限定stylesheet
1
2
3
4
5
6
7
8
9
10
11
|
$
main
-
color
=
#e1e1e1
MYCustomView
background
-
color
$
main
-
color
title
-
insets
5
,
10
,
5
,
10
&
amp
;
gt
;
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]
1
|
[
CASStyler
defaultStyler
]
.
filePath
=
[
[
NSBundle
mainBundle
]
pathForResource
:
@
&
quot
;
myStyles
.
cas
&
quot
;
ofType
:nil
]
;
|
如果你还想当发生错误时,获取错误信息以便于调试,可以使用-(void)setFilePath:error:
1
2
3
|
NSError
*error
=
nil
;
NSString
filePath
=
[
[
NSBundle
mainBundle
]
pathForResource
:
@
&
quot
;
myStyles
.
cas
&
quot
;
ofType
:nil
]
;
[
[
CASStyler
defaultStyler
]
setFilePath
:filePath
error
:
&
amp
;
amp
;
error
]
;
|
如果你是使用Storyboard/Xib组织UI界面,那就需要在main.m的int main(int argc, char * argv[])
方法设置 filePath,这样可以确保在创建UIWindow之前加载stylesheet。否则(采用手写UI代码),你在 AppDelegate.m的- (BOOL)application:didFinishLaunchingWithOptions:
方法设置filePath
Live Reload是实时显示编写UI代码效果的关键特性,它能够实时检查stylesheet文件变化,无需重新编译、构建和运行模拟器,从而极大提高开发速度。
为了启用Live Reload,你需要指定stylesheet路径,并且只运行在模拟器上。
1
2
3
4
|
#if TARGET_IPHONE_SIMULATOR
NSString
*absoluteFilePath
=
CASAbsoluteFilePath
(
@
&
quot
;
.
.
/
Styles
/
stylesheet
.
cas
&
quot
;
)
;
[
CASStyler
defaultStyler
]
.
watchFilePath
=
absoluteFilePath
;
#endif
|
Style Selectors是指定哪个view使用哪种样式的方式。主要有三种方法来指定目标view:
你可以混合使用三种方法,例子如下:
1
2
3
4
5
6
7
|
/* match views
* where class is UIButton or UIButton subclass
* and styleClass is "large"
* and superview class is UITabBar
*/
UITabBar
&
amp
;
gt
;
^
UIButton
.
large
{
}
|
想了解具体如何使用,请查阅官网Selectors章节
为了避免与Objective-C的message selectors混淆,术语style selectors表示Classy stylesheets的selectors
Classy支持所有UIAppearance的属性和方法,也支持与UIAppearance无关的很多属性。Classy使用与UIKit相同属性命名,所以你不必考虑如何将style property映射到Objective-C的property。
UIPageControl
类的属性如下:
1
2
|
@property
(
nonatomic
,
retain
)
UIColor
*pageIndicatorTintColor
;
@property
(
nonatomic
,
retain
)
UIColor
*currentPageIndicatorTintColor
;
|
style property的名字采用与objective-c一样的名字
1
2
3
4
|
UIPageControl
{
pageIndicatorTintColor
black
currentPageIndicatorTintColor
purple
}
|
style property的命名规则采用kebab case
1
2
3
4
|
UIPageControl
{
page
-
indicator
-
tint
-
color
black
current
-
page
-
indicator
-
tint
-
color
purple
}
|
想了解具体如何使用,请查阅官网Properties章节
在编程中一个很重要的原则就是避免重复,这不仅可以大量减少重复代码,并且使得代码更加容易复用和维护。Classy提供三种方式避免代码重复:grouping,nesting,variables
如果有两个以上的style selectors共用相同的属性时
1
2
3
4
5
6
7
8
9
10
|
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
}
|
我们可以提取相同的属性到分组style selector中
1
2
3
4
5
6
7
8
|
UISlider
.
info
,
UISlider
.
error
{
minimum
-
track
-
tint
-
color
black
maximum
-
track
-
tint
-
color
purple
}
UISlider
.
error
{
thumb
-
tint
-
color
red
}
|
如果两个以上style selectors共用相同的view hierarchy时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
UICollectionView
{
background
-
color
#a2a2a2
}
UICollectionView
&
amp
;
gt
;
UICollectionViewCell
{
clips
-
to
-
bounds
NO
}
UICollectionView
&
amp
;
gt
;
UICollectionViewCell
UILabel
{
text
-
color
purple
}
UICollectionView
&
amp
;
gt
;
UICollectionViewCell
UILabel
.
title
{
font
20
}
|
我们通过nesting方式将view hierarchies表达成这样方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
UICollectionView
{
background
-
color
#a2a2a2
&
amp
;
gt
;
UICollectionViewCell
{
clips
-
to
-
bounds
NO
UILabel
{
text
-
color
purple
&
amp
;
amp
;
.
title
{
font
20
}
}
}
}
|
Classy让你通过定义variables来将多个相同的style property值存储以便共享。Variable命名规则如下:
$
符号开头_
,-
或任何字母数字
1
2
3
4
5
6
7
8
9
10
11
|
// 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
}
|
ClassyLiveLayout通过结合Classy stylesheets与Masonry一起使用,能够在运行的模拟器中微调Auto Layout约束实时显示效果的工具。
ClassyLiveLayout一个核心category:UIView+ClassyLayoutProperties
,在UIView
定义以下属性:
1
2
3
4
5
6
7
8
9
10
11
|
@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
;
|
cas_margin
和cas_size
分别表示UI元素的位置和大小,而其余的属性都是对两个属性进一步细分。我们可以从stylesheets中访问style properties来定义constraints布局,做到将数据与代码分离,有利于修改和复用代码。
1
2
3
4
5
6
7
8
9
10
|
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
-
(
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配置如下:
1
2
3
|
pod
&
#039;Masonry', '~> 0.6.1'
pod
&
#039;Classy', '~> 0.2.4'
pod
&
#039;ClassyLiveLayout', '~> 0.6.0'
|
当安装好Masonry,Classy和ClassyLiveLayout后,第一次运行项目会出现没有stylesheet.cas文件错误:
No stylesheet.cas file error.png
只要向工程添加空的stylesheet.cas文件即可。
Create stylesheet.cas file.png
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便可以解决,但运行一下应用程序,出现一下错误:
主要原因是任何自定义UIView
继承SHPAbstractView
都需要override两个方法:- (void)addSubviews
和- (void)defineLayout
,我们可以查看SHPAbstractView
的源码可知:
SHPAbstractView Source Code .png
所以只要在LiveView.m
文件覆盖两个方法即可
1
2
3
4
5
6
7
8
|
#pragma mark - Add subviews and define layout
-
(
void
)
addSubviews
{
}
-
(
void
)
defineLayout
{
}
|
LiveView
主要由包含redBoxView
和blueBoxView
两个属性,redBoxView
表示红色方块,blueBoxView
表示蓝色方块。
1
2
3
4
5
6
7
8
|
#import "SHPAbstractView.h"
@interface
LiveView
: SHPAbstractView
@property
(
strong
,
nonatomic
)
UIView
*redBoxView
;
@property
(
strong
,
nonatomic
)
UIView
*blueBoxView
;
@end
|
由于SHPAbstractView
类如何初始化View已经做了处理,暴露两个接口- (void)addSubviews
和-(void)defineLayout
分别处理构建view hierarchy和定义布局,子类只要覆盖SHPAbstractView
这两个方法就可以创建LiveView了。
但是我们将Auto Layout的constraints都放在stylesheets中实时加载(Live reload),即放在本工程的stylesheet.cas文件,将布局数据和布局代码分离。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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数据后,便可以在代码布局:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
@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
=
@
&
quot
;
redBox
&
quot
;
;
_redBoxView
.
backgroundColor
=
[
UIColor
redColor
]
;
}
return
_redBoxView
;
}
-
(
UIView
*
)
blueBoxView
{
if
(
!
_blueBoxView
)
{
_blueBoxView
=
[
UIView
new
]
;
_blueBoxView
.
cas_styleClass
=
@
&
quot
;
blueBox
&
quot
;
;
_blueBoxView
.
backgroundColor
=
[
UIColor
blueColor
]
;
}
return
_blueBoxView
;
}
|
为了启用Live Reload,你需要指定stylesheet路径,并且只运行在模拟器上。
此时效果:
Live Change.gif
由于有网友提出这样一个问题:如果所有view的样式都放在同一个stylesheet.cas
文件,会让stylesheet.cas
文件繁杂,并且当多个人协同开发时,不易于合并代码,所以有必要将样式文件分离到多个文件中。
1.创建variable.cas
文件,并将redBox
对应UIView的样式放在variable.cas
文件中。
2.在stylesheet.cas
样式文件使用@import
指令引用variable.cas
文件
Live Change 1.gif
Live Change 2.gif
示例代码存放地址:LiveAutoLayout
之前手写UI代码每次更改一般都要重新编译、构建和运行模拟器才能看到效果,但结合使用Masonry,Classy和ClassLiveLayout之后,告别这个费时过程,极大地提高开发速度;不仅如此,我们将Auto Layout的constraints都放在stylesheets中实时加载(Live reload),将布局数据和布局代码分离,使得代码更加复用和维护。Classy还提供三种避免重复方法:Grouping, Nestting和Variable,尽可能复用样式数据。
范例二
使用UIImageView、UILabel、UIButton实现一个综合小案例
功能分析
(1)点击箭头切换序号、图片、描述 (2)如果是首张图片,左边箭头不能点击 (3)如果是尾张图片,右边箭头不能点击步骤分析
(1)搭建UI界面 (2)监听按钮点击切换序号、图片、描述
1. 界面分析
1> 需要读取或修改的属性的控件
// 序号标签
// 图片
// 图片描述
// 左边按钮
// 右边按钮