重用UIViewController用于模态和非模态情况

我有一个UIViewController – 我们称之为“FormController” – 它只是一个编辑对象的表单。 我想在两种不同的情况下使用它:

  1. 创建一个新对象 – 使用UINavigationController的presentModalViewController:方法。

  2. 编辑现有对象 – 将视图控制器推送到UINavigationController堆栈,而不是使用对话框方法。

在模态情况下,我希望有一个带有“取消”和“完成”按钮的工具栏,但在堆栈情况下,我想只有UINavigationController提供的导航栏。

这类似于Contacts应用程序,其中“New Contact”和“Edit Contact”屏幕似乎使用相同的视图控制器,但New Contact表单以模态方式呈现,而Edit屏幕被推送到导航堆栈。

我的问题是:在不必编写2个独立但大部分相同的视图控制器的情况下,处理这两种情况的最佳方法是什么?

我想创建一个“ModalFormController”,通过组合封装裸“FormController”并添加一个工具栏,但我在文档中的某处读到Apple不建议嵌套视图控制器。

我所做的(有时)是设置一个enum ,指定视图控制器的类型。

例如,您可能有两种类型: Edit类型和Add (“新”)类型。

Add类型通过模态视图控制器实现,而Edit类型则推送到现有导航堆栈。

在视图控制器的-viewDidLoad:方法中,我只是做一个switch/case树,它根据上面指定的类型枚举设置标题和其他外观特征。

关于这一点的好处是可以轻松添加新类型。 缺点是用于处理此枚举的条件树可能会很快变得复杂,具体取决于类型的不同。

但是switch/case树使管理起来更容易。

所以,这取决于你要对这两种类型做什么。 但它绝对可行。

为什么不使用子类? 使ModalCreateFormController成为EditFormController的子类,并处理子类中的模式特定内容。

除了在视图控制器上具有显式属性之外(正如Alex Reynolds建议的那样),我发现的另外两种方法是:

  1. 如果您正在编辑某种模型对象,请询问其当前状态。 如果它已被保存,那么您处于编辑模式。 否则,您处于创建模式。

  2. 查看控制器的parentViewController属性的值。 如果它是UINavigationController的一个实例,那么你就在导航堆栈中。 如果您以模态方式显示,它将是列表控制器的实例。

Ug,我讨厌额外的伊娃……

我改用它:

 if([[self.navigationController viewControllers] objectAtIndex:0] == self){ //Modal }else{ //Pushed } 

这有点像黑客,但我们正在使用这样的逻辑:如果违规视图控制器是堆栈中的第一个,你就不能回头了。 实际上我们忽略了它是否是模态显示的事实。

在我的应用程序中尝试了几种不同的方法后,我不得不这样做了很多次,包括使用forwardInvocation的模态子类和可重用的模式助手类。 我发现最好的模式是制作一个containsModalViewController方法,每个视图控制器(通常)创建并返回一个UINavigationController,供调用者使用presentModalViewController。

在大多数情况下,这个方法构建并返回一个带有self的UINavigationController作为根视图控制器(重复调用方法检查self.navigationController并返回,如果它不是nil)。 在其他情况下,我首先制作了一个虚拟根控制器并在第二个时间推动自我以获得后退按钮。 然后一个技巧可以用来抓住后退按钮: http : //smallduck.wordpress.com/2010/10/05/intercepting-uinavigationcontroller/

在某些情况下,视图不需要导航栏,因此此方法只调整一些标志并返回self。 我甚至发现在某些情况下确实需要导航栏,使该方法调用self.view更简单,然后调整视图层次结构以添加UINavigationBar并再次返回self。 但无论如何,设置通常与该方法隔离,并且调用者在每种情况下都处理它。

Apple解释了联系人应用程序的工作原理:

要允许自定义视图控制器类用于显示和编辑内容,请覆盖setEditing:animated:方法。

您可以免费获得一些function,例如Edit/Done按钮。