向きの変化を検出するには?


148

私はSwiftを使用していて、横向きに回転したときにUIViewControllerをロードできるようにしたいのですが、誰かが私を正しい方向に向けることができますか?

オンラインでは何も見つからず、ドキュメントに少し混乱しています。


1
APIは変更されていないので、「didRotateToOrientation」と「willRotateToOrientation」のようになっているはずです。Appleのドキュメントを参照してください
David 'mArm' Ansermot

1
こんにちは@ mArm.ch、迅速な返信ありがとうございます!それで、これをどのように実装しますか?(これは私の最初のアプリです...私はIOSに非常に新しいです):)
David

他の人のために、回答として再投稿しました。よろしければそれを受け入れてもらえますか?
David 'mArm' Ansermot

回答:


194

これが私がそれを機能させた方法です:

AppDelegate.swift内部のdidFinishLaunchingWithOptions 機能私は置きます:

NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)

次に、AppDelegateクラス内に次の関数を配置します。

func rotated() {
    if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) {
        print("Landscape")
    }

    if UIDeviceOrientationIsPortrait(UIDevice.current.orientation) {
        print("Portrait")
    }
}

これが他の誰にも役立つことを願っています!

ありがとう!


5
私はAppDelegateにaddObserverを追加しようとしましたが、認識されないセレクターを使用してCoreFoundationでSIGABRTを取得し続けました。ただし、最初のビューでaddObserverをviewDidLoadに移動すると、完全に機能しました。誰かが同じ問題に遭遇した場合の情報のみ。
FractalDoctor 2014年

1
コーディングは初めてselectorですが、文字列形式にするべきではありません"rotated:"か?
カメレオン

4
私はあなたが引数を受け入れている場合にのみそれを確信してrotated()います(受け入れません)
David

26
はとUIDeviceOrientationは異なるので注意してくださいUIInterfaceOrientation。これはUIDeviceOrientation、デバイスがほぼ平らであるがわずかにでこぼこのある表面(つまり、突出部で揺れる)に置かれている場合、コードが縦向きと横向きをランダムにジャンプすることを意味します。 6 / 6sのカメラ)
liamnichols

4
電話が自動回転を無効にしている場合、この方法は機能しません。
チェンサム

178
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    }
    if UIDevice.current.orientation.isFlat {
        print("Flat")
    } else {
        print("Portrait")
    }
}

71
これはローテーションの前に呼び出されます。たとえば、回転後のフレームサイズが必要な場合、このソリューションは機能しません。
15年

拡張では機能しませんでした。横長または縦長が「portraight」を印刷する
TomSawyer

4
電話が自動回転を無効にしている場合、この方法は機能しません。
チェンサム

5
iPadでは、サイズクラスは横長と縦長で同じレギュラーであるため、このメソッドが呼び出されることはありません。
ジャッキー、

これは、デバイスがisFlatを返す場合にも失敗します。developer.apple.com/documentation/uikit/uideviceorientation/...
CodeBender

57

でカメラを使用しているときに回転を検出する必要AVFoundationがあり、didRotate現在は非推奨)&willTransitionメソッドは私のニーズに信頼できないことがわかりました。Davidによって投稿された通知を使用することは機能しましたが、Swift 3.x / 4.xの最新ではありません。

Swift 4.2 通知名が変更されました。

クロージャ値はSwift 4.0と同じままです。

var didRotate: (Notification) -> Void = { notification in
        switch UIDevice.current.orientation {
        case .landscapeLeft, .landscapeRight:
            print("landscape")
        case .portrait, .portraitUpsideDown:
            print("Portrait")
        default:
            print("other")
        }
    }

Swift 4.2の通知を設定するには

NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification,
                                       object: nil,
                                       queue: .main,
                                       using: didRotate)

Swift 4.2の通知を破棄するには

NotificationCenter.default.removeObserver(self,
                                          name: UIDevice.orientationDidChangeNotification,
                                          object: nil)

廃止に関する声明に関して、私の最初のコメントは誤解を招くものでしたので、それを更新したいと思いました。前述のように、@objc推論の使用は非推奨になりました。これは、を使用するために必要でした#selector。代わりにクロージャーを使用することにより、これを回避でき、無効なセレクターを呼び出すことによるクラッシュを回避するソリューションが得られます。

