【问题标题】:Allow video on landscape with only-portrait app允许使用仅限纵向应用的横向视频
【发布时间】:2014-12-23 07:08:53
【问题描述】:

我有一个 UIWebView 包含在 UIViewController 中,它是 UINavigationController 的后代。它看起来像这样:

该应用程序仅是纵向的。当我播放视频时,我希望用户能够旋转设备并以横向模式观看视频。我使用此代码来允许它:

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    id presentedViewController = [self topMostController];
    NSString *className = presentedViewController ? NSStringFromClass([presentedViewController class]) : nil;

    if ([className isEqualToString:@"MPInlineVideoFullscreenViewController"] ||
        [className isEqualToString:@"MPMoviePlayerViewController"] ||
        [className isEqualToString:@"AVFullScreenViewController"]) {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    }

    return UIInterfaceOrientationMaskPortrait;
}

- (UIViewController *)topMostController {
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }

    return topController;
}

然后在我的 UINavigationController 中(所以当视频结束时,视图不会以横向呈现,而只会以纵向呈现):

- (BOOL)shouldAutorotate
{
    return NO;
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationPortrait;
}

一切正常:

但随后视频播放完毕(或用户点击“完成”)并且屏幕返回到底层视图,这就是发生的情况:

如您所见,导航栏滑到状态栏下方。 此外,我在日志中收到很多自动布局错误:http://pastebin.com/09xHzmgJ

知道如何解决这个问题吗?

【问题讨论】:

  • 你的项目中有哪些设置(代码之外?)
  • @entropid 你有没有为此打开过雷达?在 iOS 10 上仍然是一个问题...

标签: ios objective-c uiwebview uinavigationcontroller screen-rotation


【解决方案1】:

我在控制器的viewDidLoad 中使用以下代码暂时解决了(通过hack)。我必须指定代码是专门针对我的情况编写的:因为我明确禁止 UINavigationController 的横向(参见上面的代码),所以当播放完成并且窗口返回纵向时,不会调用通常的通知“UIDeviceOrientationDidChange”。但是,我希望有更好的选择,这是 SDK 的一个错误,因为它没有出现在 iOS 7 上,并且考虑到与视频播放器相关的自动布局错误的数量(我无法控制) .

- (void)viewDidLoad
{
    [super viewDidLoad];

    // […]

     /* 
     Hack to fix navigation bar position/height on iOS 8 after closing fullscreen video

     Observe for “UIWindowDidRotateNotification” since “UIDeviceOrientationDidChangeNotification” is not called in the present conditions
     Check if the notification key (“UIWindowOldOrientationUserInfoKey”) in userInfo is either 3 or 4, which means the old orientation was landscape
     If so, correct the frame of the navigation bar to the proper size.

     */
    [[NSNotificationCenter defaultCenter] addObserverForName:@"UIWindowDidRotateNotification" object:nil queue:nil usingBlock:^(NSNotification *note) {
        if ([note.userInfo[@"UIWindowOldOrientationUserInfoKey"] intValue] >= 3) {
            self.navigationController.navigationBar.frame = (CGRect){0, 0, self.view.frame.size.width, 64};
        }
    }];
}

然后……

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:@"UIWindowDidRotateNotification"];
}

【讨论】:

  • 您还可以查看子类化 UINavigationBar 并覆盖 setCenter/setFrame/setBounds。当我需要细粒度控制时,我就这样做了。
  • 我会尝试,但它是一个 UINavigationController,所以我不知道我可以对导航栏进行多少控制。此外,高度的变化是由一些奇怪的约束引起的,所以我不太确定是否能接受它。
【解决方案2】:

我遇到了完全相同的问题,并使用了与 @entropid 相同的代码。但是,接受的解决方案对我不起作用。

我花了几个小时才想出以下让我工作正常的单行修复:

- (void)viewWillLayoutSubviews {
    [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
}

【讨论】:

  • 我会尽快尝试,如果可行,我会接受它作为“解决方案”;这比我的要好得多。
  • 是的,这应该是公认的解决方案。确认可以正常工作。
  • 你是个天才。像魅力一样工作。
【解决方案3】:

Swift 版本:

//AppDelegate:
    func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {

        var presentedVC = application.keyWindow?.rootViewController
        while let pVC = presentedVC?.presentedViewController
        {
            presentedVC = pVC
        }
        if let pVC = presentedVC
        {
            if contains(["MPInlineVideoFullscreenViewController", "MPMoviePlayerViewController", "AVFullScreenViewController"], pVC.nameOfClass)
            {
                return Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue)
            }
        }

        return Int(UIInterfaceOrientationMask.Portrait.rawValue)
    }

//Extension:
public extension NSObject{
    public class var nameOfClass: String{
        return NSStringFromClass(self).componentsSeparatedByString(".").last!
    }

    public var nameOfClass: String{
        return NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
    }
}

//View controller:
    override func supportedInterfaceOrientations() -> Int {
        return Int(UIInterfaceOrientationMask.Portrait.rawValue)
    }

    override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
        return UIInterfaceOrientation.Portrait
    }

    override func shouldAutorotate() -> Bool {
        return false
    }

    override func viewWillLayoutSubviews() {
        UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.None)
    }

