Używam Swift i chcę mieć możliwość załadowania kontrolera UIViewController po obróceniu do poziomu. Czy ktoś może wskazać mi właściwy kierunek?
Nie mogę znaleźć niczego w Internecie i jestem trochę zdezorientowany dokumentacją.
Używam Swift i chcę mieć możliwość załadowania kontrolera UIViewController po obróceniu do poziomu. Czy ktoś może wskazać mi właściwy kierunek?
Nie mogę znaleźć niczego w Internecie i jestem trochę zdezorientowany dokumentacją.
Odpowiedzi:
Oto jak to działa:
W AppDelegate.swift
wewnątrz didFinishLaunchingWithOptions
funkcji I umieścić:
NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
a następnie w klasie AppDelegate umieściłem następującą funkcję:
func rotated() {
if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) {
print("Landscape")
}
if UIDeviceOrientationIsPortrait(UIDevice.current.orientation) {
print("Portrait")
}
}
Mam nadzieję, że to pomoże komukolwiek innemu!
Dzięki!
selector
mieć formatu ciągu znaków "rotated:"
??
rotated()
nie jest)
UIDeviceOrientation
to coś innego niż UIInterfaceOrientation
... Wynika to z faktu, że UIDeviceOrientation
wykrywa również orientacje zakryte i odkryte, co oznacza, że kod może losowo przeskakiwać między portretem a krajobrazem, jeśli urządzenie spoczywa na prawie płaskiej, ale nieco nierównej powierzchni (np. Kołysze się na wystającej kamera z
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")
}
}
Muszę wykryć obrót podczas używania aparatu z AVFoundation
i okazało się, że didRotate
( obecnie przestarzałe ) & willTransition
metody były zawodne dla moich potrzeb. Korzystanie z powiadomienia wysłanego przez Davida zadziałało, ale nie jest aktualne dla Swift 3.x / 4.x.
Swift 4.2 Nazwa powiadomienia została zmieniona.
Wartość zamknięcia pozostaje taka sama jak w 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")
}
}
Aby skonfigurować powiadomienie dla Swift 4.2 :
NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification,
object: nil,
queue: .main,
using: didRotate)
Aby usunąć powiadomienie dla Swift 4.2 :
NotificationCenter.default.removeObserver(self,
name: UIDevice.orientationDidChangeNotification,
object: nil)
Jeśli chodzi o oświadczenie o wycofaniu , mój pierwszy komentarz był mylący, więc chciałem to zaktualizować. Jak wspomniano, użycie @objc
wnioskowania zostało przestarzałe, co z kolei było potrzebne do korzystania z pliku #selector
. Używając zamiast tego zamknięcia, można tego uniknąć i masz teraz rozwiązanie, które powinno uniknąć awarii z powodu wywołania nieprawidłowego selektora.
Swift 4.0
Dzięki Swift 4.0 firma Apple zachęca nas do unikania korzystania z #selector
, więc to podejście wykorzystuje teraz blok uzupełniania. To podejście jest również wstecznie kompatybilne ze Swift 3.x i byłoby zalecanym podejściem w przyszłości.
To jest ostrzeżenie kompilatora, które otrzymasz w projekcie Swift 4.x, jeśli użyjesz #selector
funkcji z powodu wycofania @objc
wnioskowania:
Wejście w szybką ewolucję tej zmiany .
Skonfiguruj wywołanie zwrotne:
// 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")
}
}
Skonfiguruj powiadomienie:
NotificationCenter.default.addObserver(forName: .UIDeviceOrientationDidChange,
object: nil,
queue: .main,
using: didRotate)
Zburz to:
NotificationCenter.default.removeObserver(self, name: .UIDeviceOrientationDidChange, object: nil)
Używanie -orientation
właściwości of UIDevice
nie jest poprawne (nawet jeśli mogłoby działać w większości przypadków) i może prowadzić do pewnych błędów, na przykład weź UIDeviceOrientation
pod uwagę także orientację urządzenia, jeśli jest ono odkryte lub w dół, nie ma bezpośredniej pary w UIInterfaceOrientation
wyliczeniu dla tych wartości.
Ponadto, jeśli zablokujesz aplikację w określonej orientacji, UIDevice poda orientację urządzenia bez uwzględnienia tego.
Z drugiej strony iOS8 wycofał interfaceOrientation
właściwość UIViewController
klasy.
Dostępne są 2 opcje wykrywania orientacji interfejsu:
Brakuje jeszcze sposobu zrozumienia kierunku zmiany orientacji interfejsu, co jest bardzo ważne podczas animacji.
W sesji WWDC 2014 „Zobacz postęp kontrolera w iOS8” prelegent również przedstawia rozwiązanie tego problemu, stosując metodę, która zastępuje -will/DidRotateToInterfaceOrientation
.
Tutaj proponowane rozwiązanie częściowo wdrożone, więcej informacji tutaj :
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)
}
}
Wiem, że to pytanie jest dla Swift
, ale ponieważ jest to jeden z najlepszych linków do wyszukiwania Google i jeśli szukasz tego samego kodu w 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
}
Spokojnie, działa to w iOS8 i 9 / Swift 2 / Xcode7, po prostu umieść ten kod w swoim viewcontroller.swift. Wydrukuje wymiary ekranu przy każdej zmianie orientacji, możesz zamiast tego umieścić własny kod:
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()
że jest przestarzały, nie działa niezawodnie. Brakuje niektórych obrotów. iewWillTransitionToSize:withTransitionCoordinator:
działa dobrze.
Lubię sprawdzać powiadomienie o orientacji, ponieważ możesz dodać tę funkcję w dowolnej klasie, nie musi być widokiem ani kontrolerem widoku. Nawet w delegacie aplikacji.
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)
niż buforowanie powiadomienia
@objc func orientationChanged(notification : NSNotification) {
//your code there
}
W celu C
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
Szybko
func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
Zastąp tę metodę, aby wykryć zmianę orientacji.
Swift 3 | UIDeviceOrientationDidChange Powiadomienie obserwowane zbyt często
Poniższy kod drukuje „deviceDidRotate” za każdym razem, gdy urządzenie zmieni orientację w przestrzeni 3D - niezależnie od zmiany orientacji z pionowej na poziomą. Na przykład, jeśli trzymasz telefon w orientacji pionowej i przechylasz go do przodu i do tyłu - urządzenie deviceDidRotate () jest wywoływane wielokrotnie.
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(deviceDidRotate),
name: .UIDeviceOrientationDidChange,
object: nil
)
}
func deviceDidRotate() {
print("deviceDidRotate")
}
Aby obejść ten problem, możesz przytrzymać poprzednią orientację urządzenia i sprawdzić, czy nastąpiła zmiana w 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")
}
Możesz też użyć innego powiadomienia, które jest wywoływane tylko wtedy, gdy urządzenie zmieni się z poziomego na pionowe. W takim przypadku chcesz skorzystać z UIApplicationDidChangeStatusBarOrientation
powiadomienia.
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(deviceDidRotate),
name: .UIApplicationDidChangeStatusBarOrientation,
object: nil
)
}
func deviceDidRotate() {
print("deviceDidRotate")
}
Pełna funkcjonalna implementacja sposobu wykrywania zmiany orientacji w Swift 3.0.
Zdecydowałem się skorzystać z tej implementacji, ponieważ orientacje telefonu face up
i face down
były dla mnie ważne, a chciałem, aby widok zmieniał się tylko wtedy, gdy wiedziałem, że orientacja jest w określonej pozycji.
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")
}
}
}
Ważne elementy, na które należy zwrócić uwagę, to:
unknown
czasami jest orientacja.Mam nadzieję, że ktoś uzna to za pomocne.
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)")
}
Jeśli chcesz coś zrobić PO zakończeniu rotacji, możesz użyć UIViewControllerTransitionCoordinator
procedury obsługi zakończenia w ten sposób
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()
}
}
Od iOS 8 jest to właściwy sposób na zrobienie tego.
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`
})
}
Sprawdź, czy rotacja zmieniła się z: viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
Z coordinator.animateAlongsideTransition(nil) { (UIViewControllerTransitionCoordinatorContext)
można sprawdzić, czy przejście jest zakończona.
Zobacz kod poniżej:
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")
}
}
Oto prosty sposób na wykrycie orientacji urządzenia: ( 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
jest teraz przestarzały, lepiej go używać viewWillTransition
.
Swift 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
}
}
Wiele odpowiedzi nie pomaga, gdy trzeba wykryć różne kontrolery widoku. Ten załatwia sprawę.
viewWillDisappear
i dodać addObserver
w viewDidLoad
. iOS automatycznie anuluje subskrypcję kontrolera widoku.
UIDevice.current.orientation.isFlat
Moje podejście jest podobne do tego, co pokazuje bpedit powyżej, ale koncentruje się na iOS 9+. Chciałem zmienić zakres FSCalendar, gdy widok się obraca.
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)
}
To poniżej działało, ale czułem się z tego powodu zakłopotany :)
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
}
}
Używam UIUserInterfaceSizeClass
do wykrywania zmiany orientacji w UIViewController
klasie, po prostu:
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...
}
}
Dla 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
Szybki 5
Skonfiguruj klasę, aby otrzymywać powiadomienia o zmianie orientacji urządzenia:
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)
...
}
...
}
Kod obsługi konfiguracji:
@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");
}
}
UWAGA: Po prostu użyj tego kodu, aby zidentyfikować UIViewController, w której orientacji
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...
}
W iOS 13.1.2 orientacja zawsze zwraca 0 do momentu obrócenia urządzenia. Muszę zadzwonić do UIDevice.current.beginGeneratingDeviceOrientationNotifications (), aby uzyskać rzeczywistą rotację przed wystąpieniem jakiegokolwiek zdarzenia rotacji.