web-dev-qa-db-ja.com

ポートレートアプリのみで横向きの動画を許可する

UINavigationControllerの子孫であるUIViewControllerにUIWebViewが含まれています。次のようになります。

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

これを解決する方法について何かアイデアはありますか?

32
entropid

コントローラーの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"];
}
17
entropid

@entropidとまったく同じ問題が発生し、同じコードを使用しました。しかし、受け入れられた解決策は私にはうまくいきませんでした。

次の1行の修正を思い付くのに何時間もかかりました。

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

私は昨日この問題に直面しました。@ entropidの回答はiOS9以下では機能しましたが、iOS 10では機能しませんでした(iOS 10は実際にステータスバーを非表示にしていたため、iOS9以下ではUINavigationBarだけが変更されました。ステータスバーを非表示にせずにフレームを作成したため、そのバーとオーバーラップしました)。

また、MPMoviePlayerControllerDidExitFullScreen通知のサブスクライブも機能せず、単に呼び出されないこともありました(私の特定のケースでは、UIWebViewに似た別のクラスのプレーヤーを使用したMPMoviePlayerControllerからのビデオであったためです)。

そこで、@ StanislavPankevichが提案したような解決策を使用して解決しましたが、UIWindowが非表示になったときの通知をサブスクライブしました(これは、ビデオが終了したときなど、いくつかの場合がありますが、UIActivityViewControllerが却下されたときなど) 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よりもはるかにスムーズに見えるためです。

4
Alejandro Iván

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)
    }
3
rshev

@Stanislav Pankevichと書かれているように、とてもシンプルですが、

Swift3バージョン

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

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

これはiOS10以下のAVFullScreenViewControllerの動作を変更しませんでしたが、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にはテキストが表示されません。他のデバイスでは、テキストは必要ありません。

0
nemissm

私は同じ問題を抱えていて、@ 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};
    }
}];

私はそれを適切にテストしていませんが、今は私のために働いています

0
Usman Ahmad