以下のすべてはXCode 10およびiOS 4.2で廃止されました

Swift 4.0 Swift 4.0では、Appleはを使用しないことを推奨しているため#selector、このアプローチでは現在完了ブロックを使用しています。このアプローチはSwift 3.xとの下位互換性もあり、今後推奨されるアプローチです。

これは、推論#selectorの非推奨のために関数を使用した場合にSwift 4.xプロジェクトで受け取るコンパイラの警告です@objc

ここに画像の説明を入力してください

この変更に関する迅速な進化のエントリ

コールバックを設定します。

// If you do not use the notification var in your callback, 
// you can safely replace it with _
    var didRotate: (Notification) -> Void = { notification in
        switch UIDevice.current.orientation {
        case .landscapeLeft, .landscapeRight:
            print("landscape")
        case .portrait, .portraitUpsideDown:
            print("Portrait")
        default:
            print("other")
        }
    }

通知を設定します。

NotificationCenter.default.addObserver(forName: .UIDeviceOrientationDidChange,
                                       object: nil,
                                       queue: .main,
                                       using: didRotate)

それを取り壊します:

NotificationCenter.default.removeObserver(self, name: .UIDeviceOrientationDidChange, object: nil)

4
Swift 4で#selectorの使用を推奨しないことについてAppleが話していることについて言及がありますか?彼らがなぜこれを言っているのかについて読みたいと思います。
jeffjv 2017年

@jeffjv確かに、Appleドキュメントへの直接リンクはありませんが、以前のアプローチを使用した場合にXCodeが提供するコンパイラ警告のスクリーンショットを含めました。
CodeBender 2017年

1
変更を説明するSwift-evolutionへのリンクを追加しました。
CodeBender 2018年

1
@CodeBender:コンパイラの警告は、あなたが提案することを意味しません。#selectorは非推奨ではなく、「@ objc」推論のみです。これは、#selectorとして関数を使用する場合、明示的にマークを付ける必要があることを意味します。これにより、コンパイラーはユーザーの使用法から関数を推測しなくなるため、追加の正しいコードを生成します。したがって、Swift 3.0ソリューションのrotate()funcに「@obj」を追加すると、コードは警告なしでコンパイルされます。
ミスランディア2018

@Mythlandiaに感謝します。最初の発言の混乱を解決するために回答を更新しました。
CodeBender 2018

19

-orientationプロパティの使用UIDeviceは正しくなく(ほとんどの場合に機能する可能性があります)、いくつかのバグにつながる可能性があります。たとえばUIDeviceOrientation、デバイスが上向きまたは下向きの場合、デバイスの向きも考慮します。UIInterfaceOrientationそれらの列挙型には直接のペアはありません。値。
さらに、特定の向きでアプリをロックすると、UIDeviceはそれを考慮せずにデバイスの向きを提供します。
一方、iOS8はクラスのinterfaceOrientationプロパティを廃止しましたUIViewController
インターフェイスの方向を検出するために使用できる2つのオプションがあります。

  • ステータスバーの向きを使用する
  • サイズクラスを使用します。iPhoneでは、オーバーライドされていない場合、現在のインターフェイスの向きを理解する方法を提供します。

まだ欠けているのは、アニメーション中に非常に重要な、インターフェースの向きの変化の方向を理解する方法です。
WWDC 2014のセッション「iOS8でのビューコントローラーの進歩」では、スピーカーは、に代わる方法を使用して、この問題の解決策も提供します -will/DidRotateToInterfaceOrientation

ここで提案されたソリューションは部分的に実装されました、詳細はこちら

func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        let orientation = orientationFromTransform(coordinator.targetTransform())
        let oldOrientation = UIApplication.sharedApplication().statusBarOrientation
        myWillRotateToInterfaceOrientation(orientation,duration: duration)
        coordinator.animateAlongsideTransition({ (ctx) in
            self.myWillAnimateRotationToInterfaceOrientation(orientation,
            duration:duration)
            }) { (ctx) in
                self.myDidAnimateFromInterfaceOrientation(oldOrientation)
        }
    }

11

