UISplitViewController 和复杂的视图层次结构
Posted
技术标签:
【中文标题】UISplitViewController 和复杂的视图层次结构【英文标题】:UISplitViewController and complex view hierarchy 【发布时间】:2010-04-14 19:01:09 【问题描述】:我正在做一个 iPad 技术演示,但遇到了一个严重的技术问题。
我有一个利用 UISplitViewController 的应用概念,但不是作为整个应用的主控制器。
应用流程大致可以这样描述:
主屏幕(UIViewController) 列表->详细“目录”(UISplitViewController) 超级细节屏幕(UIViewController,但也可能是 SplitView 的子级)。
问题在于主页和目录之间的流程。一旦 UISplitViewController 视图被添加到 UIWindow 中,它就会开始发出嘶嘶声。
问题可以总结为:
当 UISplitView 生成一个弹出视图时,它似乎被锁定到其父视图。从 UIWindow 子视图中删除 UISplitView 后,您将收到 CoreGraphics 异常,并且该视图将无法删除。
当添加其他视图时(假设在这种情况下,您要返回的主屏幕),它们不会自动旋转,而是由于 CG 异常而无法删除的 UISplitView 继续响应而是旋转,导致无法仅“处理”的可怕渲染错误。此时,添加任何视图,甚至重新添加 SplitView,都会导致一连串的渲染错误。
然后我尝试简单地将 SplitView 保留为“底部”视图,并继续从其顶部添加和删除主屏幕,但这失败了,因为 SplitView 支配了方向更改调用,而主屏幕不会旋转,即使你调用 [homeScreen becomeFirstResponder]
您不能将 SplitView 放入 UINavigationController 之类的层次结构中,您将得到一个彻底的运行时错误,因此该选项已被取消。模态只是看起来很糟糕,无论如何都不鼓励。
此时我的假设是,处理此问题的唯一正确方法是以某种方式“解除” UISplitViewController 以便可以将其从其父视图中删除而不会引发未处理的异常,但我不知道如何。
如果您想查看完全符合我需要的应用程序,请查看 iPad 应用程序商店中的 GILT Groupe。他们成功了,但他们似乎已经编写了一个完整的自定义视图转换集。
我们将不胜感激。
【问题讨论】:
【参考方案1】:苹果states:
拆分视图控制器的视图 应始终作为根安装 应用程序窗口的视图。你 永远不应该呈现拆分视图 在导航栏或标签栏内 界面。
这确实意味着它应该是根视图而不是另一个视图的子视图。即使他们添加:
您不应该在导航或标签栏界面内显示拆分视图
这并不意味着您也可以将其添加为任何其他控制器的子视图。 (对不起)
我有一种感觉,你所经历的是尝试这样做的副产品。我真的很惊讶 GILT Groupe 的应用程序没有被拒绝。 Apple 最近倾向于严格执行这些 HIG 准则。当您尝试将它们添加到 NavigationController 时,它们(正如您已经发现的那样)会导致相当严重的运行时错误。
【讨论】:
正确。我实际上暂时“解决了”这个问题,但是将 SplitView 设为根,并将所有其他视图呈现为具有过渡的全屏模式。我敢打赌,GILT 可能正在做一些与一些自定义模态转换类似的事情。非常令人沮丧的是,Apple 会发布对如此有用的控件并对其进行如此多的限制。 Apple 的指导方针还规定禁止过度使用模态...但是在这种情况下它们给了我们什么选择?【参考方案2】:我自己解决了这个问题...实际上解决了...通过将所有其他可能的全屏视图呈现为 SplitView 的模态...
在我的书中,这是一种令人讨厌的做事方式,但如果您只想在应用程序中“有时”使用 SplitView,Apple 会让您别无选择。
【讨论】:
嘿 Jasconius..我正在尝试做和你一样的事情,即使我在看 Gilt 一个应用程序。我需要完全相同。你能发布一些示例代码吗?这将是很大的帮助..谢谢 是的,这正是我们在 Gilt 应用程序中所做的。 SplitViewController 在另一个控制器中确实不能很好地发挥作用,所以我们只能使用模态视图来导航离开拆分视图。 请查看我的custom UISplitViewController。它确实可以满足您的需求。【参考方案3】:我通过创建第二个 UIWindow 取得了一些成功。我将 UISplitViewController 与它相关联,并在我想显示拆分视图时将其与主窗口切换。它似乎按照我想要的方式工作,除了轮换略有延迟和关于“wait_fences”的日志消息。
【讨论】:
好主意。但是,它如何优雅地处理方向变化? 似乎工作正常。两个窗口上的方向都会跟踪设备的物理方向,就像您期望的那样,无论哪一个是可见的。唯一的缺点是“wait_fences”错误会在旋转中产生轻微的视觉障碍。可能不是生产质量,但对于我的演示来说已经足够了。 如何切换应用程序的窗口? 在 AppDelegate 中,我创建了第二个 UIWindow,添加了我的 UISplitViewController,然后将其标记为隐藏。我为原始 UIWindow 进行了正常设置。当用户想要查看 UISplitViewController 时,我将 makeKeyAndVisible 消息发送到第二个 UIWindow。完成后,我将 makeKeyAndVisible 消息发送到原始窗口。【参考方案4】:除非您为越狱设备开发,否则弯曲苹果规则/愿望不是一个好主意。就像上面的 Jann 和 Jasconius 状态一样,这意味着保持 splitView 控制器视图根,而不是过度使用模态(模糊)并且不使用多个窗口。
另外,Gilt 应用程序仅在美国可用
我也一直在努力寻找解决方案,并最终像 Tuannd 所说的那样以编程方式从窗口中删除视图,但景观渲染错误是不可原谅的。
@Jasconius,您可以随时展示的最大模态数是多少?
【讨论】:
好吧,不超过一个,因为这似乎是你可以轻松摆脱的所有。如果必须的话,我会手动放置子视图。我现在离开了那个项目,进入了一个全新的 iPad 自定义控件地狱。 iPad 的方向管理真是太可惜了。【参考方案5】:我正在努力解决同样的问题。我一直在尝试将 UISplitViewController 作为黑盒进行各种操作,看看它的反应。
我似乎已经想出了一个解决我的案例的方法,而且效果令人满意。
关键似乎是添加到 UIWindow 的第一个视图是唯一正确初始化的视图。我遇到的所有问题往往源于设备方向的错误通知。添加的第一个视图,显然已正确配置。
就我而言,我不希望 UISplitView 作为第一个视图。以下对我有用。
应用委托 application:didFinishLaunching 方法比较特殊。必须在此处将视图添加到 UIWindow。如果在别处完成,则配置不正确。
本质上是魔术酱,是将拆分视图作为添加到窗口的第一个视图。只要您保留 UISplitViewController,就可以将其删除。从那时起,您可以交换其他视图,包括 UISplitView,并且大多数情况似乎都可以。
我仍然遇到了一些问题。拆分视图以外的视图上的弹出框在视图框架和工具栏按钮的位置上会混淆,并将显示在错误的位置。然后我把它放在一个特定的位置,这似乎可以处理这种情况。
如果拆分视图上的弹出框仍然显示,并且您尝试查看另一个视图,则第二个视图的方向会混淆并横向显示。如果在显示弹出窗口之前访问该视图,则一切正常。我已经解决了这个问题,我在切换到任何其他视图之前手动关闭了弹出框。
如果有帮助,这里是代码。所有的控制器都是appDelegate的实例变量
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
// This also seems to work as good magic. Seems to set orientation and size properties that persist.
[window addSubview:splitViewController.view];
[splitViewController.view removeFromSuperview];
[self switchToNewViewController:firstController];
[window makeKeyAndVisible];
return TRUE;
- (void)switchToNewViewController:(UIViewController *)newViewController
[popoverController dismissPopoverAnimated:FALSE];
if (newViewController != currentViewController)
[currentViewController removeFromSuperview];
currentViewController = newViewController;
[window addSubView:newViewController.view];
【讨论】:
在 appDidFinishLaunching 中添加然后删除拆分视图是一个很好的见解。 “根视图控制器”的事情和错误的方向通知一直是一个公认的问题,SplitView 不能成为子视图这一事实完全加剧了这种情况。在一个理性的世界中(阅读:基本上任何不使用 SplitView 的应用程序)你总是有一个通用的 RVC 将方向信息发送给它的孩子。 Splitview 打破了这一点。你最好真的制作自己的拆分视图。【参考方案6】:只是想说我遇到了同样的问题,找到了这个论坛主题,并遵循了上面 g051051 的建议。这对我来说非常有效。我没有看到任何故障,设备控制台中也没有关于 wait_fences 的消息。
我只是使用 IB 在主 XIB 中创建了两个 UIWindow 对象,正常创建了 UISplitViewController,然后还创建了从 UIViewController 派生的另一个控制器的实例(我将其用于全屏显示)。我只是通过将每个 UIWindow 的 rootViewController 附加到相应的控制器来将它们连接起来。
在 application:didLaunch...: 方法中,我可以决定向哪个窗口发送 makeKeyAndVisible 方法以及将哪个窗口设置为隐藏。当用户想要来回切换时,我只需将 makeKeyAndVisible 发送给一个并在另一个上设置 hidden 属性,这就是它的全部内容。
如图所示,所有与旋转相关的消息都会适当地发送到每个控制器,无论哪个与当前可见窗口相关联。
无论如何,对我来说效果很好,而且实际上很容易设置。
【讨论】:
其实有个怪癖:在 XIB 中设置 UIWindows 的 rootViewControllers 会导致一个小问题。问题是,在肖像模式下启动时,UISplitViewControllerDelegate 的方法 splitViewController:willHideViewController:... 方法最初没有被调用,因此可以添加 UIBarButtonItem 。如果在 application:didFinishLaunchingWithOptions: 中明确添加 splitViewController.view 作为 UIWindow 的子视图,则此初始委托方法被正确调用。以上是关于UISplitViewController 和复杂的视图层次结构的主要内容,如果未能解决你的问题,请参考以下文章
在 UIViewControllers 和 UISplitViewController 之间导航 [关闭]
iPad 上的 UITraitCollection 和 UISplitViewController
故事板、UIViewController 和 UISplitViewController