05 自动化准备:如何使用 Fatlane 管理自动化操作?

要成为一个优秀的 iOS 开发者,我们要做的事情远多于“开发”,例如我们要构建和打包 App,管理证书,为 App 进行签名,把 App 分发给测试组,上传 App 到 App Store,等等。这些操作不但繁琐费时,而且容易出错。那么,有没有更便利的方法呢?有,那就是使用 fastlane 来完成这些重复性的工作。接下来这一讲,我们主要聊的也就是这个主题。

fastlane 安装

fastlane 是用 Ruby 语言编写的一个命令行工具,可以自动化几乎所有 iOS 开发所需要的操作,例如自动打包和签名 App,自动上传到 App Store 等等。有了 fastlane,我们就可以开发一套统一的、可靠的和可共享的配置,团队所有成员都可以通过这套配置实现自动化操作,减少重复性劳动。

如何安装 fastlane 呢?我记得在第一讲就曾提到过,可以使用 Bundler 来安装,只需要在 Gemfile 文件里面添加以下一行即可:

gem "fastlane", "2.166.0"

注意,由于是通过 Bundler 来安装 fastlane,每次执行 fastlane 命令前,都需要加上bundle execbundle exec fastlane)。不过为了简洁,在这里后面凡涉及 fastlane 命令时,我会省略bundle exec部分。

Action 与 Lane

fastlane 为我们提供了一百多个 Action,它们是 iOS 项目开发中所有自动化操作的基础。所谓的Action,你可以理解成是 fastlane 自动化流程中的最小执行单元。一般常用的 Action 有:

  • scan,用于自动测试 App;

  • cert,用于自动生成和管理 iOS App 签名的证书;

  • sigh,用于自动生成、更新、下载和修复 Provisioning Profile;

  • match,为整个团队自动管理和同步证书和 Provisioning Profile;

  • gym,用于自动构建和打包 App;

  • snapshot,用于自动在不同设备上截屏;

  • pilot,用于自动把 App 部署到 TestFlight 并管理测试用户;

  • deliver,用于自动把 App 上传到 App Store;

  • pem,用于自动生成和更新推送消息的 Profile。

这些 Action 怎么执行呢?我们可以通过fastlane <action>(例如fastlane scan)来执行。下面是执行效果,它提示我选择其中一个 Scheme 来继续执行。

从运行情况可知,尽管这些 Action 为我们提供了不少便利,但还是需要手工输入来继续。所以,我不推荐你直接使用这些 Action,而是根据项目需要,在开发自己的自动化操作时通过传入合适的参数来调用 fastlane 所提供的 Action。

具体来说,我们可以把所需的 Action 组合在一起,开发出对应的自动化操作。在 fastlane 中,我们把这个自动化操作或者任务叫作 Lane实际上, iOS 开发中的所有自动化操作,主要通过 Lane 来封装和配置的。

Lane 和 Action 的关系如上图所示, 一条 Lane 可以通过参数调用一个或几个 Action 。以 Moments app 为例,我们要自动打包和签名 App,那么我就建了一条名叫archive_appstore的 Lane。因为这条 Lane 用到的“更新签名”和“打包”在 fastalne 里已经提供了相关的 Action——update_code_signing_settingsgym,我就可以到它官网去寻找,从而减轻了开发工作量。

一般,iOS 项目所需的自动化操作都配置为 Lane 并保存在 Fastfile 文件,由 Git 统一管理起来,共享给所有成员。然后,大家就可以使用统一的自动化配置了。

这里的 Fastfile 文件是怎么出来的呢?

它是由fastlane init命令自动生成。这条命令会建立一个 fastlane 文件夹,文件夹里除了 Fastfile ,还有 Appfile,以及执行过程中所生成的一些中间文件(如截图、日志与报告等)。因为我们之前已经在 .gitignore 文件里把这些中间文件忽略了,因此这些中间文件不再保存到 Git 里面。

fastlane 文件夹里的 Appfile,用于保存 App 的唯一标识符和 Apple ID 等信息。当 fastlane 在执行一个 Action 的时候,首先会使用传递进来的参数,当参数没有传递进来时,fastlane 会从 Appfile 文件中查找并使用对应的信息。

比如,我们在 Appfile 配置了app_identifier "com.ibanimatable.moments"以后,在调用matchAction 时可以不传入app_identifier参数,fastlane 会自动把"com.ibanimatable.moments"作为app_identifier的值进行调用。

