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.swiftwewną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!
selectormieć formatu ciągu znaków "rotated:"??
rotated()nie jest)
UIDeviceOrientationto coś innego niż UIInterfaceOrientation... Wynika to z faktu, że UIDeviceOrientationwykrywa 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 AVFoundationi okazało się, że didRotate( obecnie przestarzałe ) & willTransitionmetody 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 @objcwnioskowania 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 #selectorfunkcji z powodu wycofania @objcwnioskowania:
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 -orientationwłaściwości of UIDevicenie 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ź UIDeviceOrientationpod uwagę także orientację urządzenia, jeśli jest ono odkryte lub w dół, nie ma bezpośredniej pary w UIInterfaceOrientationwyliczeniu 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ł interfaceOrientationwłaściwość UIViewControllerklasy.
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 UIApplicationDidChangeStatusBarOrientationpowiadomienia.
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 upi face downbył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:
unknownczasami 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ć UIViewControllerTransitionCoordinatorprocedury 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
}
}
willRotatejest 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ę.
viewWillDisappeari dodać addObserverw 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 UIUserInterfaceSizeClassdo wykrywania zmiany orientacji w UIViewControllerklasie, 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.