首页 文章

iOS 7关闭模态视图控制器和强制纵向方向

提问于
浏览
13

我有一个UINavigationController作为我在iOS 7和iOS 8上的UIWindow的根视图控制器 . 从它的一个视图控制器,我提出了一个具有交叉溶解演示风格的全屏模态视图控制器 . 这个模态视图控制器应该能够旋转到所有方向,并且它工作正常 .

问题是当设备以横向方向保持并且模态视图控制器被解除时 . 呈现模态的视图控制器仅支持纵向方向,并且我已确认UIInterfaceOrientationMaskPortrait返回到-application:supportedInterfaceOrientationsForWindow: . -shouldAutorotate也返回YES . 然而,在解除模态之后,呈现视图控制器的方向仍然是风景 . 在允许模态采取设备方向的同时,如何强制它保持纵向方向?我的代码如下:

应用代表:

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        UINavigationController *navigationController = (UINavigationController *)self.deckController.centerController;
        NSArray *viewControllers = [navigationController viewControllers];
        UIViewController *top = [viewControllers lastObject];

        if (top && [top presentedViewController]) {
            UIViewController *presented = [top presentedViewController];
            if ([presented respondsToSelector:@selector(isDismissing)] && ![(id)presented isDismissing]) {
                top = presented;
            }
        }

        return [top supportedInterfaceOrientations];
    }

    return (UIInterfaceOrientationMaskLandscapeLeft|UIInterfaceOrientationMaskLandscapeRight);
}

呈现视图控制器:

- (BOOL)shouldAutorotate {
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationPortrait;
}

模态视图控制器:

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    return (UIInterfaceOrientationMaskLandscape|UIInterfaceOrientationMaskLandscapeLeft|UIInterfaceOrientationMaskPortrait);
}

