UIAppearance教程:入门指南

2017-07-24

作者:Ron Kliffer,原文链接,原文日期:2017-04-25

译者:Chenghui Bai;校对:Chenghui Bai;定稿:Chenghui Bai

更新日志:由Ron Kliffer更新至Xcode 8 和 Swift 3.原教程作者是Essan Parto.

尽管借壳在 iOS 应用是老生常谈的事情,但这并不意味着您的iOS应用程序中控件仅限于库存(我的理解是默认)外观。

是的,你可以从头开发你自己的控件和应用程序样式,但是在 iOS 中,苹果推荐你使用标准的 UIKit 控件,并使用各种自定义技术.这是因为 UIKit 控件更加高效,并且你自定义的控件应该是面向未来的.

在这个 UIApprearance 课程中,你将使用一些最基本的 UI 和自定义技术去自定义 Pet Finder app,是他脱颖而出.

作为额外的福利,你将学到在晚上打开 app后,它如何自动切换你 app 的 dark 主题.

入门指南

在here下载本课程的启动项目.这个 app 有很多标准的 UIKit 控件,看起来非常精致(香草).

打开工程,看看项目结构,编译运行,你将看见Pet Finder 主界面的元素:

UIAppearance教程:入门指南_第1张图片
image.png

有一个 navigation bar 和一个 tab bar,主界面是一个宠物列表,点击某一宠物去看关于他的详情.还有一个搜索页面.这个页面允许你为你的 app 选择一个主题.这听起来是一个很好的开始的地方.

UIAppearance :支持主题

许多的 app 不允许用户去选择一个主题,并且为 app 提供主题切换方法不是总是明智的.然而,有些情况主题非常有用.在开发期间,你可能想测试不同的主题去看哪个在你的 app 中最好.你可能测试 a/b那种样式是最合适的.或者你可能想让你的眼睛更舒适的时候通过添加一个 dark 主题.

在这个 UIAppearance 教程中,你将会为你的 app 创建一些主题,可以试试哪一个是最好的.

选择File\New\File… 然后选择 iOS\Source\Swift File. 点击 Next ,以Theme作为文件名,最后点击 Create.

用下面的代码替换文件的内容:

import UIKit

enum Theme: Int {

//1

case `default`, dark, graphical

//2

private enum Keys {

static let selectedTheme = "SelectedTheme"

}

//3

static var current: Theme {

let storedTheme = UserDefaults.standard.integer(forKey: Keys.selectedTheme)

return Theme(rawValue: storedTheme) ?? .default

}

}

让我们看看这些代码做了什么:

1.定义3种主题类型,default(默认), dark,graphical.

2.定义一个厂常量,帮助存储选择的主题.

3.为选中的主题定义一个只读的计算型属性(获取方法).使用 Userdefaults 持久化当前主题,返回删一次选中的主题,如果没有上次选择的主题则返回默认主题.

现在你设置你的主题枚举,给他添加一些样式.在Theme最后(最后的关闭括号之前)添加下面的代码:

var mainColor: UIColor {

switch self {

case .default:

return UIColor(red: 87.0/255.0, green: 188.0/255.0, blue: 95.0/255.0, alpha: 1.0)

case .dark:

return UIColor(red: 255.0/255.0, green: 115.0/255.0, blue: 50.0/255.0, alpha: 1.0)

case .graphical:

return UIColor(red: 10.0/255.0, green: 10.0/255.0, blue: 10.0/255.0, alpha: 1.0)

}

}

这为每一个特定的主题定义了定义了一个主题色mainColor.

让我们看看怎么使用的.打开AppDelegate.swift ,在

 application(_:didFinishLaunchingWithOptions:)方法添加下面这行代码:

print(Theme.current.mainColor)

编译运行 app,在控制台将会打印:

UIExtendedSRGBColorSpace 0.341176 0.737255 0.372549 1

现在你有3个主题,并且能通过Theme管理.现在是时候在你的 app中使用它 了.

把主题应用到你的控件上

打开Theme.swift, 添加下面方法到Theme底部:

func apply() {

//1

UserDefaults.standard.set(rawValue, forKey: Keys.selectedTheme)

UserDefaults.standard.synchronize()

//2

UIApplication.shared.delegate?.window??.tintColor = mainColor

}

