译者注:这篇文章是使用UIAppearance定义UI界面的入门教程,下篇将介绍一些具体的控件的自定义方法。
CUSTOMIZING THE NAVIGATION BAR 自定义导航栏
打开 Theme.swift 然后添加下面的两个方法到Theme中:
1
2
3
4
5
6
7
8
9
10
11
12
|
var
barStyle
:
UIBarStyle
{
switch
self
{
case
.
Default
,
.
Graphical
:
return
.
Default
case
.
Dark
:
return
.
Black
}
}
var
navigationBackgroundImage
:
UIImage
?
{
return
self
==
.
Graphical
?
UIImage
(
named
:
"navBackground"
)
:
nil
}
|
|
这些方法简单的为导航栏获取了获取了主题对应的bar style和背景图
然后,将下面语句添加到applyTheme()
:
|
UINavigationBar
.
appearance
(
)
.
barStyle
=
theme
.
barStyle
UINavigationBar
.
appearance
(
)
.
setBackgroundImage
(
theme
.
navigationBackgroundImage
,
forBarMetrics
:
.
Default
)
|
|
好了——为什么这些现在生效了,但是之前设置UINavigationBar的barStyle却没有呢?
UIKit有一个通知机制叫UIAppearance,大部分的控件都使用了它。当你调用通过UIKit的类(非实例)调用appearance(),它就会返回一个UIAppearance代理。当你修改了这个代理的属性,所有的这个类的实例都会被赋予相同的值。这个机制就使得你无须在修改主题时手动修改所有控件的样式。
Build and run,选择Dark主题,导航栏现在的颜色就深多了。
现在看起来好多了,但是还是有一些工作要做。
下一步,你将自定义后退按钮。iOS的默认后退按钮是一个箭头,但是你可以码出一些更加令人兴奋的东西。
自定义后退按钮
这个修改会对所有主题生效,所有你只需要将下面的几行加入到Themes.swift 的applyTheme()方法中:
|
UINavigationBar
.
appearance
(
)
.
backIndicatorImage
=
UIImage
(
named
:
"backArrow"
)
UINavigationBar
.
appearance
(
)
.
backIndicatorTransitionMaskImage
=
UIImage
(
named
:
"backArrowMask"
)
|
|
在这里你设置了后退按钮的图片和遮罩图。
Build and run.选择一个宠物你会看见新的后退按钮:
打开Images.xcassets 然后在Navigation组里面找到backArrow的图片,这个图片是纯黑色的,但是在App里面他会采取你的window的色板颜色然后并使之生效(it just works)。
但是为什么iOS只是修改了工具栏按钮的图片颜色,为什么其他地方它不这么做呢?
原来,iOS中得图像有3种渲染方式
- Original:总是使用图片的原来的颜色。
- Template:忽略颜色,只是把图片看做一个模板。在这个模式下,iOS只用到图片的形状,然后自己渲染图片的颜色。所以当一个控件有tint color时,iOS取了你提供的图片的形状,然后用tint color绘制他了。
- Automatic:看你在什么环境下使用图片,系统会自己决定使用“original”或者“template”方式。对后退按钮,导航栏上的按钮和标签栏上的按钮,iOS会默认的忽略他们的图片颜色除非你修改了他们的渲染模式。
回到App,点击一个宠物并点击Adopt进行领养。仔细看这个后退按钮的动画。有看出问题吗?
当Back文件往左移动时,文字和按钮的重叠看起来非常怪:
为了改正这个,你需要修改遮罩图:
在Themes.swift 中的applyTheme()方法中,修改设置backIndicatorTransitionMaskImage的代码为以下这一行:
|
UINavigationBar
.
appearance
(
)
.
backIndicatorTransitionMaskImage
=
UIImage
(
named
:
"backArrow"
)
|
|
Build and run. 重新点击宠物并点击Adopt进行领养。这次效果看起来好多了。
这些文字没有后退标切断,看起来就像藏在了后退按钮下一样。这中间到底发生了什么呢?
当iOS使用非透明后退图片绘制图标,如果添加了过渡遮罩层,它将给非透明的后退图标添加一个蒙版层。这个图标也只在那个区域可见。
在最开始的实现方法中,你提供了一个覆盖整个后退按钮的图片,那样的话文字将会一致可见。现在你将图片设置成了蒙版,然后文件将在蒙版的右侧就不见了,而不是在重叠在图标下方显示。
看看这个箭头图和它在图片assets目录下得原始蒙版图;你就会发现他们配合得非常棒。
黑色的图形是后退箭头,红色的是蒙版。你会希望文字在红色区域下面能够显示而在该图标的其他地方隐藏。
修改applyTheme的最后一行,并使用这个修改过的蒙版:
|
UINavigationBar
.
appearance
(
)
.
backIndicatorTransitionMaskImage
=
UIImage
(
named
:
"backArrowMaskFixed"
)
|
|
Build and run. 最后一次点击宠物并点击Adopt进行领养,你会发现文字在蒙版层才显示,达到了想要的效果。
现在你的导航栏已经是像素级别的效果了,现在是时候给标签栏一点点爱了(- -!)
CUSTOMIZING THE TAB BAR自定义标签栏
还是在Theme.swift,添加下面的属性到Theme中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
var
tabBarBackgroundImage
:
UIImage
?
{
return
self
==
.
Graphical
?
UIImage
(
named
:
"tabBarBackground"
)
:
nil
}
var
backgroundColor
:
UIColor
{
switch
self
{
case
.
Default
,
.
Graphical
:
return
UIColor
(
white
:
0.9
,
alpha
:
1.0
)
case
.
Dark
:
return
UIColor
(
white
:
0.8
,
alpha
:
1.0
)
}
}
var
secondaryColor
:
UIColor
{
switch
self
{
case
.
Default
:
return
UIColor
(
red
:
242
.
0
/
255
.
0
,
green
:
101
.
0
/
255
.
0
,
blue
:
34
.
0
/
255
.
0
,
alpha
:
1.0
)
case
.
Dark
:
return
UIColor
(
red
:
34
.
0
/
255
.
0
,
green
:
128
.
0
/
255
.
0
,
blue
:
66
.
0
/
255
.
0
,
alpha
:
1.0
)
case
.
Graphical
:
return
UIColor
(
red
:
140
.
0
/
255
.
0
,
green
:
50
.
0
/
255
.
0
,
blue
:
48
.
0
/
255
.
0
,
alpha
:
1.0
)
}
}
|
|
这些属性提供了与主题相配的背景图,背景色和次要的颜色。
要应用这些样式,添加项目的这几行代码到applyTheme()
.
|
UITabBar
.
appearance
(
)
.
barStyle
=
theme
.
barStyle
UITabBar
.
appearance
(
)
.
backgroundImage
=
theme
.
tabBarBackgroundImage
let
tabIndicator
=
UIImage
(
named
:
"tabBarSelectionIndicator"
)
?
.
imageWithRenderingMode
(
.
AlwaysTemplate
)
let
tabResizableIndicator
=
tabIndicator
?
.
resizableImageWithCapInsets
(
UIEdgeInsets
(
top
:
0
,
left
:
2.0
,
bottom
:
0
,
right
:
2.0
)
)
UITabBar
.
appearance
(
)
.
selectionIndicatorImage
=
tabResizableIndicator
|
|
设置barStyle和backgroundImage大家应该很熟悉了;这和之前在UINavigationBar所做的是一样的。
在最后三行代码中,你使用了asset目录下得一个图片作为标签图片然后设置他的渲染模式为.AlwaysTemplate。这是一个iOS没有自动使用template rendering mode的例子。
最后,你创建了一个可以拉伸的图片,并且设置它为标签栏的selectionIndicatorImage
Build and run. 你会看见最新带主题的标签栏:
这个dark主题看起来更加出色了。
看见选中标签下得那条线了吗?那是你的指示图片。虽然它的高度只有6,宽度只有49,iOS会把它拉升到标签栏的全宽。
下一个章节会聊到这些可拉升的图片和它们的工作原理。
自定义分段控件
还有一个组件没有被修改就是选中主题的分段控件还没有被修改。是时候将它也带入精彩的主题世界了。
添加下面的代码到Theme.swift的applyTheme()方法的底部:
|
let
controlBackground
=
UIImage
(
named
:
"controlBackground"
)
?
.
imageWithRenderingMode
(
.
AlwaysTemplate
)
.
resizableImageWithCapInsets
(
UIEdgeInsets
(
top
:
3
,
left
:
3
,
bottom
:
3
,
right
:
3
)
)
let
controlSelectedBackground
=
UIImage
(
named
:
"controlSelectedBackground"
)
?
.
imageWithRenderingMode
(
.
AlwaysTemplate
)
.
resizableImageWithCapInsets
(
UIEdgeInsets
(
top
:
3
,
left
:
3
,
bottom
:
3
,
right
:
3
)
)
UISegmentedControl
.
appearance
(
)
.
setBackgroundImage
(
controlBackground
,
forState
:
.
Normal
,
barMetrics
:
.
Default
)
UISegmentedControl
.
appearance
(
)
.
setBackgroundImage
(
controlSelectedBackground
,
forState
:
.
Selected
,
barMetrics
:
.
Default
)
|
|
为了理解上面的代码,首先看一下asset目录下的controlBackground图片。这个图片也许很小,但是iOS知道如何使用它来绘制UISegmentedControl的边框,就像它预先切好展好的那样。
切好是什么意思呢?看一看下面的放大的图吧:
这有4个3*3的正方形,每个角一个。这些正方形将会在拉升图片时被保留原样。然而灰色的部分将会被拉升。
在图片中,黑色部分将呈现为控件的配色。你将通过UIEdgeInsets()
告诉iOS如何拉升这个图片然后会给top、left、bottom和right4个参数传值为3,因为你的4个角是3*3。
Build and run.点击设置图片,你将会发现UISegmentedControl有了新的样式:
圆角被替换成了3*3的方形角。
现在已经给分段控件配了色,剩下的工作就是绘制其他控件的样式了。
关闭设置界面,点击右上角的放大镜;你将看见另外的分段控件,还有UIStepper
, UISlider
, and UISwitch需要加上主题。
抓起刷子,穿上废旧的衣服开始画画吧!
自定义累加器、滑块和开关
将下面行加入到Theme.swift的applyTheme()
|
UIStepper
.
appearance
(
)
.
setBackgroundImage
(
controlBackground
,
forState
:
.
Normal
)
UIStepper
.
appearance
(
)
.
setBackgroundImage
(
controlBackground
,
forState
:
.
Disabled
)
UIStepper
.
appearance
(
)
.
setBackgroundImage
(
controlBackground
,
forState
:
.
Highlighted
)
UIStepper
.
appearance
(
)
.
setDecrementImage
(
UIImage
(
named
:
"fewerPaws"
)
,
forState
:
.
Normal
)
UIStepper
.
appearance
(
)
.
setIncrementImage
(
UIImage
(
named
:
"morePaws"
)
,
forState
:
.
Normal
)
|
|
你使用了和UISegmentedControl相同可以拉升的图片;唯一的不同是UIStepper会在值达到上限或下限时变成不可用,所以也需要为这种情况做准备。简单起见这里用之前的图。
这里不仅修改了累加器的颜色,还替换了单调的+和-的符号按钮
Build and run.打开查找界面去看看了累加器变成什么样了。
UISlider
和 UISwitch
同样也缺爱
将下面代码加入到applyTheme()
:
|
UISlider
.
appearance
(
)
.
setThumbImage
(
UIImage
(
named
:
"sliderThumb"
)
,
forState
:
.
Normal
)
UISlider
.
appearance
(
)
.
setMaximumTrackImage
(
UIImage
(
named
:
"maximumTrack"
)
?
.
resizableImageWithCapInsets
(
UIEdgeInsets
(
top
:
0
,
left
:
0.0
,
bottom
:
0
,
right
:
6.0
)
)
,
forState
:
.
Normal
)
UISlider
.
appearance
(
)
.
setMinimumTrackImage
(
UIImage
(
named
:
"minimumTrack"
)
?
.
imageWithRenderingMode
(
.
AlwaysTemplate
)
.
resizableImageWithCapInsets
(
UIEdgeInsets
(
top
:
0
,
left
:
6.0
,
bottom
:
0
,
right
:
0
)
)
,
forState
:
.
Normal
)
UISwitch
.
appearance
(
)
.
onTintColor
=
theme
.
mainColor
.
colorWithAlphaComponent
(
0.3
)
UISwitch
.
appearance
(
)
.
thumbTintColor
=
theme
.
mainColor
|
|
UISlider 有3个主要的自定义的敌方:滑动块,最大值印记和最小值印记。
滑动块应用了来自你的assets 目录的图片,最大值印记用了一个可伸缩的图片,初始模式无论在哪个主题下下它会是黑色、最小值印记也是一个可伸缩图片,但是他使用了template渲染方法,所以它继承了模板的颜色。
你设定thumbTintColor为主色调,并且设置了onTintColor为一个稍微淡一点的颜色。这样也就修改了UISwitch的样式。
Build and run.打开查找界面去滑动块和开关变成这样了。
与你看到的UISegmentedControl一样,外观代理自定义了所有的代理类的实体。但是有个时候你并不想要所有控件一个样–这样的话,你可以自定义某一个控件实体。
自定义控件实体
打开SearchTableViewController.swift然后添加下面的行到viewDidLoad()
:
|
speciesSelector
.
setImage
(
UIImage
(
named
:
"dog"
)
,
forSegmentAtIndex
:
0
)
speciesSelector
.
setImage
(
UIImage
(
named
:
"cat"
)
,
forSegmentAtIndex
:
1
)
|
|
在这里你只是设置了种类选择器的segment的图片。
Build and run. 打开搜索界面,种族选择器会变成这样
你无须做任何事情,iOS便会倒反选中项的图片的颜色,这是因为这里的图片会自动以Template模式进行渲染。
如何选择性的修改控件的字体?这个也很简单
打开 PetTableViewController.swift 并添加下面两行到 viewWillAppear()的底部
:
|
view
.
backgroundColor
=
ThemeManager
.
currentTheme
(
)
.
backgroundColor
tableView
.
separatorColor
=
ThemeManager
.
currentTheme
(
)
.
secondaryColor
|
|
然后, 将下面这行添加到 tableView(_:cellForRowAtIndexPath:)的末尾,在return之前:
|
cell
.
textLabel
!
.
font
=
UIFont
(
name
:
"Zapfino"
,
size
:
14
.
0
)
|
|
这样就会修改显示动物名字的label的字体了。
Build and run. 进行一下比较。
下面的图片显示除了查找界面的不同效果;新的版本就没有用到iOS的vanilla主题,并且显得更加有趣:
下面还有些什么?
点击这里可以下载这篇教程的完成版的项目。
在Objective-C里,你可以指定特定的自定义设置应用到只有当他们包含在特定类的其他控件里。例如,您可以将自定义的UITextField放到UINavigationBar里。
但悲剧的是,swift不能这么干。不过有个好消息是,iOS9将添加此功能,请期待稍后更新本教程。
我希望你喜欢这篇UIAppearance的教程,并且学习到调整你的UI的方法。其实它真的很简单。
via Ray Wenderlich.
由创意应用翻译,转载请注明出处。