私はこの質問をするために知っているSwiftあなたは同じコードで探しているなら、それはグーグルのトップのリンクのいずれかをだから検索してObjective-C

// add the observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(rotated:) name:UIDeviceOrientationDidChangeNotification object:nil];

// remove the observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];

// method signature
- (void)rotated:(NSNotification *)notification {
    // do stuff here
}

11

簡単です。これはiOS8および9 / Swift 2 / Xcode7で機能します。このコードをviewcontroller.swift内に配置するだけです。向きが変わるたびに画面の寸法が出力されます。代わりに独自のコードを配置できます。

override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
        getScreenSize()
    }
    var screenWidth:CGFloat=0
    var screenHeight:CGFloat=0
    func getScreenSize(){
        screenWidth=UIScreen.mainScreen().bounds.width
        screenHeight=UIScreen.mainScreen().bounds.height
        print("SCREEN RESOLUTION: "+screenWidth.description+" x "+screenHeight.description)
    }

5
この関数は廃止されました。代わりに「viewWillTransitionToSize:withTransitionCoordinator:」を使用してください
Mas S-AiYa

非推奨であることに加えて、didRotateFromInterfaceOrientation()確実に機能しません。いくつかの回転を逃します。iewWillTransitionToSize:withTransitionCoordinator:大丈夫です。
Andrej

@Andrej Swift 3のおかげで多くのものが非推奨になりました
Josh


9

この機能はどのクラスにも追加できるため、方向通知を確認するのが好きです。ビューやビューコントローラである必要はありません。アプリのデリゲートでも。

SWIFT 5:

    //ask the system to start notifying when interface change
    UIDevice.current.beginGeneratingDeviceOrientationNotifications()
    //add the observer
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(orientationChanged(notification:)),
        name: UIDevice.orientationDidChangeNotification,
        object: nil)

通知をキャッシュするより

    @objc func orientationChanged(notification : NSNotification) {
        //your code there
    }

8

目的C

-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator

迅速に

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

このメソッドをオーバーライドして、向きの変化を検出します。


8

Swift 3 | 頻繁に監視されるUIDeviceOrientationDidChange通知

次のコードは、縦向きから横向きへの変更に関係なく、デバイスが3D空間で向きを変更するたびに「deviceDidRotate」を出力します。たとえば、スマートフォンを縦向きにして、前後に傾けると、deviceDidRotate()が繰り返し呼び出されます。

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(
        self, 
        selector:  #selector(deviceDidRotate), 
        name: .UIDeviceOrientationDidChange, 
        object: nil
    )
}

func deviceDidRotate() {
    print("deviceDidRotate")
}

これを回避するには、以前のデバイスの向きを保持し、deviceDidRotate()の変更を確認します。

var previousDeviceOrientation: UIDeviceOrientation = UIDevice.current.orientation

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(
        self, 
        selector:  #selector(deviceDidRotate), 
        name: .UIDeviceOrientationDidChange, 
        object: nil
    )
}

func deviceDidRotate() {
    if UIDevice.current.orientation == previousDeviceOrientation { return }
    previousDeviceOrientation = UIDevice.current.orientation
    print("deviceDidRotate")
}

または、デバイスが横向きから縦向きに変わったときにのみ呼び出される別の通知を使用することもできます。この場合、UIApplicationDidChangeStatusBarOrientation通知を使用します。

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(
        self, 
        selector:  #selector(deviceDidRotate), 
        name: .UIApplicationDidChangeStatusBarOrientation, 
        object: nil
    )
}

func deviceDidRotate() {
    print("deviceDidRotate")
}

6

Swift 3.0で向きの変化を検出する方法の完全に機能する実装。

私は、の電話の向きため、この実装を使用することを選んだface upface down私にとって重要だった、と私は向きが指定された位置に知っていた後、私は変更するビューを望んでいました。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        //1
        NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

    }

    deinit {
        //3
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
    }

    func deviceOrientationDidChange() {
        //2
        switch UIDevice.current.orientation {
        case .faceDown:
            print("Face down")
        case .faceUp:
            print("Face up")
        case .unknown:
            print("Unknown")
        case .landscapeLeft:
            print("Landscape left")
        case .landscapeRight:
            print("Landscape right")
        case .portrait:
            print("Portrait")
        case .portraitUpsideDown:
            print("Portrait upside down")
        }
    }

}

