使用UITabBarController的UIModalTransitionStylePartialCurl

6
这个问题已经被问了很多次,例如这里,但据我所见,还没有完整回答。
我有一个UITabBarController,其中一个选项卡的根视图控制器是UINavigationController,并且该导航控制器的根视图控制器是MKMapView。我想要的行为是地图部分向上翻转,同时保留工具栏(类似于Maps应用程序)。
到目前为止,我只能让整个视图翻转,这不够好看。
我看到的解决方案是将hidesBottomBarWhenPushed属性设置为NO,这很有道理,但是这似乎不起作用(除非我做错了什么)。
为了清晰起见,我的代码如下:
MyVC *aView = [MyVC init];
aView.modalTransitionStyle = UIModalTransitionStylePartialCurl;
aView.hidesBottomBarWhenPushed = NO;

关于展示部分,我尝试了以下两种替代方案,但似乎都不起作用:

[self presentModalViewController:updateStatus animated:YES];
[[self navigationController] presentModalViewController:updateStatus animated:YES];

非常感谢您的帮助。


以上的init方法只是一个静态便捷方法,用于分配等操作。 - danhopwood
2个回答

4
我已经在StackOverflow(和互联网上)搜寻了解决此问题的方法。这个问题已经被问过很多次,但正如您所指出的那样,从未得到充分的回答。许多解决方案可以提供一个可接受的解决方案,但如果不重要,例如一个较低的工具栏也会卷曲起来,那么这些解决方案就无法胜任。 其他人提供了一种解决方案,使用UIView动画/CoreAnimation而不是UIModalTransitionStylePartialCurl作为模态转换样式;最坏的情况是这不是App Store允许的解决方案,而最好的情况则不完全与通过UIModalTransitionStylePartialCurl获得的效果相同(例如,卷曲的形状不同)。
这些解决方案都没有提供类似于Maps应用程序中苹果解决方案的答案(即使用UIModalTransitionStylePartialCurl,但在屏幕底部留下未卷曲的UIToolbar)。
我将继续遵循这种不完整回答的传统,因为你问到了一个UITabBarController,而我的解决方案并没有具体针对这种情况。然而,它确实解决了我的问题,即在页面底部有一个未卷曲的工具栏的半页卷曲问题。
可能有更优雅的方法来做到这一点,但这是我处理的方式。
我的AppDelegate的rootViewController是UIViewController的子类,我将其称为TAContainerViewController。TAContainerViewController管理屏幕的实际内容(要卷曲的“内容”),TAContentViewController,以及TAContentViewController“后面”的内容(例如设置),我将其称为TAUnderCurlViewController。
我的TAContainerViewController实例具有TAContentViewControllerTAUnderCurlViewController的属性。我的内容UIViewTAContentViewControllerview属性的子视图;同样,用户在翻页下方看到的是TAUnderCurlViewControllerview属性。

TAContainerViewControllerinit方法中,我确保执行以下操作:

    _underCurlVC.modalTransitionStyle = UIModalTransitionStylePartialCurl;

为了使内容卷曲以便在页面下方显示,我设置了一个调用以下代码的动作:

    [self.contentVC presentModalViewController:self.underCurlVC animated:YES];`

其中,selfTAContainerViewControllercontentVCTAContentViewController的实例,underCurlVCTAUnderCurlViewController的实例。

要关闭视图,只需简单地使用[self.contentVC dismissModalViewControllerAnimated:YES];

当模态视图被关闭时,contentVC的框架似乎会发生一些奇怪的事情,因此我在模态视图关闭时手动重置了框架。

我在Github上发布了一个带有更多细节的示例项目。希望有人可以将其转化为稍微更优雅的解决方案,或者扩展它以适用于UINavigationControllerUITabBarController。我认为关键是将View Controllers从Cocoa子类中定义良好的关系中分离出来,所以可能需要对那些特殊的View Controllers进行子类化。


1

蒂姆·阿诺德的回答对我非常有帮助,谢谢!

要注意的一个陷阱是:如果您的内容视图控制器被添加为容器视图控制器的子视图,则您的模态翻页过渡将占据整个屏幕。您可以选择不将其添加为子视图,但这样您的内容控制器上的所有视图生命周期方法(例如viewDidLoadviewWillAppear)都将无法调用,这可能会成为一个问题。

幸运的是,有一种解决方法。在您的容器控制器中:

  • viewDidLoad中将您的内容控制器作为子视图添加
  • viewDidAppear中将其作为子视图移除
  • viewWillDisappear中重新将其作为子视图添加。

这样,您的内容控制器就可以调用其生命周期方法,同时仍能进行模态翻页过渡,而不会占据整个屏幕。

下面是一个简单解决方案的完整代码:

@interface XXContainerController : UIViewController
@property (strong, nonatomic) UIViewController *contentController;
@property (nonatomic) BOOL curled;
@end

@implementation XXContainerController

@synthesize contentController = _contentController;
@synthesize curled = _curled;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.contentController = [self.storyboard
        instantiateViewControllerWithIdentifier:@"SomeControllerInStoryboard"];

    // Add content controller as child view controller.
    // This way, it will receive all the view lifecycle events
    [self addChildViewController:self.contentController];
    self.contentController.view.frame = self.view.bounds;
    [self.view addSubview:self.contentController.view];
    [self.contentController didMoveToParentViewController:self];    
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    // Remove the content controller as child view controller.
    // This way, the modal page curl transition will
    // not take over the whole screen.
    // NOTE: need to wait until content controller has appeared
    // (which will happen later).
    // Achieve this by running the code at the end of the animation loop
    [UIView animateWithDuration:0 animations:nil completion:^(BOOL finished) {
        [self.contentController removeFromParentViewController];
    }];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    // Add the content controller as child view controller again
    // so it receives the view lifecycle events
    [self addChildViewController:self.contentController];
}

- (void)setCurled:(BOOL)curled
{
    if (curled == _curled) return;

    _curled = curled;

    // Curl up the content view and show underneath controller's view
    if (curled) {
        // Note you can specify any modal transition in storyboard
        // E.g. page curl, flip horizontal
        [self.contentController
            performSegueWithIdentifier:@"SomeModalSegueDefinedInStoryboard"
            sender:self];

    // Uncurl and show the content controller's view again
    } else {
        [self.contentController dismissModalViewControllerAnimated:YES];

        // Have to do this, otherwise the content controller's view
        // gets messed up for some reason 
        self.contentController.view.frame = self.view.bounds;
    }
}

@end

谢谢您的详细说明!我的解决方案有一个不完整的方面,就是它没有使用正确的视图控制器容器API。 - Tim Arnold

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接