Я використовую 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 (), щоб отримати фактичне обертання перед тим, як відбудеться будь-яка подія обертання.