使用指南 - BB9z/iOS-Project-Template GitHub Wiki
推荐直接在项目页下载仓库的 zip 包:

下载解压后,双击执行项目根目录下的 bootstrap 脚本,该脚本会协助你完成项目配置。

手动修改项目名
如果 bootstrap 脚本不工作,手动推荐步骤如下:
- 重命名
App.xcworkspace,然后打开; - 在左侧文件管理中,选中最上方蓝色的项目文件,按回车重命名。成功的话 Xcode 会弹出重命名其它 target 的对话框,继续重命名这些 target;
- 修改 Podfile 中的 target 名
- 执行 pod install
- 编辑 workspace 中的
contents.xcworkspacedata,项目路径改成相对路径,然后打开 workspace - 在文件管理中,删除旧的 libPods-App.a 和 xcconfig 文件
- (可选)修改
ProjectFileVerification.rb中的main_target_name - (可选)修改项目属性中的 Organization 名和 Class Prefix
关于项目文件的组织方式,请参看文件夹下对应的 README 文件。
大部分组件在文件头有自己的说明,这里进行补充,尤其是成体系的内容。
这部分组件目录已有说明,但很重要,有必要提出强调。
定义项目中的统一行为好处多多,很有必要。举例说明:
- 通过 MBGeneralItemExchanging 和 MBGeneralListExchanging 定义了应用统一的传值方式,在此基础上可以实现一定程度的自动传值,见下面的界面间传值方式,对手动传值也有帮助;
- MBGeneralListDisplaying 定义了列表访问方式和刷新方法,便于其他组件发现列表或进行刷新。
页面如果通过 segue 跳转,项目有一套自动的传值,自动尝试传递 item 或 items 属性中的变量,支持 vc 到 vc,cell(及子 view 触发的跳转)到 vc,具体行为参见 MBGeneralSegue 文档。
item 及 items 属性分别定义在 MBGeneralItemExchanging 和 MBGeneralListExchanging 协议中,属性只需要 @objc 暴露出来即可,无需显示声明协议。
大量 UI 第三方库会选择把外观通过代码固化在组件中,这就需要它们的作者提供多于实际需要的外观配置项,或者多种外观,因为不同的项目,外观差距是很大的。这么做成倍提升维护成本,导致臃肿的组件,提升理解难度。
只集成一套外观也是不可取的,没通用性,必然面临常常需要修改的状况,此时现有的代码可能是种负担,失去了组件复用的意义。
这套模版里 UI 相关组件能有几十个,但应该没有任何一个有随组件定义的外观,甚至像红点提示的气泡、数字这样的都没有预设外观。组件里有的多只有逻辑和少量的配置项,外观需要在 Interface Builder 定义。
用时需要做的多是在 Interface Builder 中修改控件的类,调整 IBInspectable 属性,连接必要的 IBOutlet,有些控件还需要一定的层级结构。
UI 与逻辑分开是公认的基本原则,区别在粒度。View 就不可以是控制器么?在 view 的代码里控制逻辑,外观在 Interface Builder 画,对我来说这样很舒服。外观尽量定义在 Interface Builder 的另一个好处是直观,通过画布了解一个界面的外观,比去读代码生成的绝大多数情况更快,更容易。
以我的经验来看,view 控制器化基本没副作用,组件复用起来很稳定,很少需要修改。同时解决了很多项目庞大的 view controller 的问题,我这超百行的 view controller 都算大的,90% 一屏以内解决。外观的复用可在 Interface Builder 中复制粘贴。
但项目级别的控件我是支持集成外观的,一个有着正常设计的 app,里面的按钮、输入框之类的控件应该是有统一的外观或者固定几套外观,这时应避免在 Interface Builder 中重复调各种参数,而通过设置不同的类或样式名区分,在代码中统一设置样式。参见:Button.swift,TextField.swift
模型应该是数据和相关操作的集合体,而不是只单单存放数据。同时,富模型不是把所有能塞的东西统统塞入模型中,除了公用模块(如网络请求、数据库)外,模型文件不应引用其他模块,比如引用某个 view。
然后我们讨论数据模型如何在页面、view 间传递,数据变化如何更新 view。
如果项目中允许用 SwiftUI,官方提供的 @State、@Binding 和观察等机制是不错的选择,参考 Data Essentials in SwiftUI (WWDC 2020, Session 10040)。
但目前阶段,假设一个模型实例在多个页面,多个 view 间传递,在任一处进行的修改会在所有地方得到反应,这是典型的引用传值场景,这些页面、view 持有的模型应该指向同一个实例。用 structure 同步起来麻烦。
随项目的 demo 演示了通过 multicast delegate 和模型更新协议实现的一处修改多处更新,这种方式优于 KVO 和 notification 通知。
整个应用的用唯一的 UINavigationController 进行管理,这个管理了所有页面的导航控制器可以集中做很多事,举例:
- 在 view controller 中声明样式(如是否隐藏导航条、是否显示底部 tab、导航条颜色等),导航在切换页面时进行统一设置,这比在 viewWillAppear()、viewWillAppear() 中进行设置要好很多;
- View controller 从导航移除时取消与页面管理的网络请求;
- 页面切换的数据分析埋点,一次性写在导航中即可;
- 有游客模式的应用,导航统一控制跳转登入页;
- 有各种教程、弹窗的应用,可用导航统一调度。
随项目 demo 演示了不用 UITabBarController 实现的底部 tab,另一个值得注意点是 NavigationController 不是顶层 vc,而是套在 RootViewController 中的,这是为了有弹出层能遮住导航栏。
调试时,Xcode 可以模拟地理位置信息,可以是固定的点或者随时间变化的轨迹。模拟器和真机上都可以模拟,在真机上可能必需重启系统才能恢复真实位置的定位。
在调试菜单,有一个定位菜单可以选择模拟数据,高亮表示正在激活。

在调试时,可以指定应用沙箱中的所有数据,可以用于测试特定数据时应用的行为。典型场景如测试数据库升级。
可以在 target scheme -> Options -> Application Data 中指定。

你可以在设备页面下载应用的 data package。