【讨论】:

    【解决方案4】:

    我昨天遇到了这个问题,@entropid 答案适用于 iOS 9 及更低版本,但对于 iOS 10 它没有(因为 iOS 10 确实隐藏了状态栏,在 iOS 9 及更低版本中它只是 @ 987654321@ 改变了它的框架但没有隐藏状态栏,因此它与那个栏重叠)。

    另外,订阅MPMoviePlayerControllerDidExitFullScreen 通知也不起作用,有时它根本没有被调用(在我的特定情况下,这是因为它是来自UIWebView 的视频,它使用了不同级别的播放器类似于MPMoviePlayerController)。

    所以我使用@StanislavPankevich 建议的解决方案解决了这个问题,但是当UIWindow 被隐藏时我订阅了通知(这可能在多种情况下,比如视频结束时,也可能是@ 987654326@ 解雇和其他情况)而不是viewWillLayoutSubviews。对于我的特殊情况(UINavigationController 的子类),根本没有调用 viewDidAppearviewWillAppear 等方法。

    viewDidLoad

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(videoDidExitFullscreen:)
                                                     name:UIWindowDidBecomeHiddenNotification
                                                   object:nil];
    
        // Some other logic...
    }
    

    dealloc

    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    

    最后,videoDidExitFullscreen

    - (void)videoDidExitFullscreen:(NSNotification *)notification {
        // You would want to check here if the window dismissed was actually a video one or not.
    
        [[UIApplication sharedApplication] setStatusBarHidden:NO
                                                withAnimation:UIStatusBarAnimationFade];
    
    }
    

    我使用了UIStatusBarAnimationFade,因为它看起来比UIStatusBarAnimationNone 流畅很多,至少从用户的角度来看是这样。

    【讨论】:

      【解决方案5】:

      正如@Stanislav Pankevich 所说,这很简单,但是在

      swift 3 版本

      override func viewWillLayoutSubviews() {
          super.viewWillLayoutSubviews();
          UIApplication.shared.isStatusBarHidden = false
      }
      

      【讨论】:

        【解决方案6】:

        我遇到了同样的问题,使用 @entropid 解决方案我能够解决导航栏问题。但是我的视图仍然在我使用“sizeToFit”修复的导航栏下方。

        [[NSNotificationCenter defaultCenter] addObserverForName:@"UIWindowDidRotateNotification" object:nil queue:nil usingBlock:^(NSNotification *note) {
            if ([note.userInfo[@"UIWindowOldOrientationUserInfoKey"] intValue] >= 3) {
                [self.navigationController.navigationBar sizeToFit];
                self.navigationController.navigationBar.frame = (CGRect){0, 0, self.view.frame.size.width, 64};
            }
        }];
        

        我没有正确测试它,但它现在对我有用

        【讨论】:

          【解决方案7】:

          在 iOS 11 上接受的解决方案对我不起作用。似乎导航栏停止反映帧更改。但是有一个解决方法。首先,我们需要修改supportedInterfaceOrientationsForWindow 方法,为视频控制器返回UIInterfaceOrientationMaskLandscape 而不是UIInterfaceOrientationMaskAllButUpsideDown。在我的情况下,当我点击嵌入的 YouTube 视频时,系统总是打开 AVFullScreenViewController,所以我从原始示例中删除了其他检查。代码:

          - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
              __kindof UIViewController *presentedViewController = [self topMostController];
          
              // Allow rotate videos
              NSString *className = presentedViewController ? NSStringFromClass([presentedViewController class]) : nil;
              if ([className isEqualToString:@"AVFullScreenViewController"]) {
                  return UIInterfaceOrientationMaskLandscape;
              }
          
              return UIInterfaceOrientationMaskPortrait;
          }
          

          这并没有改变 AVFullScreenViewController 在 iOS 10 及更低版本上的行为,但修复了 iOS 11 上的导航栏,因此无需更新帧(iOS 11 上还有一个副作用,即视频从横向旋转时开始播放,但这是一个权衡)。接下来,我们需要在UIWindowDidBecomeHiddenNotification方法中添加check:

          - (void)videoDidExitFullscreen {
              if (@available(iOS 11, *)) {
                  // Fixes status bar on iPhone X
                  [self setNeedsStatusBarAppearanceUpdate];
              } else {
                  self.navigationController.navigationBar.frame = CGRectMake(0, 0, self.view.bounds.size.width, statusAndNavBarHeight);
              }
          }
          

          如果没有setNeedsStatusBarAppearanceUpdate,状态栏中的文本将不会出现在 iPhone X 上,对于其他设备则不需要。

          【讨论】:

            【解决方案8】:

            我对这个问题的回答很好。 Here 它会在你的 WebView 中正常播放视频,但如果你倾斜手机,它会在横向播放!

            另外需要注意的是,如果您将 youtube.com 作为基本 URL,它将加载得更快。

            在你的故事板中创建一个 UIWebView 并将@property 连接到它,然后参考下面。

                CGFloat width = self.webView.frame.size.height;
                CGFloat height = self.webView.frame.size.width;
                NSString *youTubeVideoCode = @"dQw4w9WgXcQ";
                NSString *embedHTML = @"<iframe width=\"%f\" height=\"%f\" src=\"http://www.youtube.com/embed/%@\" frameborder=\"0\" style=\"margin:-8px;padding:0;\" allowfullscreen></iframe>";
                NSString *html = [NSString stringWithFormat:embedHTML, width, height, youTubeVideoCode];
                self.webView.scrollView.bounces = NO;
                [self.webView loadHTMLString:html baseURL:[NSURL URLWithString:@"http://www.youtube.com"]];
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-03-08
              • 1970-01-01
              • 2013-06-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-04-02
              相关资源
              最近更新 更多