Я використовую Swift, і я хочу мати змогу завантажити UIViewController, коли я повертаюсь до пейзажу, чи може хто-небудь вказати мене в потрібному напрямку?
В Інтернеті я нічого не можу знайти і трохи заплутаний у документації.
Я використовую Swift, і я хочу мати змогу завантажити UIViewController, коли я повертаюсь до пейзажу, чи може хто-небудь вказати мене в потрібному напрямку?
В Інтернеті я нічого не можу знайти і трохи заплутаний у документації.
Відповіді:
Ось як я працював:
У 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")
}
}
Сподіваюсь, це допомагає комусь іншому!
Дякую!
selector
мати формат рядка "rotated:"
??
rotated()
не відповідає)
UIDeviceOrientation
це щось інше UIInterfaceOrientation
. Це тому, що ви UIDeviceOrientation
також виявляєте орієнтації обличчям вниз та обличчям вгору, тобто ваш код може перемикатися між портретом і пейзажем безладно, якщо ваш пристрій опирається на майже рівну, але злегка нерівну поверхню (тобто гойдається на виступаючій поверхні) камера
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")
}
}
Потрібно виявити обертання під час використання камери з 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
. Використовуючи натомість закриття, цього можна уникнути, і тепер у вас є рішення, яке повинно уникнути аварії через виклик недійсного селектора.
Swift 4.0
Завдяки Swift 4.0 Apple рекомендує нам уникати використання#selector
, тому зараз цей підхід використовує блок завершення. Такий підхід також сумісний із Swift 3.x і був би рекомендованим підходом уперед.
Це попередження компілятора, яке ви отримаєте в проекті Swift 4.x, якщо ви будете використовувати цю #selector
функцію через депресію @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)
Використання -orientation
властивості UIDevice
не є коректним (навіть якщо це може працювати в більшості випадків) і може призвести до деяких помилок, наприклад, UIDeviceOrientation
врахуйте також орієнтацію пристрою, якщо він знаходиться вгору або вниз, немає прямої пари в UIInterfaceOrientation
перерахунку для цих значення.
Крім того, якщо ви заблокуєте додаток у певній орієнтації, UIDevice надасть вам орієнтацію на пристрій, не враховуючи це.
З іншого боку, iOS8 застаріло interfaceOrientation
властивість у UIViewController
класі.
Для визначення орієнтації інтерфейсу доступні 2 варіанти:
Що ще не вистачає - це спосіб зрозуміти напрямок зміни орієнтації інтерфейсу, що дуже важливо під час анімації.
На сесії 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)
}
}
Я знаю, що це питання 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
}
Легко, це працює в 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)
}
didRotateFromInterfaceOrientation()
він не працює надійно. У ньому відсутні деякі обертання. iewWillTransitionToSize:withTransitionCoordinator:
працює добре.
Використовуйте новий viewWillTransitionToSize (_: зTransitionCoordinator :)
Мені подобається перевіряти сповіщення про орієнтацію, оскільки ви можете додати цю функцію в будь-який клас, не потрібно бути представником чи контролером перегляду. Навіть у делегата програми.
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
}
В Цілі С
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
Швидко
func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
Замініть цей метод, щоб виявити зміну орієнтації.
Швидкий 3 | Повідомлення UIDeviceOrientationDidChange спостерігається занадто часто
Наступний код друкує "deviceDidRotate" щоразу, коли ваш пристрій змінює орієнтацію в тривимірному просторі - незалежно від зміни з портретної на альбомну орієнтацію. Наприклад, якщо ви тримаєте телефон у вертикальній орієнтації та нахиляєте його вперед та назад - пристрійDidRotate () викликається повторно.
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")
}
}
}
Важливі деталі, які слід зазначити:
unknown
орієнтація є часом.Сподіваюся, хтось вважає це корисним.
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)")
}
Якщо ви хочете зробити щось ПІСЛЯ обертання завершено, ви можете скористатися таким 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`
})
}
Перевірте, чи змінився обертання за допомогою: 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
}
}
willRotate
зараз застаріло, краще використовувати viewWillTransition
.
Швидкий 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
}
}
Дуже багато відповідей не допомагають, коли потрібно виявляти різні контролери перегляду. Цей робить трюк.
viewWillDisappear
і додати addObserver
в viewDidLoad
. iOS відписує контролер перегляду автоматично.
UIDevice.current.orientation.isFlat
Мій підхід схожий на те, що показано на 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
}
}
Я використовую 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...
}
}
Для 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
Швидкий 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
}
}
}
- (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, в якій орієнтації
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...
}
У iOS 13.1.2 орієнтація завжди повертає 0 до повернення пристрою. Мені потрібно зателефонувати UIDevice.current.beginGeneratingDeviceOrientationNotifications (), щоб отримати фактичне обертання перед тим, як відбудеться будь-яка подія обертання.