31

У меня есть 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

Но тогда видео сделали игру (или «Done» заходами пользователя) и экраны возврата к основной точке зрения, это то, что происходит:

Navigation bar issue

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

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

+0

Какие настройки в вашем проекте (за пределами кода?) –

+0

@entropid Вы когда-нибудь открыть для этого радар? Еще проблема на iOS 10 ... –

ответ

17

Я временно решил (через взломать) со следующим кодом в контроллере 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"]; 
} 
+0

Вы также можете посмотреть подклассификацию UINavigationBar и переопределить setCenter/setFrame/setBounds. Я сделал это, когда мне нужен был мелкозернистый контроль. –

+0

Я попробую это, но это UINavigationController, поэтому я не знаю, сколько контроля я могу иметь на панели навигации. Более того, изменение высоты вызвано некоторыми странными ограничениями, которые ломаются, поэтому я не слишком уверен в этом. – entropid

+0

Отлично работает! спасибо – AleyRobotics

5

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

меня ушло часа, чтобы придумать следующий однострочного исправления, сделанные вещи, рабочий для меня:

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

Я попробую это скоро, и если это сработает, я приму это как «решение»; это намного лучше, чем мой. – entropid

+0

Да, было бы хорошо знать, работает ли оно на вас. –

+3

Да, это должно быть приемлемым решением. Подтверждено, чтобы работать должным образом. –

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

Мой ответ на этот вопрос великие произведения. Here Он будет воспроизводить видео внутри вашего 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"]]; 
0

У меня была такая же проблема, и с помощью решения @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}; 
    } 
}]; 

Я не проверял это правильно, но его работу для меня прямо сейчас

4

вчера я столкнулся с этой проблемой, где @entropid ответ работал прошивка 9 и ниже, но для прошивки 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, по крайней мере, с точки зрения пользователя.

0

На принятом решении 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 на прошивке 10 и меньше, но фиксирует панель навигации на прошивке 11, так что нет необходимости обновлять кадр (также есть побочный эффект на прошивке 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, для других устройств это не требуется.

1

Это очень просто, как говорит @Stanislav Панкевич, но в

скор 3 версии

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