web-dev-qa-db-ja.com

子View Controllerは回転するが、親View Controllerは回転しない

私がやろうとしていること:

親ビューコントローラーによって管理されている親ビューSHOULD NOT ROTATE

子ビューコントローラーによって管理される子ビューすべての方向に回転(SHOULD ROTATEする必要があります。


enter image description here

enter image description here


私が試したこと:

ParentViewController

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return .Portrait
}

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

fun addChildViewController() {
    let storyBoard = UIStoryboard(name: "Main", bundle: nil)
    self.childViewController = storyBoard("Child View Controller") as? ChildViewController
    self .addChildViewController(self.childViewController!)
    self.childViewController! .didMoveToParentViewController(self)
    self.view .addSubview(self.childViewController!.view)
    self.childViewController!.view.frame = CGRectMake (40, 200, 400, 250)
}

ChildViewController

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return  .All
}

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

Xcode Deployment Infoでサポートされている方向は、4つすべてに設定されています。

得られるもの:

ビューは回転しません。親の回転をすべてに設定すると、すべてのビューが一緒に回転します。つまり、全部かゼロかです。


更新

UIDeviceOrientationDidChangeNotificationのオブザーバーを配置して、UIDevice.currentDevice()。orientationを使用して回転するときchildView CGAffaineTransformMakeRotateを使用すると、望ましい結果が得られます。ただし、横向きに回転し、通知センターを引き下げようとした場合(またはシステム通知を取得した場合)は、引き続き縦向き(アプリは元々縦向きのままであるため)、回転childViewは回転して縦向きに戻り、ステータスバー/通知/通知センターを尊重します。

ストックiOSカメラアプリは、私が提示できる最良の例です。メインキャンバスは異なる方向に回転しませんが、ステータスバーはシーンの背後で、デバイスの回転に合わせて回転します。また、サブビューはそれら自体の中で回転し、さまざまな方向を尊重します。私はこの行動を達成しようとしています...

26
Gizmodo

Appleそれら自体、およびそれらが同じリンクを指している Technical Q&A QA189 を使用して多くの前後に行った後、私は以下の流れの方法でこれを行わなければなりませんでした:

MotherViewController

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

        super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

        coordinator.animateAlongsideTransition({(context: UIViewControllerTransitionCoordinatorContext) -> Void in
            let deltaTransform: CGAffineTransform = coordinator.targetTransform()
            let deltaAngle: CGFloat = atan2(deltaTransform.b, deltaTransform.a)
            var currentRotation: CGFloat = self.mainView.layer.valueForKeyPath("transform.rotation.z") as! CGFloat

            // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
            currentRotation += -1 * deltaAngle + 0.0001
            self.mainView.layer.setValue(currentRotation, forKeyPath: "transform.rotation.z")

            }, completion: {(
                context: UIViewControllerTransitionCoordinatorContext) -> Void in
                // Integralize the transform to undo the extra 0.0001 added to the rotation angle.
                var currentTransform: CGAffineTransform = self.mainView.transform
                currentTransform.a = round(currentTransform.a)
                currentTransform.b = round(currentTransform.b)
                currentTransform.c = round(currentTransform.c)
                currentTransform.d = round(currentTransform.d)
                self.mainView.transform = currentTransform
        })
    }

MainViewController

NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name:UIDeviceOrientationDidChangeNotification, object: nil)

func orientationChanged ( notification: NSNotification){
        switch UIDevice.currentDevice().orientation {
        case .Portrait:
            self.rotateChildViewsForOrientation(0)
        case .PortraitUpsideDown:
            self.rotateChildViewsForOrientation(0)
        case .LandscapeLeft:
            self.rotateChildViewsForOrientation(90)
        case .LandscapeRight:
            self.rotateChildViewsForOrientation(-90)
        default:
            print ("Default")
                }
    }

これは、メインビューを静的に保ちながら子ビューを回転させるようです。また、通知が届くと、アプリは適切な方向を知っているようです(マザービューは実際には舞台裏で回転するため)。

jameskWarif Akhand Rishiに感謝します!

2
Gizmodo

カメラアプリで得られる効果は、次の組み合わせを使用した場合と同様です。

テクニカルノートでは、自動回転の基本的な実装には、アプリケーションのウィンドウに直接変換を適用する必要があると説明されています。