3 回答

  • 5

    如果模态控制器在解除之前处于横向方向,则呈现的ViewController可能不会返回到原点方向(纵向) . 问题是因为在控制器实际被解除之前调用了AppDelegate supportedInterfaceOrientationsForWindow方法,并且呈现的控制器检查仍然返回Landscape掩码 .

    设置一个标志以指示是否显示(模态) presented 视图控制器 .

    - (void)awakeFromNib // or where you instantiate your ViewController from
    {
        [super awakeFromNib];
        self.presented = YES;
    }
    
    - (IBAction)exitAction:(id)sender // where you dismiss the modal
    {
        self.presented = NO;
        [self dismissViewControllerAnimated:NO completion:nil];
    }
    

    并且在提供的模态中,ViewController根据标志设置方向:当呈现模态ViewController时 - 返回Landscape . 当它被解雇然后返回肖像

    - (NSUInteger)supportedInterfaceOrientations
    {
        if ([self isPresented]) {
            return UIInterfaceOrientationMaskLandscape;
        } else {
            return UIInterfaceOrientationMaskPortrait;
        }
    }
    

    最后一步 - 从你的AppDelegate调用模态呈现ViewController的方向 . 我只是检查当前呈现的ViewController并在其上调用supportedInterfaceOrientations

    - (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
    {
        NSUInteger orientationMask = UIInterfaceOrientationMaskPortrait;
    
        UIViewController *currentVC = self.window.rootViewController.presentedViewController; // gets the presented VC
        orientationMask = [currentVC supportedInterfaceOrientations];
    
        return orientationMask;
    }
    

    有关更多信息,请查看this link

  • 1

    我最终继承了UINavigationController并重写了它的旋转方法 . 以下解决方案适用于iOS 7,但我相信iOS 8 beta 5中存在一个错误,导致视图控制器的视图在横向偏移模式后缩小到屏幕高度的一半 .

    UINavigationController子类:

    - (BOOL)shouldAutorotate
    {
        return NO;
    }
    
    - (NSUInteger)supportedInterfaceOrientations
    {
        return UIInterfaceOrientationMaskPortrait;
    }
    
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
    {
        return UIInterfaceOrientationPortrait;
    }
    
  • 1

    此解决方案适用于iOS 8 .


    问题描述

    • 应用程序密钥窗口将UINavigationController的子类作为其rootViewController .

    • 此NC子类禁止某些接口方向 .

    • NC堆栈中的某些视图控制器(VC1)以模态和全屏方式呈现另一个视图控制器(VC2) .

    • 这个呈现的VC2允许比NC更多的接口方向 .

    • 用户将设备旋转到NC禁止的方向,但由提供的VC2允许 .

    • 用户驳回提供的VC2 .

    • VC1视图的帧不正确 .


    设置和插图

    UINavigationController的子类:

    - (NSUInteger)supportedInterfaceOrientations
    {
        return UIInterfaceOrientationMaskPortrait;
    }
    
    - (BOOL)shouldAutorotate
    {
        return YES;
    }
    

    VC1初始外观和UI视图堆栈:

    Initial appearance

    从VC1呈现VC2(该示例中的QLPreviewController):

    QLPreviewController *pc = [[QLPreviewController alloc] init];
    pc.dataSource = self;
    pc.delegate = self;
    pc.modalPresentationStyle = UIModalPresentationFullScreen;
    [self.navigationController presentViewController:pc animated:YES completion:nil];
    

    显示VC2并将设备旋转为横向:

    Presented and rotated

    VC2被解雇,设备返回纵向模式,但NC堆栈保持在横向状态:

    VC2 dismissed


    原因

    Apple文档说明:

    使用presentViewController:animated:completion:方法呈现视图控制器时,UIKit始终管理演示过程 . 该过程的一部分涉及创建适合给定演示样式的演示控制器 .

    显然处理UINavigationController堆栈时存在一个错误 .


    解决方案

    通过提供我们自己的转换委托可以绕过此错误 .

    BTTransitioningDelegate.h

    #import <UIKit/UIKit.h>
    
    @interface BTTransitioningDelegate : NSObject <UIViewControllerTransitioningDelegate>
    
    @end
    

    BTTransitioningDelegate.m

    #import "BTTransitioningDelegate.h"
    
    static NSTimeInterval kDuration = 0.5;
    
    // This class handles presentation phase.
    @interface BTPresentedAC : NSObject <UIViewControllerAnimatedTransitioning>
    
    @end
    
    @implementation BTPresentedAC
    
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        return kDuration;
    }
    
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)context
    {
        // presented VC
        UIViewController *toVC = [context viewControllerForKey:UITransitionContextToViewControllerKey];
    
        // presented controller ought to be fullscreen
        CGRect frame = [[[UIApplication sharedApplication] keyWindow] bounds];
        // we will slide view of the presended VC from the bottom of the screen,
        // so here we set the initial frame
        toVC.view.frame = CGRectMake(frame.origin.x, frame.origin.y + frame.size.height, frame.size.width, frame.size.height);
    
        // [context containerView] acts as the superview for the views involved in the transition
        [[context containerView] addSubview:toVC.view];
    
        UIViewAnimationOptions options = (UIViewAnimationOptionCurveEaseOut);
    
        [UIView animateWithDuration:kDuration delay:0 options:options animations:^{
            // slide view to position
            toVC.view.frame = frame;
        } completion:^(BOOL finished) {
            // required to notify the system that the transition animation is done
            [context completeTransition:finished];
        }];
    }
    
    @end
    
    
    // This class handles dismission phase.
    @interface BTDismissedAC : NSObject <UIViewControllerAnimatedTransitioning>
    
    @end
    
    @implementation BTDismissedAC
    
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        return kDuration;
    }
    
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)context
    {
        // presented VC
        UIViewController *fromVC = [context viewControllerForKey:UITransitionContextFromViewControllerKey];
        // presenting VC
        UIViewController *toVC = [context viewControllerForKey:UITransitionContextToViewControllerKey];
    
        // inserting presenting VC's view under presented VC's view
        toVC.view.frame = [[[UIApplication sharedApplication] keyWindow] bounds];
        [[context containerView] insertSubview:toVC.view belowSubview:fromVC.view];
    
        // current frame and transform of presented VC
        CGRect frame = fromVC.view.frame;
        CGAffineTransform transform = fromVC.view.transform;
    
        // determine current presented VC's view rotation and assemble
        // target frame to provide naturally-looking dismissal animation
        if (transform.b == -1) {
            // -pi/2
            frame = CGRectMake(frame.origin.x + frame.size.width, frame.origin.y, frame.size.width, frame.size.height);
        } else if (transform.b == 1) {
            // pi/2
            frame = CGRectMake(frame.origin.x - frame.size.width, frame.origin.y, frame.size.width, frame.size.height);
        } else if (transform.a == -1) {
            // pi
            frame = CGRectMake(frame.origin.x, frame.origin.y - frame.size.height, frame.size.width, frame.size.height);
        } else {
            // 0
            frame = CGRectMake(frame.origin.x, frame.origin.y + frame.size.height, frame.size.width, frame.size.height);
        }
    
        UIViewAnimationOptions options = (UIViewAnimationOptionCurveEaseOut);
    
        [UIView animateWithDuration:kDuration delay:0 options:options animations:^{
            // slide view off-screen
            fromVC.view.frame = frame;
        } completion:^(BOOL finished) {
            // required to notify the system that the transition animation is done
            [context completeTransition:finished];
        }];
    }
    
    @end
    
    
    @implementation BTTransitioningDelegate
    
    - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
    {
        return [[BTPresentedAC alloc] init];
    }
    
    - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
    {
        return [[BTDismissedAC alloc] init];
    }
    
    @end
    

    在呈现VC时导入转换委托:

    #import "BTTransitioningDelegate.h"
    

    存储对实例的强引用:

    @property (nonatomic, strong) BTTransitioningDelegate *transitioningDelegate;
    

    在_888168中实例化:

    self.transitioningDelegate = [[BTTransitioningDelegate alloc] init];
    

    适当时打电话:

    QLPreviewController *pc = [[QLPreviewController alloc] init];
    pc.dataSource = self;
    pc.delegate = self;
    pc.transitioningDelegate = self.transitioningDelegate;
    pc.modalPresentationStyle = UIModalPresentationFullScreen;
    
    [self.navigationController presentViewController:pc animated:YES completion:nil];
    

相关问题