Как обнаружить изменение ориентации?


Я использую Swift, и я хочу иметь возможность загружать UIViewController, когда я поворачиваюсь к ландшафту, может ли кто-нибудь указать мне в правильном направлении?

Я ничего не могу найти в интернете и немного запутался в документации.

20 121

20 ответов:

вот как я получил это работает:

на AppDelegate.swift внутри Я:

NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

а затем внутри класса AppDelegate я поставил следующую функцию:

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

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

}

надеюсь, это поможет кому-нибудь еще!

спасибо!

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

необходимо обнаружить вращение при использовании камеры с AVFoundation и didRotate (теперь устаревший)& willTransition методы были ненадежны для моих нужд. Использование уведомления, опубликованного Дэвидом, действительно работало, но не является текущим для 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.х & бы быть рекомендуемый подход для продвижения вперед.

это предупреждение компилятора, которое вы получите в Swift 4.проект X если вы используете #selector функция должная к осуждение @objc вывод:

enter image description here

запись в swift-evolution об этом изменении.

настройка вызова:

// 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)

Swift 3.0 это было мое предыдущее решение, но я бы посоветовал использовать решение 4.0 выше:

настройка уведомления (уведомления имя является недействительным в Свифт-4.2):

NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

ответ на уведомление:

func rotated() {
    switch UIDevice.current.orientation {
    case .landscapeLeft, .landscapeRight:
        print("landscape")
    default:
        print("Portrait")
    }
}

сорвать уведомление (имя уведомления недопустимо в Swift 4.2):

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

используя -orientation собственность UIDevice не правильно (даже если это может работать в большинстве случаев) и может привести к некоторым ошибкам, например UIDeviceOrientation рассмотрим также ориентацию устройства, если оно обращено вверх или вниз, нет прямой пары в UIInterfaceOrientation enum для этих значений.
Кроме того, если вы заблокируете свое приложение в определенной ориентации, UIDevice предоставит вам ориентацию устройства без учета этого.
С другой стороны iOS8 устарел interfaceOrientation недвижимость на UIViewController класса.
Существует 2 варианта определения ориентации интерфейса:

  • используйте ориентацию строки состояния
  • используйте классы размера, на iPhone, если они не переопределены, они могут дать вам способ понять текущую ориентацию интерфейса

то, что все еще отсутствует, - это способ понять направление изменения ориентации интерфейса, что очень важно во время анимации.
в сессии WWDC 2014 "View controller advancement in 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)
        }
    }

легко, это работает в iOS8 и 9 / Swift 2 / Xcode7, просто поместите этот код внутри вашего viewcontroller.быстрый. Он будет печатать размеры экрана с каждым изменением ориентации, вы можете поместить свой собственный код вместо:

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

Я знаю, что этот вопрос является для Swift, но так как это одна из главных ссылок для поиска Google, и если вы ищете тот же код в 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
}

В Объективе C

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

быстро

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

переопределите этот метод для обнаружения изменения ориентации.

Swift 3 / Uideviceorientationdidchange Уведомление Наблюдается Слишком Часто

следующий код печатает "deviceDidRotate" каждый раз, когда ваше устройство меняет ориентацию в 3D - пространстве-независимо от изменения от портретной до альбомной ориентации. Например, если вы держите телефон в портретной ориентации и наклоняете его вперед и назад - 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")
}

полная рабочая реализация способа обнаружения изменения ориентации в Swift 3.0.

Я решил использовать эту реализацию, потому что телефон ориентаций face up и face 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 обязательно прекратите наблюдение за потоком уведомлений.

надеюсь, кто-то найдет это полезным.

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

Проверьте, изменилось ли вращение с помощью: 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")
    }

}

вот простой способ определить ориентацию устройства: ( 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
        }
    }

мне нравится проверять уведомление об ориентации, потому что вы можете добавить эту функцию в любой класс, не нужно быть видом или контроллером вида. Даже в вашем делегате приложения.

    //ask the system to start notifying when interface change
    UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()
    //add the observer
    NSNotificationCenter.defaultCenter().addObserver(
        self,
        selector: #selector(self.orientationChanged(_:)),
        name: UIDeviceOrientationDidChangeNotification,
        object: nil)

чем кэширование уведомление

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

мой подход похож на то, что 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
            }
    }

Для Swift 3

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

Я использую 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...
     }
}

Если вы хотите что-то сделать после завершения вращения, вы можете использовать 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()
    }
}

Так как 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`
    })
}
- (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 находится в какой ориентации