UINavigationControllerの子孫であるUIViewControllerにUIWebViewが含まれています。次のようになります。
アプリはポートレートのみです。ビデオを再生するとき、ユーザーがデバイスを回転させて、横向きモードでビデオを見ることができるようにしたいと思います。私はそれを許可するためにこのコードを使用します:
- (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
これを解決する方法について何かアイデアはありますか?
コントローラーの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"];
}
@entropidとまったく同じ問題が発生し、同じコードを使用しました。しかし、受け入れられた解決策は私にはうまくいきませんでした。
次の1行の修正を思い付くのに何時間もかかりました。
- (void)viewWillLayoutSubviews {
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
}
私は昨日この問題に直面しました。@ entropidの回答はiOS9以下では機能しましたが、iOS 10では機能しませんでした(iOS 10は実際にステータスバーを非表示にしていたため、iOS9以下では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
よりもはるかにスムーズに見えるためです。
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)
}
@Stanislav Pankevichと書かれているように、とてもシンプルですが、
Swift3バージョン
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews();
UIApplication.shared.isStatusBarHidden = false
}
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にはテキストが表示されません。他のデバイスでは、テキストは必要ありません。
私は同じ問題を抱えていて、@ 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};
}
}];
私はそれを適切にテストしていませんが、今は私のために働いています