但是为了方便管理所有的 Lane ,保证每次执行的效果都一样,我建议在每次调用 Action 的时候,都明确传递每一个所需的参数,而不是从 Appfile 文件读取。下面我就演示下如何明确传递每一个参数来执行matchAction。

  match(type: "appstore",force: true,storage_mode: "git",git_url: "https://github.com/JakeLin/moments-codesign",app_identifier: "com.ibanimatable.moments", # pass  app_identifier explicitlyteam_id: "6HLFCRTYQU",api_key: api_key)

常用 Lane 定义

通过上面的介绍你已经知道,我们会使用 Lane 来封装项目所需的各个自动化操作。那么,这些 Lane 是如何开发定义的呢?接下来,我就为你介绍几种非常实用的 Lane,一起来看看怎么做。

扫描和检查代码

每条 Lane 的定义都是放在一个lane <lane_name> do <lane_body> end的代码块里面。它以关键字lane开头,接着是这条 Lane 的名字。 下面是用于检查代码的 Lane 源码。

lane :lint_code doputs("Lint code using SwfitLint")swiftlint(mode: :lint,executable: "./Pods/SwiftLint/swiftlint",  # Important if you've installed it via CocoaPodsconfig_file: './Moments/.swiftlint.yml',raise_if_swiftlint_error: true)
end

在上面的例子中,我们定义了一个叫作lint_code的 Lane。因为 fastlane 使用 Ruby 开发,所以在 Fastfile 里面,Lane 的名字也遵循它的编码规范,使用小写字母和下划线组合的蛇式命名法。

Lane 的实现逻辑放在doend中间,我们可以调用 fastlane 提供的任意 Action。在这个例子中我们就调用了swiftlintAction,并把lint传递给mode参数,以此来执行代码扫描和检查操作。

特别需要注意的是,由于我们之前使用了 CocoaPods 来安装 SwiftLint,因此要为executable参数指定 SwiftLint 的安装路径./Pods/SwiftLint/swiftlint。同时要把 .swiftlint.yml 文件的所在路径也传递给config_file参数。这样就能保证 fastlane 使用了统一的 SwiftLint 版本和规则文件,方便团队所有人执行该 Lane 时得到统一的效果。

当一条 Lane 开发配置完毕以后,我们就可以在项目的根目录执行 fastlane <lane_name>。比如扫描和检查代码的 Lane ,我们可以在终端输入fastlane lint_code看到它的执行效果。

Driving the lane 'ios lint_code'
Lint code using SwfitLint
--- Step: swiftlint ---
$ ./Pods/SwiftLint/swiftlint lint --config ./Moments/.swiftlint.yml
Linting Swift files in current working directory
Linting 'Strings.swift' (1/87)
Linting 'MomentListItemViewModel.swift' (2/87)
Linting ......s

UIButtonExtensions.swift:14:46: warning: no_hardcoded_strings Violation: Please do not hardcode strings and add them to the appropriate Localizable.strings file; a build script compiles all strings into strongly typed resources available through Generated/Strings.swift, e.g. `L10n.accessCamera (no_hardcoded_strings)
Done linting! Found 6 violations, 0 serious in 87 files.
fastlane.tools finished successfully

在执行过程中,fastlane 先从 Fastfile 文件里名叫lint_code的 Lane 的定义,然后执行了该 Lane 里使用到的 swiftlint Action。swiftlint Action 会把项目下 87 个 Swift 源代码文件进行扫描和检查,并把所有不符合规范的代码提示给我们。

格式化代码

检查代码之后,接下来就是清理不符合规范的代码,比如删掉所有代码中不必要的空格或者空行,修正缩进的大小等等。我们可以定义一条叫作format_code的 Lane 来执行该功能。有了它以后,我们只需要执行fastlane format_code就能把整个项目所有的代码进行格式化。

lane :format_code doputs("Lint and format code using SwfitLint")swiftlint(mode: :autocorrect,executable: "./Pods/SwiftLint/swiftlint",  # Important if you've installed it via CocoaPodsconfig_file: './Moments/.swiftlint.yml',raise_if_swiftlint_error: true)
end

format_codelint_code两条 Lane 都使用了swiftlintAction,唯一不同的地方是为mode参数传递了autocorrect

排序 Xcode 项目文件列表

在多人开发的项目下,我们经常会修改项目文件,这往往很容易引起合并冲突,而合并 xcodeproj 文件又是一件非常麻烦的事情。怎么办呢?

一个有效办法就是在每次新建源代码和资源文件时,把 xcodeproj 里面的文件列表进行重新排序。这样能极大地减低合并冲突的发生。我们把这一个经常使用到的操作也配置到 Fastfile 里面,如下所示。

lane :sort_files doputs("Sort the files for the Xcode project")sh "../scripts/sort-Xcode-project-file.pl ../Moments/Moments.xcodeproj"
end

可以看到,fastlane 除了能调用其提供的 Action 以外,还可以通过sh来调用其他程序命令。在这里我们调用了由苹果公司提供的一个Perl 程序来为 xcodeproj 里面的文件列表进行排序。你也可以在拉勾教育的代码仓库找到这个Perl 程序。

调用其他 Lane 操作

除了调用一些程序命令(如sh)以外,一条 Lane 还可以调用 Fastfile 里面其他的 Lane。例如我们定义了一条叫作prepare_pr的 Lane ,它可以帮我们在提交 Pull Request 之前做一些必要的准备。下面这个代码表示的就是,这条 Lane 在内部调用了另外两条 Lane ——format_codesort_files,以此来同时完成格式化代码和排序 Xcode 项目文件列表的操作。

lane :prepare_pr doformat_codesort_files
end

定义私有 Lane 和返回值

类似于 Swift 语言能通过private来定义内部使用的方法,我们也能定义私有 Lane 给Fastfile 内的其他 Lane 所调用,提高代码的复用。其做法就是把原先的关键字lane替换成private_lane。例如我们定义一条叫作get_pi的私有 Lane,代码如下。

private_lane :get_pi dopi = 3.1415pi
end

该 Lane 的实现体有两行代码,第一行是给一个临时变量pi赋值。第二行表示把pi作为返回值传递给调用者。例如下面就演示了如何调用get_pi并取得返回值。

lane :foo dopi = get_piputs(pi)
end

这是执行fastlane foo的结果:

Driving the lane 'ios foo'
--- Step: Switch to ios get_pi lane ---
Cruising over to lane 'ios get_pi'
Cruising back to lane 'ios foo'
3.1415

fastlane 首先调用fooLane,然后进去get_piLane 并返回到foo,同时把返回结果打印出来。

总结

这一讲我介绍了如何从头开始搭建一个 fastlane 环境。在这里需要注意三点:

  1. 不要单独手工执行 fastlane 所提供的 Action,而是使用 Fastfile 文件来统一开发、配置和管理日常中经常使用的所有自动化操作;

  2. 在开发我们的 Lane 时,要优先使用和调用 fastlane 提供的 Action,因为这些 Action 都是经过社区完善的,且会随着 Xcode 版本的升级而更新;

  3. 当我们调用 fastlane 所提供的 Action 时,要明确传递各个参数,在执行过程中就无须任何手工交互就能从头到尾执行整个操作。

有了项目需要的所有 Lane 以后,能有效减轻团队成员的重复劳动,并为项目的自动化和工程化打下坚实的基础。在后面的章节中,我会详细介绍如何使用 fastlane 来管理证书,打包 App 和上传到 App Store。

思考题:

在你的项目中是怎样实施自动化的?如果你是第一次使用 fastlane,请按照上述的文章和 fastlane 的官方文档来开发一条执行测试的 Lane。

可以把你的答案写得留言区哦,下一讲我将介绍如何使用 Git 与 GitHub 统一代码管理流程。

源码地址:

Fastfile 文件地址:https://github.com/lagoueduCol/iOS-linyongjian/blob/main/fastlane/Fastfile#L19-L50


06 代码管理:如何使用 Git 与 GitHub 统一代码管理流程?

在软件开发当中,代码管理一直是其中重要的一环,每当软件出现问题,我们就需要查看源码,及时发现其中的漏洞加以修复。并且,由于分工不同,软件开发需要多个人共同完成,如何保证每个人编写的代码符合要求,能够相互配合,也是一个重要的问题。所以,一个非常实用的代码管理工具和一套统一的代码管理流程在开发当中必不可少。

而在这方面,StackOverflow 曾调查发现,有超过 87% 的开发者使用 Git,有超过 82% 的开发者使用 GitHub 来进行代码托管和开发协作。可以说,熟练使用 Git 和 GitHub 已经成为开发者的基本技能,而如何结合它们来规范代码管理也是 iOS 开发工程化实践当中的基础。

Git 分支管理

现代的软件开发活动通常需要多人参与。为了保证不同开发者可以同时贡献到同一个代码库,Git 提供了分支(Branch)来支持并行开发。不同团队有不同的 Git 分支管理方式,根据我们团队多年的经验,经过不断的完善,最终形成出一套简单并十分有效的 Git 分支管理规范,你可以参考下。这套规范是怎样的呢?

具体来说,我把所有的 Git 分支分成三类:主分支,功能分支和发布分支,让它们各自承担不同的功能。

其中主分支也称为master或者main分支, 是 Git 代码仓库的默认分支。在 Xcode 12 以后,新建项目时也会默认生成命名为main的主分支。

主分支在软件开发中非常重要,它是我们 App 的唯一的信息源(Single source of truth),不论是编译出不同版本的 App 还是排查问题,都需要用到主分支的代码。并且,团队同事的代码,最终也必须汇总到这个主分支中,且不能出错。所以,所谓的统一代码管理流程,就是制定其他分支的代码如何合并到主分支的流程。

功能分支是我们在开发过程中建立的临时分支,它可以用来保存一次开发活动的状态。根据不同的开发活动,我把功能分支分成几个小类。

  1. feature分支,当开发一个新功能的时候,我会为每一个功能建立一个叫作feature分支,当整个功能完成后就可以合并到主分支里面,并把该分支从 Git 代码仓库中删除掉。

  2. bugfix分支,当发现 Bug 的时候,会专门建立一个bugfix分支,修改 Bug 后把它合并到主分支里面去。

  3. spike分支,当我们探索或研究一些新技术(如 App Clips 功能)的时候,会建立一个叫作spike的分支。在得出结论以后,我才决定是否把该分支合并到主分支。如果探索失败,我就不会把相关的spike分支合并到主分支了。

在新建一个功能分支的时候,我们都遵循一定的命名规范,一般会把功能的描述作为分支的名称。例如当我们要开发一个点赞功能时,会把该分支可以命名为feature/add-like-button-to-moments-screen。又例如当我们需要修改用户头像的一个 UI Bug 时,会把分支命名为bugfix/fix-avatar-ui-bug

有那么多功能分支,在并行开发过程中我们该如何管理它们呢?

来看下面这幅。

举例来说,我有一个同事要开发点赞功能,他就从主分支最新的MC1commit 签出(checkout)并新建feature/add-like-button-to-moments-screen来进行点赞功能的开发。与此同时,另外一个同事发现了一个用户头像的 UI Bug,她也从MC1commit 签出并新建了bugfix/fix-avatar-ui-bug来修改 Bug。

当头像的 Bug 修改完成后,开发组长把BC2commit 合并到主分支里面。在此之后点赞功能也开发完毕,开发组长又把该功能分支上的FC4commit 合并到主分支里面。这两个分支彼此独立,而且互不影响。

除了主分支和功能分支以外,我们在发布 App 的时候还使用到发布分支。

发布分支一般命名为release。每次当我们发布 App 之前都会把主分支的最新代码合并到发布分支去。因此发布分支会一直保存 App 发布版的源码记录。

有了发布分支以后,一旦发生严重的线上事故,例如出现引起高崩溃率的 Bug 时,我们可以马上在发布分支上进行修复。一般的做法是从发布分支上签出一个功能分支,例如当修复点赞按钮引起的崩溃时,我们可以建立一个叫作bugfix/fix-like-button-crash的功能分支,在修复该崩溃以后马上合并到发布分支,并提交到 App Store ,更新线上的 App。

由上图可见,当我们把主分支最新的MC1commit 合并到release分支以后,提交了一个版本号为V2.0的 App 到 App Store。当我们通过查看崩溃报告,得知新上线的点缀功能引起很多崩溃时,可以采取如下措施:

  1. 马上从release分支的V2.0commit 签出bugfix/fix-like-button-crash分支并修复该崩溃;

  2. BC2commit 合并回release分支,并立刻提交版本号为V2.1的 App 到 App Store;

  3. 把包含了该修复的V2.1commit 合并到main分支,保证主分支统一管理所有代码的更新状态。

有了上面讲的三大分支,以及它们签出和合并的流程,我们就定义了一部分的代码管理规范——知道什么时候要使用哪个分支进行开发或者发布。但是你可能已经察觉到,当我们把功能分支合并到主分支的时候并没有进行任何的审查,万一有人不小心把 Bug 合并到主分支怎么办?

我们在这方面就吃过很多大的亏,有人把没有经过验证的代码直接 Push 到主分支,使得 App 的崩溃率提升了 10% 以上。为了保证主分支受控,我们就需要引入一个代码管理流程来管控主分支的合并过程。目前管控这一流程最有效的办法是使用Pull Request,众多代码托管和协作平台(如 GitLab,BitBucket 等)都支持 Pull Request 功能。下面我就以 GitHub Pull Request 为例子来看看它是如何工作的。

GitHub Pull Request 流程

Pull Request我把它简称为 PR。PR 是一种团队协助的机制,在 GitLab 也叫作 Merge Request。当一个开发者完成一个功能的开发时,可以通过 PR 来通知团队其他成员进行代码审查和讨论。在协商并得到共识后可以通过 PR 把功能代码合并到主分支中。

一套完善的 PR 流程能有效降低沟通成本,提高代码质量,以及提升项目的自动化和工程化程度。

下面我们一起看一套完整的,并经过我们多年实践证明过的 PR 流程。

这套流程分成六步。

第一步,当我们要开发一个新功能或者修改一个 Bug 时,从主分支的签出并建立一个功能分支。这里需要注意的是,为了减少合并时出现的冲突,我们需要从主分支最新的一个 commit 签出。

第二步,我们可以在功能分支上持续开发并多次提交 commit。因为我们是在独立的功能分支上进行开发,所有的变动都不会影响到主分支,所以可以放心修改所需的代码。

第三步,当我们完成一个功能的开发以后,就可以提交一个 PR 了。为了避免合并冲突,我建议在提交 PR 前先 rebase 主分支的 Git 历史。同时为了方便其他成员审查代码和参与讨论,我们在提交 PR 的时候需要清楚地描述所完成的功能,并把注意 事项,UI 前后变动的区别,测试步骤等等一同写到 PR 描述文档里面。

在我们 Moments App 项目中,为了方便开发者编写 PR 描述文档,我们建立了一个模板文件pull_request_template.md。当我们提交 PR 的时候,GitHub 会自动读取并准备好描述文档的模板,我们只需要填写相关内容即可。

你可以到拉钩的代码仓库查看该模板文件。

在提交 PR 的时候,我们还可以加上代码审查人(Reviewer)来通知他/她审查代码。同时也可以加上分类的标签(Label)来方便管理所有的 PR,例如使用enhancement表示功能开发,使用bug表示 Bug 修改。如果你的项目由多团队同时开发,我们还会为每个产品团队都建立一个标签,这样就能清楚地知道这个 PR 来自那个产品团队了。

第四步,一旦 PR 提交以后,其他成员就会收到通知消息,他们可以进行代码审查,并把反馈意见留言到 RP 里面。提交者可以根据留言来修改代码和提交新的 commit。当所有留言都修正和完善以后,可以再次通知审查人进行进一步的审查。

在项目之初,这个过程可能需要来回好几遍。但随着团队的不断磨合,这个过程会越来越快。因为代码审查人一般由项目中最资深开发者组成,他们的留言能帮助团队新成员迅速熟悉项目相关的技术和背景知识。同时能保证代码风格的一致性和提高整个项目的代码质量。

根据我们的经验,在代码审查上的投资所得到的好处,远高于所花的时间成本,我建议你或者你所在的团队在项目初期多投入 PR 审查工作。

第五步,大家通过协商得到共识以后,审查人就可以批准(approve)该 PR。这表示该 PR 可以随时合并到主分支了。

第六步,当 PR 得到批准以后,提交人可以把 PR 合并到主分支里面去。我建议在合并之前先 rebase 主分支上的最新 commit,这样能保证合并过程没有冲突,并使得 Git 的历史更加简洁。

上面就是我们的统一的代码管理流程。为了进一步保证少犯错误,我们可以通过修改 GitHub 上的配置来为每一步操作做自动检查,以保证所有成员都必须严格遵循一致的代码管理流程。

GitHub 配置

首先,我们可以把主分支保护起来,不允许任何人直接 Push 到主分支。

然后要求所有 PR 在合并之前都必须经过一个或以上的代码审查人批准。审查人的数量可以根据团队的情况进行调整。

当 Github 检查到有某些条件不完全符合时,就不允许我们合并该 PR。

比如,因为我们的 Moments 项目配置了所有的 PR 都必须有一个或以上的代码审查人批准后才能合并。上图可以见 GitHub PR 页面上的Merge pull request按钮是失效的,并提示 “Merging can be performed automatically with 1 approving review” (需要一个代码审查通过后才能合并)。

总结

在这一讲我介绍了 Git 的分支管理和 GitHub 的 Pull Request 流程。根据多年的项目经验,我给出了一套完整的统一代码管理流程,其重点是把 Git 分支分成三类,主分支、功能分支和发布分支,然后严格按照 GitHub Pull Request 流程来把代码合并到主分支里面。

有了这个规范,开发者就能严格遵循这个流程贡献代码,从而保证主分支在管控状态,同时也为项目的自动化和工程化打下基础。

思考题:

请问你是使用怎样的流程来进行代码管理的?在上述 PR 流程的第一步是从主分支签出一个功能分支,而不是使用 Fork,为什么这样做?

可以把你的答案写得留言区哦,下一讲我将介绍如何统一设计规范,提高沟通效率。

源码地址:

pull_request_template.md
https://github.com/lagoueduCol/iOS-linyongjian/blob/main/.github/pull_request_template.md


07 设计规范:如何统一设计规范,提高沟通效率?

作为 iOS 工程师,我们开发的绝大部分功能都是与用户界面和用户交互有关。所以,和设计师协调沟通也成了我们的日常工作之一。在这个过程中,就免不了和他们争论有关间距大小、字体粗细、字号大小、颜色深浅等问题。想必有时候你也很烦恼,怎么和这些设计师们沟通就那么麻烦呢!

其实,这些问题都可以用一套统一的设计规范来解决,从而有效降低沟通成本。有了规范,设计师可以根据明确的指引和预定义好的设计元素,来设计出新的界面;而开发者也能使用预先封装好的、可重用的设计组件实现快速开发。最后,开发的产品为用户提供一致的体验。

那一套统一的设计规范到底是怎样的呢?它通常包括间距、字体、颜色、图标和常用组件等,这一讲,我们就一起看看怎样定义这些设计规范吧。

间距

间距(Spacing)是父子组件之间,以及平级组件之间的留白,合理使用间距能有效分离和组织内容,也能保证页面风格一致,提升用户体验。

但是,在实际工作当中,你有没有遇到过打开设计师给的设计稿后发现,同一个界面里的间距定义杂乱无章,不同页面类似组件的间距也大不相同,这样导致的结果是,开发出的 App 在视觉上会给人风格混乱的感觉。

那怎样定义统一的间距呢?其实很简单,我们只保留几个可用的间距,并给它们赋予定义,下面是我们 Moments App 的间距定义。

根据我们的经验,在选择间距的值时,为了具有和谐感,我们把间距分成三组:小(Small)、中(Medium)、大(Large)。两个小间距之间的差别是 4pt,中间距与小间距相差 6pt,而大间距直接相差 8pt,给用户一种循序渐进的感觉。这些间距的定义能覆盖 App 所有的使用需求了。

有了统一的间距定义,设计师就可以在设计稿里面标注间距的定义,而不是具体的值。开发者也可以通过代码中原先定义好的间距,而无须每次都硬编码(Hardcode)间距的值。

除了 iOS 以外,统一的间距还可以用到 Android 和 Web 上,如下面是开源设计规范 Backpack 所定义的间距,其包含了 iOS、 Android 和 Web 三个平台。

有了这些间距的定义以后,设计师只需设计一份设计稿,不同平台的开发者都可以使用同一份设计稿进行开发。

字体

任何一款 App,都离不开文字,除了其本身传达信息,文字的各种样式,包括字体类型、大小、粗细,在其中也承担着重要角色。比如合理使用行楷会给人美感;字体放大可以暗示优先阅读;字体加粗,会起到强调的作用,吸引用户关注等等。

在开发当中,字体类型、字号大小、字体粗细分别由 Font family、Font size、Font weight 属性定义。除非有特殊的原因(如品牌需要,或者为了增强游戏体验),iOS 的 App 一般都使用 iOS 系统所自带的字体系列。这样更能符合用户的阅读习惯。在自带的字体系列的基础上,通过把字号大小和字体粗细组合起来定义一些字体类型。下面是我们 Moments App 所定义的字体规范。

根据字体类型的时候,我们分成四组:

  • 大标题(Display),用于显示这个页面的唯一标题,使用特大字号(如 42pt 和 36pt);

  • 标题(Titile),用于显示段落的标题,我们提供了五种不同的段落标题,其字号由大变小;

  • 文本(Body),用于显示一般的内容文本,我们提供了普通和加粗两种类型来呈现不同的文本;

  • 小文本(Small text),使用较小的字体来显示辅佐内容,例如备注、版本信息等。

为了让读者有更好的阅读体验,我们在字体粗细上都统一选择了偏细的字重,比如使用semibold而不是bold,并且选择light而不是regular来定义这套字体。

有了这些字体类型的定义,设计师可以很方便地选择字体,比如在设计文章内容部分时,可以从body或者bodyBold中选择出来,而不需要思考到底是使用 14p、15pt 还是 16pt。同时,开发者也可以使用原先定义好的字体组件,提升效率。

除此之外,还有动态字体,因为我们在此之前已经对字体的类型、大小、粗细做了定义,设计师只需要做一个设计稿就行了,无须为不同的字体设计出不同的版本,而我们开发者只需要通过封装好的字体组件来支持即可。具体如何封装字体组件,我会在第 12 讲详细介绍。

颜色

在一款 App 的视觉体验中,颜色毫无疑问在其中占据很重要的位置。如果颜色搭配不当,很容易被用户吐槽甚至弃用。所以设计之初,就要对颜色做好规范。

为了给用户提供颜色一致的体验,在 App 设计中,我们一般采用统一的调色板(Color palette)来完成。在实际工作当中,你可以和设计师一起在各类开源设计网站中寻找,比如,以天蓝为主色调的 Backpack,还有谷歌的 Material Design 还提供了调色板生成工具,你可以根据自己品牌的颜色来生成一个调色板。

来源:backpack.github.io

来源:2014 Material Design color palettes

有了调色板,我们就可以在设计常用组件时(如按钮,卡片,警告信息等)使用调色板里面的颜色。例如 Backpack 的主色是天蓝色,在设计按钮时,主按钮的背景可以使用天蓝色(#0770e3)。

为了照顾不同用户的使用习惯,提高夜间视觉体验,iOS 系统在原有的浅色模式之外,还提供了深色模式。我们在设计调色板的时候,也最好考虑到对深色模式的支持。

来源:backpack.github.io

在  Moments App 项目中,我们采用 Backpack 的调色板来设计深色模式下的颜色。该调色板定义了一组语义化的颜色(Semantic color)供我们选择。比如设计师在选择背景颜色的时候可以根据需要从背景主色、背景副色、背景第三色中任选一个,Backpack 每种颜色都提供了浅色模式和深色模式两种类型,设计师在设计过程中就不需要为这两种模式制作不同的设计稿了,开发者也可以使用定义好的颜色组件来同时支持两种模式,不需任何额外的工作。

需要注意的是,在定义语义化颜色时要特别注意颜色之间的对比度,例如使用了Text Primary Color的文本在使用Background Color的背景下能容易阅读,而使用灰色的背景再使用黑色的文本会难以阅读。

如果你所在团队没有专门的设计师来定义这些颜色,也可以使用 iOS 提供的动态系统颜色(Dynamic System Colors),它为我们定义了同时支持浅色和深色模式的各种颜色。

图标

图标(Iconography)在 App 设计中也承担重要的作用,它可以有效地把功能呈现给用户,例如一个加号(+)的图标能让用户知道点击的时候可以进行新增操作。图标也能简化界面的设计,让页面更加吸引用户,例如在买房的 App 里面我们可以使用一辆车的图标表示多少个车位,一个浴缸的图标表示多少个洗手间,这样能省去大量的文件描述。图标还能帮助用户导航。因此我们在设计 App 的时候也需要合理地使用图标。

如果你们公司没有专门的图标设计师,那么你可能会购买或者使用一些开源的图标。这里我有一些建议。

首先,同一个 App 只需要一种图标,千万不要把不同风格的图标混合使用。

其次,如果没有特殊要求,我推荐直接使用苹果公司提供的。具体来说,在 iOS 系统内置的 SF Symbols 为我们提供了 3150 个一致的、可定制的图标,如下面的例子。

SF Symbols 有很多优点:

  • 它数量巨大,几乎覆盖所有应用场景;

  • 整合了 San Francisco 字体系统,当用户改变字体大小的时候,这些图标都会自动对齐;

  • 所有图标都支持颜色,我们可以根据需求搭配不同的颜色;

  • 使用这些图标时也无须安装,可以减少 App 的体积。

我们的 Moments App 也使用到 SF Symbols 来呈现点赞按钮。

SF Symbols 里绝大部分的图标都通过了轮廓和填充两个版本,我们可以使用填充的图标表示选中状态。

常用组件

随着 App 功能的增多,你会发现一些 UI 会出现在许多地方,例如下图的用户头像,分别出现在个人主页、朋友圈、点赞处。

在这种情况下,我们就可以把这个重复出现的 UI 封装成一个常用的设计组件,并纳入我们在制定设计规范中。常用的组件一般有按钮(Button)、用户头像(User avatar)、复选框(Checkbox)、徽章(Badge)等等。下面是 Moments App 设计规定中用户头像组件的定义。

有了用户头像组件,设计师在设计过程中就不需要考虑在呈现头像时需要怎样设计,使用的圆角到底要多少度,是否需要阴影和边框等等。开发者也可以使用原先封装好的组件来加快开发。

需要注意的是,UI 组件是在开发过程中发现某个 UI 反复使用后才封装的,在开始的时候不要贪多,避免定义一堆不用的组件。

总结

在这一讲,我们通过间距,字体,颜色,图标和常用组件为例子来讲述一套统一的设计规范。有了它,能极大降低设计师与开发者的沟通成本,也节省设计师和开发者在设计或实现过程的时间,一举多得。

在定义和管理设计规范过程中需要注意这几点:

  • 在定义规范过程中,设计师和开发者要一起参与进来,达成共识方便后期彼此间的沟通与协作;

  • 在更新规范时,要有设计审查流程保证主设计版本一直在受控状态,我建议使用上节课所说的 GitHub Pull Request 流程来管理这套规范;

  • 每次更新的时候,都需要更新设计文档,就像 Backpack 的文档一样,当设计师或者开发者有疑问的时候都可以把这个文档作为唯一信息源来查看;

  • 坚持少即是多的原则,特别是在定义规范开始的时候,不要求多求全,可以根据业务需要不断地迭代完善。

思考题:

你们公司有没有统一的设计规范,如果有,是通过怎样的方式来管理的?

你可以把自己的思考写到下面的留言区哦,这一讲设计规范我就介绍到这里,下一讲我将介绍如何封装和开发设计组件。


ios架构与开发第二课 代码规范管理与自动化构建相关推荐

  1. qq机器人开发第二课-发送图片

    重要的知识点: 监听图片发送,要注意一点 图片除了可能在一般的消息中发送,还有可能是通过离线文件发送来的 qq机器人开发第二课-发送图片

  2. ios架构与开发第七课 UI层架构与点赞实战

    20 View Model 架构:如何准备 UI 层的数据? UI 是 App 的重要组成部分,因为所有 App 都必须呈现 UI,并接收用户的事件.为了让 UI 能正确显示,我们需要把 Model ...

  3. 【项目篇】Android团队项目开发之统一代码规范

    前言 团队项目开发前的统一三要素:统一需求/开发文档,统一代码规范,统一环境(编译/测试/发布). 一个项目团队,要想有高效的产出,必须在团队协作上下好功夫,必须在项目开发统一进度上做好协调.只有在高 ...

  4. 网站开发之前端代码规范

    前端代码规范 前言 一.唯一定律 二.前段代码规范 (一)命名规范 1.1.项目命名 1.2 .目录命名 1.3.文件存放位置 1.4.JS.CSS.HTML.PNG 等文件命名 1.4.命名严谨性 ...

  5. 吴恩达Deeplearning第二课代码bug修正大全

    解决维度不匹配问题 第一周 Initialization 错误: plt.title("Model with Zeros initialization") axes = plt.g ...

  6. iOS架构-cocoaPods之自制私有库及管理(17)

    CocoaPods是iOS,Mac下优秀的第三方包管理工具,类似于java的maven,给我们项目管理带来了极大的方便. 个人或公司在开发过程中,会积累很多可以复用的代码包,有些我们不想开源,又想像开 ...

  7. Python之代码规范管理工具(pylint、black)

    参考链接 在之前<使代码整洁的几种规范>文章中,介绍了比较常用的变量.函数等定义方法,本文专门针对 python 的代码风格进行介绍,主要用到两个超牛的工具 pylink 和 black, ...

  8. 「移动开发」iuap mobile玩转前端自动化构建

    提起iuapmobile studio,相信大家已经不再陌生了,是进行移动应用开发的强大集成工具与必备神器.借助强大的iuap mobile studio,可以实现对开发.测试.调试.打包.发布全程管 ...

  9. 阿里高级微服务架构师亲手教你搭建一套可自动化构建的微服务框架

    这套微服务框架能干啥? 这套系统搭建完之后,那可就厉害了: 微服务架构 你的整个应用程序将会被拆分成一个个功能独立的子系统,独立运行,系统与系统之间通过RPC接口通信.这样这些系统之间的耦合度大大降低 ...

最新文章

  1. java关键字 面试_java面试 关键字
  2. C++中的union(联合体,共用体,数据变量可以共享内存,以节省内存空间)
  3. chrome浏览器使用技巧
  4. goj基础环境的配置
  5. SAP云平台CloudFoundry上部署了一个应用的技术明细
  6. html中input、label、form、textarea、select
  7. vscode remote ssh 重新连接_使用 VSCode 愉快地进行 R 远程开发
  8. php界面框架luy_LazyPHP
  9. 网络中计算机传输信息时所遵从的不同规则,2012年上海市高中学业水平考试信息科技试卷(第6套)...
  10. ftp/sftp定时自动上传文件脚本(CentOS)
  11. 1. 使用 MegaRAID Storage Manager 监控
  12. 移动设备支持方式-移动设备管理MDM
  13. python编写安装脚本_ido-Python 安装脚本包
  14. 大学计算机技术导论,北京邮电大学计算机学院网络技术导论第一章资料.ppt
  15. Android打包apk实现原理与流程(雷惊风)
  16. 频繁出现域名无法解析该如何处理
  17. 第21节--非线性回归(下)
  18. 在matlab中数据波动指标,阶跃响应指标的matlab计算
  19. opengl简单模拟行星运转
  20. CUMT-CTF第二次双月赛Writeup

热门文章

  1. Matlab移动色带位置
  2. DSA数字签名算法及其实现
  3. 学生如何免费申请使用idea
  4. 京东宣布涨薪,两年内将员工平均年薪从14薪涨到16薪!
  5. Android中悬浮窗口
  6. Java微信支付-支付成功异步回调验签
  7. 支付宝支付成功后要重新登录问题解决小记
  8. 运维 如何选择最合适的服务器方案(一):服务器硬件配置
  9. 瞬间击退眼睛疲劳的五大运动
  10. 敏捷管理(2)- 敏捷价值、需求、估算、计划、监控、风险管理