注意すべき重要な部分は次のとおりです。

  1. DeviceOrientationDidChange通知ストリームをリッスンして、関数deviceOrientationDidChangeに結び付けます
  2. 次に、デバイスの向きをオンにしますunknown。向きが時々あることに注意してください。
  3. 他の通知と同様に、viewControllerが初期化解除される前に、通知ストリームの監視を停止してください。

これが役に立ったと誰かが願っています。


ありがとう、ありがとう
Mohammad Razipour 2017

だから私は混乱しています、現在私のすべてのビューはポートレートからランドスケープに基づいて調整されますが、デバイスが上向きの場合は変更されませんか?上記のコードを使用して、上向きのときに同じ効果を得るにはどうすればよいですか?
Famic Tech 2017

もう少しコンテキストを教えてもらえますか?あなたがどのような影響を参照しているかはわかりません。このコードは、方向を簡単に検出するように機能します。方向を検出するために複数の方法を使用している場合、問題が発生する可能性があります。
Rob Norback

6
override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
    //swift 3
    getScreenSize()
}


func getScreenSize(){
   let screenWidth = UIScreen.main.bounds.width
   let  screenHeight = UIScreen.main.bounds.height
    print("SCREEN RESOLUTION: \(screenWidth.description) x \(screenHeight.description)")
}

きれいな答え。私のために働いた。
Dorad

これは非推奨になりました
Aziz Javed

5

回転が完了したUIViewControllerTransitionCoordinator後で何かしたい場合は、次のような完了ハンドラを使用できます

public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    // Hook in to the rotation animation completion handler
    coordinator.animate(alongsideTransition: nil) { (_) in
        // Updates to your UI...
        self.tableView.reloadData()
    }
}

5

iOS 8以降、これが正しい方法です。

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    coordinator.animate(alongsideTransition: { context in
        // This is called during the animation
    }, completion: { context in
        // This is called after the rotation is finished. Equal to deprecated `didRotate`
    })
}

4

ローテーションが変更されたかどうかを確認します。 viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)

を使用してcoordinator.animateAlongsideTransition(nil) { (UIViewControllerTransitionCoordinatorContext)、移行が終了したかどうかを確認できます。

以下のコードを参照してください。

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

    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    coordinator.animateAlongsideTransition(nil) { (UIViewControllerTransitionCoordinatorContext) in
        // if you want to execute code after transition finished
        print("Transition finished")
    }

    if size.height < size.width {
        // Landscape
        print("Landscape")
    } else {
        // Portrait
        print("Portrait")
    }

}

4

デバイスの向きを検出する簡単な方法を次に示します:(Swift 3

override func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
            handleViewRotaion(orientation: toInterfaceOrientation)
        }

    //MARK: - Rotation controls
    func handleViewRotaion(orientation:UIInterfaceOrientation) -> Void {
        switch orientation {
        case .portrait :
            print("portrait view")
            break
        case .portraitUpsideDown :
            print("portraitUpsideDown view")
            break
        case .landscapeLeft :
            print("landscapeLeft view")
            break
        case .landscapeRight :
            print("landscapeRight view")
            break
        case .unknown :
            break
        }
    }

2
willRotateは非推奨になりましたviewWillTransition。より適切に使用してください。
前の

4

スウィフト4:

override func viewWillAppear(_ animated: Bool) {
    NotificationCenter.default.addObserver(self, selector: #selector(deviceRotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
}

@objc func deviceRotated(){
    if UIDevice.current.orientation.isLandscape {
        //Code here
    } else {
        //Code here
    }
}

さまざまなView Controllerを検出する必要がある場合、多くの回答が役に立ちません。これはトリックを行います。


また、ビューコントローラーが非表示になっているときにデバイスが回転したかどうかを確認する必要もあります(他のビューコントローラーが現在より上で開かれている場合)。実際には、スキップすることができますviewWillDisappearし、追加addObserverの中でviewDidLoad。iOSは自動的にView Controllerの登録を解除します。
アレクサンドルVolkov

忘れましたUIDevice.current.orientation.isFlat
user924

3

私のアプローチはbpeditが上に示したものと似ていますが、iOS 9以降に重点を置いています。ビューが回転したときにFSCalendarのスコープを変更したいと思いました。

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

    coordinator.animateAlongsideTransition({ (context) in
        if size.height < size.width {
            self.calendar.setScope(.Week, animated: true)
            self.calendar.appearance.cellShape = .Rectangle
        }
        else {
            self.calendar.appearance.cellShape = .Circle
            self.calendar.setScope(.Month, animated: true)

        }

        }, completion: nil)
}