在这去快速说明上面代码:

1.使用 UserDefaults持久化选择的主题.

2.把 mainColor 应用到你的应用的 window 中的 tintColor 属性上,待会,你将学习更多关于 tintColor 内容.

现在你只需要调用下这个方法.

打开AppDelegate.swift ,使用下面代码替换print(),

Theme.current.apply()

编译运行你的 app, 你将会看见新 app, 看起来是绿色的:

UIAppearance教程:入门指南_第2张图片
image.png

浏览app,到处都是绿色色调,但是你不能改变所有的控件和视图.这是黑...绿…魔法?

应用 tintColor

从iOS 7开始, UIView 就暴露了 tintColor 属性.他通常用于定义整个 app 界面元素的基本颜色.

当你为一个 view 指定色调时,他将自动传播到所有继承自该视图的子视图. 由于UIWindow继承自 UIView,所以你可以为整个 app指定一个色调,通过设置 tintColor.这就是上面 apply()方法做的所有事情.

点击你的app 左上角 Gear 图标.一个 TableView 上有一个 Segmendt 控件.当你选择不同主题,点击Apply,不会发生任何事情.现在去修复.

打开SettingsTableViewController.swift 添加下面一行代码到 applyTheme(_:), 方法,位于dismissAnimated()方法上面:

if let selectedTheme = Theme(rawValue: themeSelector.selectedSegmentIndex) {

selectedTheme.apply()

}

现在调用方法去添加主题,在 root window中设置所选主题的mainColor.

下一步,添加下面这行代码到 viewDidLoad()方法中.当控制器第一次加载时,他将选择一个主题,并持久化到 UserDefaults.

themeSelector.selectedSegmentIndex = Theme.current.rawValue

编译运行 app, 点击 setting 按钮,选择 Dark,点击 Apply, 你 app 的色调将会改变,从绿色到橘黄色:

UIAppearance教程:入门指南_第3张图片
image.png

眼尖的读者可能注意到这些颜色是在Theme中定义的mainColor,但是,你选择 Dark 的话,看起来不是 dark 的.要有这种效果,你必须自定义更多的东西.

自定义NavigationBar

打开Theme.swift 添加下面2个属性到Theme:

var barStyle: UIBarStyle {

switch self {

case .default, .graphical:

return .default

case .dark:

return .black

}

}

var navigationBackgroundImage: UIImage? {

return self == .graphical ? UIImage(named: "navBackground") : nil

}

这2方法为每个主题,返回了namvigation bar一个适当的样式和背景图

下一步,添加下面代码到 apply()底部:

UINavigationBar.appearance().barStyle = barStyle

UINavigationBar.appearance().setBackgroundImage(navigationBackgroundImage, for: .default)

OK,为什么在这里工作?难道不需要存储UINavigationBar的实例对象?

UIKit有一个非正式协议叫做UIAppearance ,大多数控件都遵循这个协议。当你调用UIKit控件的appearance() 方法(不是实例),他会返回一个UIAppearance 代理。当你改变这个代理的属性,这个类的所有实例都会自动改变他们的属性值。这是非常便利的,你不需要手动为每一个被实例化的控件改变他们的属性值。

编译运行app,选择 Dark 主题,navigation bar 现在将会非常暗。

UIAppearance教程:入门指南_第4张图片
image.png

这样看起来好了一点,但是你忍忍还有一些工作要做。

下一步,你讲自定义返回指示器。iOS默认使用一个人字形图标作为返回指示器,但是你可以用代码改变他,使他更棒!

自定义navigationbar bar返回按钮指示器

这个需要让所有主题都得到改变,因此你需要去添加下面代码到Themes.swift 的 apply() 方法中:

UINavigationBar.appearance().backIndicatorImage = UIImage(named: "backArrow")

UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named: "backArrowMask")

现在你只设置了一张图片和一个过度图到返回指示器上。

编译运行app. 点击一个宠物,你将会看见一个新的指示器:

UIAppearance教程:入门指南_第5张图片
image.png

打开Images.xcassets 在Navigation组中找到名为 backArrow 的图片。这里的图片都是黑色的,但是你的app在window中设置了tint color起了作用。

UIAppearance教程:入门指南_第6张图片
image.jpeg

但是iOS怎么能改变工具栏按钮的图像颜色呢?为什么不到处都是呢?