自動回転は、インターフェイスを回転する必要があるとシステムが判断したときに、アプリケーションのウィンドウに回転変換を適用することによって実装されます。次に、ウィンドウは新しい方向に合わせて境界を調整し、この変更を各ビューコントローラーおよびプレゼンテーションコントローラーのviewWillTransitionToSize:withTransitionCoordinator:メソッドの呼び出しを介してビューコントローラー階層全体に伝達します。

カメラアプリは自動回転をオプトアウトしないことに注意してください。カメラアプリを使用する場合、通知センターとコントロールセンターの向きはデバイスの向きを反映します。自動回転をオプトアウトしているように見えるのは、カメラアプリ内の特定のビューのみです。実際、 この答え は、このようなアプリが AVCaptureVideoPreviewLayer や-などのAVFoundationクラスによって提供されるvideoGravityおよびvideoOrientationプロパティを通常使用することを示唆しています AVCaptureConnection

使用するアプローチは、親ビューの回転のみを停止するか、UINavigationControllerなどのコンテナービューコントローラーの回転を停止するか(デバイスの向きをロックするなど)によって異なります。

前者の場合、テクニカルノートで説明されている手法を使用して親ビューの方向を修正し、(iOS 9の場合)UIStackViewで回転サブビューをラップし、axisプロパティを使用してデバイスの回転時にサブビューを再配置します。 、または(iOS 8の場合)デバイスのローテーションでサブビューを手動でローテーションします( this answer および this sample code を参照してください)。

後者の場合、あなたが取っているアプローチは正しい軌道に乗っています。サンプルコードについては、 この答え を参照してください。通知センターに関する問題を診断するには、コードとビューコントローラ階層に関する詳細情報を共有する必要があります。

補遺shouldAutorotateおよびsupportedInterfaceOrientationsへの呼び出しが子View Controllerに伝達されなかった理由は、- テクニカルQ&A QA1688

IOS 6以降、最上位のビューコントローラー(UIApplicationオブジェクトと共に)のみが、デバイスの向きの変化に応じて回転するかどうかを決定します。多くの場合、コンテンツを表示するビューコントローラーは、UINavigationControllerUITabBarControllerの子、またはカスタムコンテナーのビューコントローラーです。 supportedInterfaceOrientationsおよびshouldAutorotateメソッドによって返される値を変更するために、コンテナービューコントローラーをサブクラス化する必要がある場合があります。

Swiftでは、 拡張機能を使用してこれらのメソッドをオーバーライド できます。

9
jamesk

IOS開発者ライブラリから テクニカルQ&A

自動回転は、インターフェイスを回転する必要があるとシステムが判断したときに、アプリケーションのウィンドウに回転変換を適用することによって実装されます。次に、ウィンドウは新しい方向に合わせて境界を調整し、各ビューコントローラーおよびプレゼンテーションコントローラーの-viewWillTransitionToSize:withTransitionCoordinator:メソッドを呼び出して、ビューコントローラー階層全体にこの変更を伝達します。このメソッドの呼び出しにより、ウィンドウの現在の変換と新しい変換のデルタを含む遷移コーディネーターオブジェクトが提供されます。これは、-targetTransformメッセージを遷移コーディネーターに送信することで取得できます。ビューコントローラーまたはプレゼンテーションコントローラーは、適切な逆変換を導出し、それをターゲットビューに適用できます。これにより、その特定のビューに対するウィンドウ変換の効果が無効になり、残りのインターフェイスが正常に回転しても、ビューが現在の方向に固定されたままのように見えます。

つまり、noRotatingViewという名前のビューがある場合

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    noRotatingView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds))
}

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in

        let deltaTransform = coordinator.targetTransform()
        let deltaAngle = atan2f(Float(deltaTransform.b), Float(deltaTransform.a))

        var currentRotation = self.noRotatingView.layer.valueForKeyPath("transform.rotation.z")?.floatValue

        // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
        currentRotation = currentRotation! + (-1 * Float(deltaAngle)) + 0.0001
        self.noRotatingView.layer.setValue(currentRotation, forKeyPath:"transform.rotation.z")

        }) { (UIViewControllerTransitionCoordinatorContext) -> Void in

            var currentTransform = self.noRotatingView.transform;
            currentTransform.a = round(currentTransform.a);
            currentTransform.b = round(currentTransform.b);
            currentTransform.c = round(currentTransform.c);
            currentTransform.d = round(currentTransform.d);
            self.noRotatingView.transform = currentTransform;
    }
}

https://github.com/rishi420/TestCameraRotation でデモプロジェクトを作成しようとしました

2