Подтвердить что ты не робот

Разрешить видео с пейзажами только с портретом

У меня есть UIWebView, включенный в UIViewController, который является потомком UINavigationController. Это выглядит так:

Main view

Приложение - только портрет. Когда я воспроизвожу видео, я хочу, чтобы пользователь мог повернуть устройство и посмотреть видео в альбомном режиме. Я использую этот код, чтобы разрешить это:

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

Все работает отлично:

Video portraitVideo landscape

Но потом видео воспроизводится (или пользовательские отступы "Готово" ), и экраны возвращаются к базовому виду, вот что происходит:

Navigation bar issue

Как вы можете видеть, панель навигации скользит под строкой состояния. Кроме того, в журналах я получаю много ошибок автоматического макета: http://pastebin.com/09xHzmgJ

Любая идея о том, как решить эту проблему?

4b9b3361

Ответ 1

Я временно решил (через хак) следующий код в viewDidLoad моего контроллера. Я должен указать, что код специально создан для моего случая: поскольку я явно запрещаю ландшафтную ориентацию моего 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"];
}

Ответ 2

Я столкнулся точно с той же проблемой и использовал тот же код, что и @entropid. Однако принятое решение не сработало для меня.

Мне потребовалось несколько часов, чтобы придумать следующее однострочное исправление, которое заставило меня работать:

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

Ответ 3

Я столкнулся с этой проблемой вчера, когда ответ @entropid работал для iOS 9 и ниже, но для iOS 10 это не так (поскольку iOS 10 действительно скрывал строку состояния, где на iOS 9 и ниже она была просто UINavigationBar, который изменил свой кадр, не скрывая строку состояния, и, таким образом, он перекрыл эту полосу).

Кроме того, подписка на уведомление MPMoviePlayerControllerDidExitFullScreen тоже не работала, иногда ее просто не вызывали (в моем конкретном случае это было потому, что это было видео с UIWebView, в котором использовался другой класс игрока который похож на MPMoviePlayerController).

Поэтому я решил это, используя решение, подобное предложению @StanislavPankevich, но я подписался на уведомления, когда UIWindow стал скрытым (что может быть в нескольких случаях, например, когда видео закончилось, а также когда UIActivityViewController отклонения и другие случаи) вместо viewWillLayoutSubviews. Для моего частного случая (подкласс UINavigationController) методы, такие как viewDidAppear, viewWillAppear и т.д., Просто не вызывались.

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, по крайней мере, с точки зрения пользователя.

Ответ 4

Быстрая версия:

//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)
    }

Ответ 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:

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

Мой ответ на этот вопрос отлично работает. Здесь Он будет воспроизводить видео внутри вашего WebView, но если вы наклоняете свой телефон, он будет играть в Landscape!

Также важно отметить, что если вы включите 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"]];