UITabBarController,MoreNavigationController和设备旋转的圣杯 [英] UITabBarController, MoreNavigationController and the Holy Grail of Device Rotation

查看:123
本文介绍了UITabBarController,MoreNavigationController和设备旋转的圣杯的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:请先参阅我对这个问题的回答.这似乎是一个错误.已创建了一个最小的测试用例,并已向Apple提交了一份报告. (已从iPhone OS 3.1修复.)

这是我好亲密!"中的一个令人困惑的问题.部门.

我有一个基于Tab Bar的iPhone应用程序.每个选项卡均具有 UINavigationController ,其中包含通常的可疑内容(导航栏,表格视图...进而可能导致其他VC等).

现在,这些较低级别的VC之一将用于纵向横向模式.但是有一个问题.我们对风景友好的VC的 shouldAutorotateToInterfaceOrientation:不会被称为开箱即用!该怎么办?

这就是我们要做的.在我已在其自己的文件中实现的Tab Bar Controller中,它具有以下功能:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
     return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}

这最终将请求传播到了我的环境友好型VC,该VC也响应了此消息.我所有其他的VC都没有实现此方法,因此它们只是采用默认的纵向方向.

问题解决了!!!是的!

嗯,不太正确.:(

当在标签栏控制器的 MoreNavigationController 的深度范围内调用我的横向友好的VC时,似乎事情进展不太顺利.

我决定比较/对比从前四个标签栏 UINavigationControllers 中的一个调用的VC和从 > MoreNavigationController .这将有点过分详细,请耐心等待.希望逐个剧本被证明对侦探有用.

在加载应用程序时,将对标签栏控制器的shouldAutorotate ...方法进行多次初始调用.在这些早期情况下, selectedViewController 为零.但是,我们最终完成了加载,选择了初始选项卡,一切顺利.

对.首先,让我们选择前四个标签栏项之一,然后向下钻取我们的VC.

我们将选择第三个导航栏项目,因此这是第三个导航控制器.我们深入研究支持旋转的VC.快速检查确认父级确实是我们标签栏的视图控制器列表中的第三个导航控制器.好!

旋转设备.要求标签栏控制器自动旋转(请参见上面的代码).我们观察到 selectedViewController 也是第三个导航控制器,加上导航控制器的顶部和可见视图控制器都设置为支持旋转的可信VC.

因此,标签栏控制器会将 shouldAutorotate 消息转发到第三个导航控制器……但是我们的旋转友好型VC最终收到了该消息. (我在这里没有做任何特别的事情.也许所需的VC收到了消息,因为它是顶部和/或可见的VC?)无论如何,我们旋转到横向,调整大小,一切都很好. 伟大的成功!"

现在让我们点击该后退按钮并弹出VC堆栈,在此过程中保留横向模式.再次查询标签栏控制器.

这里有一点时间.我们的导航控制器的 topViewController 仍然是旋转友好的VC,但是 visibleViewController 现在已设置为 UISnapshotModalViewController !嘿.以前从未见过…………但埃里卡·萨顿(Erica Sadun).看起来像是消失的视图控制器"(在这种情况下肯定是真的-消失了).

随着我不断前进,可见的VC仍保持为Snapshot,但由于我的特殊VC最终不见了,因此顶层VC最终会更改为堆栈中的下一个VC.足够公平.

所以那是一切正常的情况.

现在让我们尝试相同的测试,只是这次我们要转到 MoreNavigationController (更多"选项卡栏项),并向下钻取与以前相同的VC类.就我而言,它恰好是标签栏控制器的VC列表中的第七个.

我们输入了可感知旋转的VC,并且...这一次它被要求直接旋转 !完全不要求标签栏控制器 允许旋转.嗯.

对父级VC的快速检查显示它是一个 MoreNavigationController .好的,那很有道理.

现在让我们尝试旋转设备. 没有来电.我们的断点都没有受到打击.不在我们的VC中.不在我们的标签栏控制器中. (嗯?!?!)

O-kaaaay.让我们弹出堆栈,回到相同的VC,然后尝试再次旋转.奇怪的.现在,我们在Tab Bar Controller中接到一个呼叫,要求自动旋转权限.在这里,选定的Controller是我们值得信赖的Nav控制器(#7),但这一次其 visibleViewController topViewController SET TO NIL

一旦我们从此处继续,调试器控制台中就会出现一条神秘消息:

使用两阶段旋转动画.到 使用更平滑的单级 动画,此应用程序必须 删除两阶段方法 实施.

之所以神秘,是因为我没有使用两阶段旋转动画!我的源代码中的任何地方都没有 SecondHalf 方法变体在起作用.

A,我的旋转感知VC从未被告知正在旋转(即使旋转确实发生在屏幕上),因此,我的视图当然被弄坏了.随之而来的是混乱和悲伤. :(

在这一点上,我们甚至都不会弹出堆栈.

我认为View Controller文档提示了可能的问题:

如果要执行自定义 定向期间的动画 更改,您可以选择以下两种方法之一 方法.习惯于改变方向 发生在两个步骤中,并带有通知 发生在开始,中间, 和旋转的终点. 但是,在iPhone OS 3.0中,支持是 添加用于执行定向 一步改变.一步一步 方向改变趋向于更快 比旧的两步过程要好 通常建议用于任何新产品 代码.

我想知道 MoreNavigationController 是否仍在响应两步过程,因此是否正在尝试使用单步过程?请注意,如果您回答两步消息,则一步变体将不起作用(同样,根据文档).我没有对他们做出回应,但是我暗中怀疑幕后的IS是什么.

实际上,如果我注释掉了单步方法,并尝试响应 willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration:,我 do 会得到备忘录!但是它仍然不能很干净地从栈上弹出(就视觉而言).即使是陌生人:也不会调用 willAnimateFirstHalfOfRotationFromInterfaceOrientation:duration:,即使我试图在 shouldAutorotateToInterfaceOrientation:中偷偷呼叫自己(使用FirstHalf消息)也是如此.它会在跟踪过程中立即返回,就好像我从未定义过它一样.叹气.

这就是逐个播放.

总而言之,任何人是否已成功处理了从选项卡栏控制器的MoreNavigationController中调用的VC的一步式设备旋转?想问的人想知道!

解决方案

Apple建议不要对UITabBarController进行子类化,因此我找到了一种使用类别来处理自动旋转的简单方法.它不能使用更多..."视图控制器来修复您的错误,但是我认为这是完成任务的一种更加苹果友好的方式(并且意味着您可以减少子类化).

要使应用程序中的每个选项卡正确地自动旋转,我在自定义视图控制器中定义了-shouldAutorotateToInterfaceOrientation:,但它们都位于UITabBarController的UINavigationControllers中,因此消息不会沿着链条发送到我的VC直到那两个人也回应.因此,我在我的应用程序委托文件中添加了以下几行:

添加到MyAppDelegate.h的底部

@interface UITabBarController (MyApp)
@end

@interface UINavigationController (MyApp)
@end

添加到MyAppDelegate.m的底部

@implementation UITabBarController (MyApp) 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return YES;
}
@end

@implementation UINavigationController (MyApp) 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return YES;
}
@end

UPDATE: See my answer to this question first. This appears to be a bug. A minimal test case has been created and a report has been filed with Apple. (Fixed as of iPhone OS 3.1.)

Here's a puzzler from the "I'm so close!" department.

I have a Tab Bar-based iPhone app. Each tab features a UINavigationController with the usual suspects (nav bar, table view ... which in turn can lead to another VC, etc.).

Now, one of those lower-level VCs is to be used in portait and landscape modes. But there's a problem. Our landscape-friendly VC's shouldAutorotateToInterfaceOrientation: won't get called out-of-the-box! What to do?

Here's what we do. In my Tab Bar Controller, which I have implemented in its own file, I have this:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
     return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}

This ends up propagating the request to my landscape-friendly VC, which also responds to this message. All my other VCs don't implement this method, so they simply go with the default portrait orientation.

Problem solved!!! Yay!

Well, not quite. :(

Seems like things don't go so well when my landscape-friendly VC is invoked from within the depths of the tab bar controller's MoreNavigationController.

I decided to compare/contrast between a VC called from within one of the first four tab bar UINavigationControllers ... and that same VC called from within the MoreNavigationController. This is going to be a bit ultra-detailed, so bear with me. Hopefully the play by play proves useful for sleuthing things out.

When the app loads, there are several initial calls to the tab bar controller's shouldAutorotate... method. In these early cases, selectedViewController is nil. However, we eventually finish loading, the initial tab item is selected, and all is well.

Right. First, let's pick one of the first four tab bar items and drill down to our VC.

We'll pick third nav bar item, so that's the third nav controller. We drill down to our VC that supports rotation. A quick inspection confirms that the parent is indeed the third nav controller from our tab bar's view controller list. Good!

Let's rotate the device. The tab bar controller is asked to autorotate (see the above code). We observe that selectedViewController is also that third nav controller, plus the nav controller's top and visible view controllers are both set to our trusty VC that supports rotation.

Thus, the tab bar controller will forward the shouldAutorotate message over to the third nav controller ... but our rotation-friendly VC ultimately gets the message. (I'm not doing anything special here. Maybe the desired VC gets the message because it's the top and/or visible VC?) In any event, we rotate to landscape, things are resized, and all is well. "Great Success!"

Now let's hit that back button and pop the VC stack, leaving landscape mode in the process. The tab bar controller is queried again.

Time for a little aside here. The topViewController for our nav controller is still that rotation-friendly VC, but visibleViewController is now set to UISnapshotModalViewController! Heh. Never saw this one before ... but Erica Sadun has. Looks like it's for "disappearing view controllers" (which in this case is certainly true - it's disappearing alright).

As I keep stepping through, the visible VC stays as Snapshot, but the top VC eventually changes to the next VC on the stack, since my special VC is eventually gone. Fair enough.

So that's the scenario where everything works well.

Now let's try the same test, only this time we're going to go to the MoreNavigationController (the More tab bar item) and drill down to the same VC class as before. In my case it happens to be the 7th one in the tab bar controller's VC list.

We enter the rotation-aware VC and ... this time it gets asked to rotate directly! The Tab Bar Controller is not asked for permission to rotate at all. Hmm.

A quick check of the parent VC shows it is a MoreNavigationController. OK, that makes sense.

Now let's try to rotate the device. NOTHING GETS CALLED. None of our breakpoints get hit. Not in our VC. Not in our tab bar controller. (Huh?!?!)

O-kaaaay. Let's pop the stack, go back into the same VC and try to rotate again. Weird. NOW we get a call in the Tab Bar Controller asking for autorotation permission. Here, the selected Controller is our trusty Nav controller (#7), but this time its visibleViewController and topViewController are SET TO NIL!

Once we continue from here, a mysterious message appears in the debugger console:

Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.

Mysterious because I'm not using two-stage rotation animation! No SecondHalf method variants are in play anywhere in my source code.

Alas, my rotation-aware VC is never told that rotation is occurring (even though rotation does occur on-screen), so of course my view is all fouled up as a result. Mayhem and sadness ensue. :(

We won't even bother popping the stack at this point.

I think the View Controller doc hints at the possible problem:

If you want to perform custom animations during an orientation change, you can do so in one of two ways. Orientation changes used to occur in two steps, with notifications occurring at the beginning, middle, and end points of the rotation. However, in iPhone OS 3.0, support was added for performing orientation changes in one step. Using a one-step orientation change tends to be faster than the older two-step process and is generally recommended for any new code.

I wonder if MoreNavigationController is still responding to the two-step process, and is thus tripping up any attempts to use the one-step process? Note that, if you respond to the two-step messages, the one-step variant will not work (again, per the docs). I'm not responding to them, but I have a sneaking suspicion something behind-the-scenes IS.

In fact, if I comment out the single-step method, and try to respond to willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration:, I do get the memo! But it still doesn't pop off the stack very cleanly (in terms of visuals). Even stranger: willAnimateFirstHalfOfRotationFromInterfaceOrientation:duration: is NOT called, even when I tried to sneak a call to self (using the FirstHalf message) in shouldAutorotateToInterfaceOrientation:. It returns immediately during a trace, as if I never even defined it. Sigh.

So that's the play-by-play.

In summary, has anyone successfully handled one-step device rotation for a VC invoked from within a Tab Bar Controller's MoreNavigationController? Inquiring minds want to know!

解决方案

Apple advises against subclassing UITabBarController, so I found an easy way to handle autorotation using categories instead. It doesn't fix your bug with the More... view controllers, but I think it's a more Apple-friendly way of getting the job done (and means less subclassing for you).

To make every tab in my application autorotate properly, I've defined -shouldAutorotateToInterfaceOrientation: in my custom view controllers, but they are all inside UINavigationControllers within a UITabBarController, so the message won't get sent down the chain to my VC until those two also respond. So I added the following lines to my app delegate files:

Added to the bottom of MyAppDelegate.h

@interface UITabBarController (MyApp)
@end

@interface UINavigationController (MyApp)
@end

Added to the bottom of MyAppDelegate.m

@implementation UITabBarController (MyApp) 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return YES;
}
@end

@implementation UINavigationController (MyApp) 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return YES;
}
@end

这篇关于UITabBarController,MoreNavigationController和设备旋转的圣杯的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