これはうまくいきましたが、私はそれについてひどく感じました:)

coordinator.animateAlongsideTransition({ (context) in
        if size.height < size.width {
            self.calendar.scope = .Week
            self.calendar.appearance.cellShape = .Rectangle
        }
        }) { (context) in
            if size.height > size.width {
                self.calendar.scope = .Month
                self.calendar.appearance.cellShape = .Circle
            }
    }

2

私はそのようなクラスでUIUserInterfaceSizeClass変更された方向を検出するために使用しますUIViewController

override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {

    let isiPadLandscapePortrait = newCollection.horizontalSizeClass == .regular && newCollection.verticalSizeClass == .regular
    let isiPhonePlustLandscape = newCollection.horizontalSizeClass == .regular && newCollection.verticalSizeClass == .compact
    let isiPhonePortrait = newCollection.horizontalSizeClass == .compact && newCollection.verticalSizeClass == .regular
    let isiPhoneLandscape = newCollection.horizontalSizeClass == .compact && newCollection.verticalSizeClass == .compact

     if isiPhonePortrait {
         // do something...
     }
}

1

Swift 3の場合

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    if UIDevice.current.orientation.isLandscape {
        //Landscape
    }
    else if UIDevice.current.orientation.isFlat {
        //isFlat
    }
    else {
        //Portrait
    }
}

忘れましたUIDevice.current.orientation.isFlat
user924

1

スウィフト5

デバイスの向きの変更の通知を受け取るセットアップクラス:

class MyClass {

    ...

    init (...) {

        ...

        super.init(...)

        // subscribe to device orientation change notifications
        UIDevice.current.beginGeneratingDeviceOrientationNotifications()
        NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil)

        ...

    }

    ...

}

セットアップハンドラーコード:

@objc extension MyClass {
    func orientationChanged(_ notification: NSNotification) {
        let device = notification.object as! UIDevice
        let deviceOrientation = device.orientation

        switch deviceOrientation {
        case .landscapeLeft:   //do something for landscape left
        case .landscapeRight:  //do something for landscape right
        case .portrait:        //do something for portrait
        case .portraitUpsideDown: //do something for portrait upside-down 
        case .faceDown:        //do something for face down
        case .faceUp:          //do something for face up
        case .unknown:         //handle unknown
        @unknown default:      //handle unknown default
        }
    }
}

0
- (void)viewDidLoad {
  [super viewDidLoad];
  [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(OrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
}

-(void)OrientationDidChange:(NSNotification*)notification {
  UIDeviceOrientation Orientation=[[UIDevice currentDevice]orientation];

  if(Orientation==UIDeviceOrientationLandscapeLeft || Orientation==UIDeviceOrientationLandscapeRight) {
    NSLog(@"Landscape");
  } else if(Orientation==UIDeviceOrientationPortrait) {
    NSLog(@"Potrait Mode");
  }
}

注:このコードを使用して、UIViewControllerがどの方向にあるかを識別します


コードがプロジェクトで機能していません。他に何かする必要がありますか?
Syed Ali Salman

0
override func viewDidLoad() {
    NotificationCenter.default.addObserver(self, selector: #selector(MyController.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
//...
}

@objc
private func rotated() {
    if UIDevice.current.orientation.isLandscape {

    } else if UIDevice.current.orientation.isPortrait {

    }

    //or you can check orientation separately UIDevice.current.orientation
    //portrait, portraitUpsideDown, landscapeLeft, landscapeRight... 

}

0

iOS 13.1.2では、デバイスが回転するまで、方向は常に0を返します。回転イベントが発生する前に実際の回転を取得するには、UIDevice.current.beginGeneratingDeviceOrientationNotifications()を呼び出す必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.