如果我想构建一个通用的 UI 组件(一个将处理自己的视图和逻辑、数据源等的组件),子类化 UIViewController 是一个好习惯吗?
4 回答
我过于简单的答案是“不,在构建通用组件时不会自动从UIViewController
子类开始”。然而,仅此一项并不是一个有用的答案,在某些情况下,UIViewController
子类正是正确的解决方案。
相反,让我们考虑如何决定你的组件应该是什么。我认为解决这个问题的最好方法是回答三个问题:
- 这个组件负责什么?
- 你想如何与之互动?
- 它的依赖是什么?
在所有情况下,我们都可以尝试尽可能简单地开始,只有在这些问题的答案需要时才增加复杂性。
这个组件可以只是一个功能吗?没有对象,没有类,如果您只想添加一些行为,那么我们可能需要的只是一个函数。如果此行为仅适用于特定的现有类型,那么也许我们需要现有类上的类别。
不足以涵盖我们想要做什么?好的,我想我们可能正在谈论一种新类型,所以让我们创建一个类。它可以只是一个子类NSObject
吗?
想要在视图中显示一些东西?好的,那么我们至少有一个子类,如果它更具交互性UIView
,也许是一个子类。UIControl
视图需要一些数据来支持它吗?没问题,听起来我们现在需要两件;显示数据的视图和提供数据的数据源。在这里,视图不需要知道谁创建和拥有这个数据源。只要向视图提供了一个,我们就可以使用它,其他任何事情都超出了视图的职责范围。类似地,我们可能会添加一个委托来通知其他一些与视图交互的对象。
如果我们还没有涵盖这个组件的所有职责,那么也许我们还需要另一个部分,在控制器层中的一些东西来管理我们的视图。我们仍然没有(还)谈论一个UIViewController
虽然!拥有一个作为NSObject
子类的“控制器”或“服务”很好。如果这部分需要做的只是管理网络连接,或者将NSFetchedResultController
结果映射到我们视图的数据源协议并更新视图,或者只是提供最常见的模型对象映射到视图数据源的便捷实现,那么一个简单的“控制器” “对象仍然是我们所需要的。
还是不够?最后,我们到了考虑提供UIViewController
子类的地步。也许我们想让组件的用户只展示一个模态视图控制器来移交交互的责任(发送电子邮件,撰写推文)。也许我们想要提供一组与视图控制器生命周期事件相关的通用默认行为(UITableViewController
闪烁的滚动条-viewDidAppear:
)。
从您需要支持您想要提供的行为的部分构建您的组件,但要使其尽可能小和简单。
我会说不。但这取决于。如果您的组件管理其他视图控制器或具有/将具有任何类型的非特定于视图的逻辑(例如导航逻辑、业务逻辑等),那么您应该子类化视图控制器。再一次,这使它不仅仅是一个 UI 组件。
否则,您应该继承一个 UIView,就像 Apple 对许多组件所做的那样,包括 UITableView(说到数据源)、GLKView、UICollectionView。
是的,在许多情况下这是一种很好的做法。iOS SDK 包含许多UIViewController
子类示例。其中一些只包含少量的通用行为,如果不进行定制基本上是无用的:
GLKViewController
UICollectionViewController
UITableViewController
其中一些提供了重要的通用行为,但仍然主要是您自己的视图控制器的容器:
UINavigationController
UIPageViewController
UISplitViewController
UITabBarController
但它们中的大多数本质上都是完整的包,很少或不需要(或能力)自定义它们的行为:
ABNewPersonViewController
ABPersonViewController
ABUnknownPersonViewController
EKCalendarChooser
EKEventEditViewController
EKEventViewController
GKAchievementViewController
GKFriendRequestComposeViewController
GKGameCenterViewController
GKLeaderboardViewController
GKMatchmakerViewController
GKTurnBasedMatchmakerViewController
MFMailComposeViewController
MFMessageComposeViewController
MPMediaPickerController
MPMoviePlayerViewController
PKAddPassesViewController
QLPreviewController
SKStoreProductViewController
SLComposeViewController
TWTweetComposeViewController
UIActivityViewController
UIImagePickerController
UIReferenceLibraryViewController
UIVideoEditorController
如果您考虑所有这些的共同点,您可能会得出结论,它们都有两个共同点:
每个都有与之交互的特定模型(在 MVC 意义上),并且该模型并不特定于您的应用程序。模型差异很大(
UIVideoEditorController
的模型是单个视频;UIImagePickerController
的模型是整个照片库;GKAchievementViewController
的模型是可能在“云”中的 Apple 服务器上的数据库),在某些情况下您提供模型(或某些模型的属性),在某些情况下,您会在最后收到模型(或其片段)。但在任何情况下,视图控制器都会处理用户和模型之间的所有交互,而您的应用程序很少或没有持续的帮助。每个都提供自己的视图层次结构,几乎不需要或不需要自定义(甚至允许)。
换句话说,这些视图控制器中的每一个都不仅仅是 MVC 模式的“C”。这是整个 MVC 冰山的一角。
如果您的组件也是 MVC 的冰山,那么将冰山一角暴露为UIViewController
子类是非常合适的。
这取决于您使用的 iOS 版本。
A. 在 iOS 5.0 之前,这不是一个好的做法,也不推荐。
B. 从 iOS 5.0 开始,就可以了。
子类化一个 UI 组件的视图控制器(UIView),这意味着你想使用视图控制器生命周期方法来管理用户交互和视图显示过程,数据模型等。所以,如果生命周期方法不能轻易自动化调用,视图控制器没有那么有意义。
从 iOS 5.0 开始,可以创建自定义视图控制器容器,因此,可以使用视图控制器构建自定义通用 ui 组件以添加为子视图控制器。
在 iOS 5.0 之前。使用自定义委托协议子类化 UIView 是正确的方法。另一件事是关于代码组织。将所有自定义委托方法放到另一个自定义类中,而不是在视图控制器中,在视图控制器中只需初始化一个委托类实例,并将其设置为视图委托属性。