事实证明,iOS中的图像有3中呈现模式:

Original:始终使用图像其原有颜色。

Template:忽略颜色,使用图像作为模板。这种模式,iOS只使用图像的形状,并在屏幕上渲染之前将图像本身着色。所有当一个控件有 tint color,iOS会使用图片提供的这个形状并且使用tint color为他上色。

Automatic:依靠你使用的图片的上下文,系统决定是否将图像绘制为“Original”或“Template”。对于诸如“back指示器”、“导航控件”,tab bar图像等项,IOS默认情况下忽略了图像颜色。你可以手动设置模式覆盖这个默认行为。

回到app,点击一个宠物,然后点击Adopt.仔细看导航栏上的返回指示器,你发现什么问题了吗?

image.gif

当你Back 文字往左边过度时,他与指示器重叠了,看起来非常不好:

去修复他,你需要去改版这个过度图片。

更新属性backIndicatorTransitionMaskImage 在Theme.swift 的 apply() 中设置的。用下面的代码:

UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named: "backArrow")

编译运行app,再一次点击宠物,点击Adopt. 现在这个过度看起来非常好了:

image.gif

这个文本不再被切断,看起来像是在指示器下面。所以,这发生了什么?

iOS 使用的是非透明像素的返回指示器图片去话指示器。然而,他与过度图片做了一些完全不一样的工作。他使用了不透明像素的过度图片来屏蔽指示器。所以当文本左移,这指示器就成龙唯一可见的了。

在这个原始实现中,你会提供一个图片,覆盖在返回指示器的整个表面。这就是为什么文本通过过渡仍然可见的原因。现在你使用指示图自己作为mask,看起来不错,但是如果你仔细看,你会看到文字消失在最右边的mask,而不是在指示器。

看指示器图片和这个被固定的版本的面具在你的image assets 中,你将会发现他们之间显示的很好:

UIAppearance教程:入门指南_第7张图片
image.png

这个黑色形状作为返回指示器,红色形状作为你的 mask,您希望文本只有在红色区域下可见在其他地方隐藏。

这么做,再次改变apply()方法的最后一行代码:

UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named: "backArrowMaskFixed")

编译运行app,最后一次点击一个宠物,然后点击Adopt.你将看见文本现在小时在图片下面,正式你期望的:

image.gif

现在你的navigation bar已经完美展示了,是时候去给你的tab bar送去一些爱了。

自定义Tab Bar

依然是 Theme.swift,添加下面属性到Theme:

var tabBarBackgroundImage: UIImage? {

return self == .graphical ? UIImage(named: "tabBarBackground") : nil

}

这个属性将会为每个主题的tab bar提供适当的背景图。引用下他们的样式,添加下面代码到apply()方法:

UITabBar.appearance().barStyle = barStyle

UITabBar.appearance().backgroundImage = tabBarBackgroundImage

let tabIndicator = UIImage(named: "tabBarSelectionIndicator")?.withRenderingMode(.alwaysTemplate)

let tabResizableIndicator = tabIndicator?.resizableImage(

withCapInsets: UIEdgeInsets(top: 0, left: 2.0, bottom: 0, right: 2.0))

UITabBar.appearance().selectionIndicatorImage = tabResizableIndicator

设置 barStyle 和 backgroundImage 应该很熟悉了现在。这个和之前设置navigationBar的方式一样。

在上面的最后3行代码,你到检索下asset目录检索一个指示器图片像,并将其呈现模式设置为.AlwaysTemplate。这是一个不使用自动设置渲染模式的例子。

最后,你创建一个可调整大小的图片,并设置为tab bar的selectionIndicatorImage.

编译运行app,你将会看见一个全新的tab bar主题.

UIAppearance教程:入门指南_第8张图片
image.png

开启Dark 主题去查看更多。

看见选中的tab bar下面的线条吗?这是一个指示器图片,尽管他只有6个点的高度和49个点的宽度,iOS在运行时拉伸这个tab宽度。

下一部分是可拉伸的图片,以及怎么工作的。

自定义分段(Segmented)控件

有一个控件没做任何改变,Segment控件,选中显示当前主题的。是时候给他带来一个精彩的主题了。

未完待续。。。

你可能感兴趣的:(UIAppearance教程:入门指南)