【问题标题】:iOS 7+ Dismiss Modal View Controller and Force Portrait OrientationiOS 7+ 关闭模态视图控制器并强制纵向
【发布时间】:2014-10-13 00:49:44
【问题描述】:

我有一个 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);
}

【问题讨论】:

标签: ios objective-c iphone rotation screen-orientation


【解决方案1】:

我最终继承了 UINavigationController 并覆盖了它的旋转方法。以下解决方案适用于 iOS 7,但我认为 iOS 8 beta 5 中存在一个错误,导致呈现视图控制器的视图在关闭横向模式后缩小到屏幕高度的一半。

UINavigationController 子类:

- (BOOL)shouldAutorotate
{
    return NO;
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationPortrait;
}

【讨论】:

  • @TomerPeled,是的
【解决方案2】:

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

设置一个标志来指示(模态)呈现的视图控制器是否将被显示。

- (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

【讨论】:

  • 完美解决方案。 (y)
【解决方案3】:

此解决方案适用于 iOS 8+。


问题描述

  1. 应用程序键窗口将 UINavigationController 的子类作为其 rootViewController。
  2. 此 NC 子类禁止某些接口方向。
  3. NC 堆栈中的某些视图控制器 (VC1) 正在模态和全屏显示另一个视图控制器 (VC2)。
  4. 这个展示的 VC2 允许比 NC 更多的界面方向。
  5. 用户将设备旋转到 NC 禁止但提供的 VC2 允许的方向。
  6. 用户关闭显示的 VC2。
  7. VC1 视图的帧不正确。

设置和插图

UINavigationController 的子类:

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

- (BOOL)shouldAutorotate
{
    return YES;
}

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

从 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 出现并且设备旋转到横向:

VC2 已关闭,设备返回纵向模式,但 NC 堆栈仍处于横向模式:


原因

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;

-viewDidLoad中实例化:

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];

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-01
    相关资源
    最近更新 更多