Swift - Yön değişiklikleri nasıl tespit edilir


96

Tek resim görünümüne iki resim eklemek istiyorum (yani yatay bir resim için ve dikey için başka bir resim için) ancak hızlı dilleri kullanarak yön değişikliklerini nasıl algılayacağımı bilmiyorum.

Bu cevabı denedim ama sadece bir görüntü alıyor

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    if UIDevice.currentDevice().orientation.isLandscape.boolValue {
        print("Landscape")
    } else {
        print("Portrait")
    }
}

İOS geliştirmede yeniyim, herhangi bir tavsiye çok takdir edilecektir!


1
Lütfen sorunuzu düzenleyip kodunuzu biçimlendirir misiniz? Kodunuzu seçtiğinizde bunu cmd + k ile yapabilirsiniz.
jbehrens94


Manzara / portre doğru yazdırılıyor mu?
Daniel

evet doğru şekilde yazdırılıyor @simpleBob
Raghuram

Cihaz yönü yerine arayüz yönlendirmesini kullanmalısınız => stackoverflow.com/a/60577486/8780127
Wilfried Josset

Yanıtlar:


121
let const = "Background" //image name
let const2 = "GreyBackground" // image name
    @IBOutlet weak var imageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()

        imageView.image = UIImage(named: const)
        // Do any additional setup after loading the view.
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        if UIDevice.current.orientation.isLandscape {
            print("Landscape")
            imageView.image = UIImage(named: const2)
        } else {
            print("Portrait")
            imageView.image = UIImage(named: const)
        }
    }

Teşekkürler imageView için çalışıyor, peki ya viewController'a arka plan resmi eklemek istersem ??
Raghuram

1
Arka plana bir imageView yerleştirin. ViewController ana görünümüne tüm arka planı kapsayacak şekilde üst, alt, ön, son için imageView kısıtlaması verin.
Rutvik Kanbargi

4
Geçersiz kılma yönteminizde super.viewWillTransitionToSize'ı çağırmayı unutmayın
Brian Sachetta

viewWillTransitionAlt sınıflandırma yaparken neden geçersiz kılamayacağımı biliyor musunuz UIView?
Xcoder

2
Başka bir yönelim türü vardır: .isFlat. Sadece yakalamak portraitistiyorsanız, else cümlesini olarak değiştirmenizi öneririm else if UIDevice.current.orientation.isPortrait. Not: .isFlathem dikey hem de yatay olarak gerçekleşebilir ve başka bir {} ile her zaman orada varsayılan olacaktır (düz bir manzara olsa bile).
PMT

78

Kullanılması NotificationCenter ve UIDevice 'sbeginGeneratingDeviceOrientationNotifications

Swift 4.2+

override func viewDidLoad() {
    super.viewDidLoad()        

    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}

deinit {
   NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)         
}

func rotated() {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else {
        print("Portrait")
    }
}

Hızlı 3

override func viewDidLoad() {
    super.viewDidLoad()        

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

deinit {
     NotificationCenter.default.removeObserver(self)
}

func rotated() {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else {
        print("Portrait")
    }
}

1
Kesinlikle - yaklaşımımın bazı ek kodlara ihtiyacı var. Ama gerçekten "yönelim değişikliklerini algılar".
maslovsa

3
@Michael Çünkü kullanım viewWillTransition:sırasında değişiklik olacağını öngörür, değişiklik UIDeviceOrientationDidChangetamamlandığında çağrılır.
Lyndsey Scott

1
@LyndseyScott Açıklanan davranışın potansiyel olarak tehlikeli NSNotificationCenter kullanılmadan gerçekleştirilebileceğine hala inanıyorum. Lütfen aşağıdaki yanıtı gözden geçirin ve düşüncelerinizi bana bildirin: stackoverflow.com/a/26944087/1306884
Michael

4
Bu cevapta ilginç olan şey, bunun, görüntü denetleyicisinin shouldAutorotateayarlanması durumunda bile mevcut yönlendirmeyi vermesidir false. Bu, Kamera uygulamasının ana ekranı gibi ekranlar için kullanışlıdır - yön kilitlidir ancak düğmeler dönebilir.
yoninja

1
İOS 9'dan itibaren açıkça deinit'i çağırmanıza bile gerek yok. Buradan daha fazla bilgi edinebilirsiniz: useyourloaf.com/blog/…
James Jordan Taylor

63

Swift 3 Yukarıdaki kod güncellendi:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else {
        print("Portrait")
    }
}

1
UIView altsınıflandırırken viewWillTransition'ı neden geçersiz kılamadığımı biliyor musunuz?
Xcoder

Denetleyicideki görünümlerden herhangi birine başvurmaya çalışırsanız bu çökecektir.
James Jordan Taylor

@JamesJordanTaylor lütfen neden çökeceğini açıklar mısınız?
orium

Yorum yaptığımda 6 ay önceydi, ancak Xcode'un bunu denediğimde vermesinin nedeni bir sıfır işaretçi istisnasıydı çünkü görünümün kendisi henüz somutlaştırılmamıştı, sadece viewController. Yine de yanlış hatırlıyor olabilirim.
James Jordan Taylor

@Xcoder bu, UIViewController'daki bir işlevdir, UIView değil - bu yüzden onu geçersiz kılamazsınız.
LightningStryk

25

⚠️Cihaz Yönü! = Arayüz Yönlendirmesi⚠️

Swift 5. * iOS14 ve altı

Aşağıdakiler arasında gerçekten bir fark yaratmalısınız:

  • Aygıt Yönü => Fiziksel aygıtın yönünü gösterir
  • Arayüz Yönü => Ekranda görüntülenen arayüzün yönünü belirtir

Bu 2 değerin uyumsuz olduğu birçok senaryo vardır, örneğin:

  • Ekran yönünüzü kilitlediğinizde
  • Cihazınızı düz tuttuğunuzda

Çoğu durumda arayüz yönünü kullanmak istersiniz ve bunu pencereden alabilirsiniz:

private var windowInterfaceOrientation: UIInterfaceOrientation? {
    return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
}

Ayrıca <iOS 13'ü (iOS 12 gibi) desteklemek istemeniz durumunda şunları yaparsınız:

private var windowInterfaceOrientation: UIInterfaceOrientation? {
    if #available(iOS 13.0, *) {
        return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
    } else {
        return UIApplication.shared.statusBarOrientation
    }
}

Şimdi, pencere arayüzü yönelim değişikliğine nerede tepki vereceğinizi tanımlamanız gerekiyor. Bunu yapmanın birden fazla yolu vardır, ancak en uygun çözüm bunu kendi içinde yapmaktır willTransition(to newCollection: UITraitCollection.

Geçersiz kılınabilen bu miras alınan UIViewController yöntemi, arayüz yönü her değiştiğinde tetiklenecektir. Sonuç olarak, tüm değişikliklerinizi ikincisinde yapabilirsiniz.

İşte bir çözüm örneği:

class ViewController: UIViewController {
    override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
        super.willTransition(to: newCollection, with: coordinator)
        
        coordinator.animate(alongsideTransition: { (context) in
            guard let windowInterfaceOrientation = self.windowInterfaceOrientation else { return }
            
            if windowInterfaceOrientation.isLandscape {
                // activate landscape changes
            } else {
                // activate portrait changes
            }
        })
    }
    
    private var windowInterfaceOrientation: UIInterfaceOrientation? {
        return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
    }
}

Bu yöntemi uygulayarak, arayüzünüzdeki herhangi bir yönelim değişikliğine tepki verebileceksiniz. Ancak uygulamanın açılışında tetiklenmeyeceğini, bu nedenle arayüzünüzü manuel olarak güncellemeniz gerekeceğini unutmayın viewWillAppear().

Cihaz yönlendirmesi ve arayüz yönlendirmesi arasındaki farkın altını çizen örnek bir proje oluşturdum. Ek olarak, kullanıcı arayüzünüzü güncellemeye karar verdiğiniz yaşam döngüsü adımına bağlı olarak farklı davranışları anlamanıza yardımcı olacaktır.

Aşağıdaki depoyu klonlamaktan ve çalıştırmaktan çekinmeyin: https://github.com/wjosset/ReactToOrientation


iOS 13'ten önce bunu nasıl yapabilirim?
Daniel Springer

1
@DanielSpringer yorumunuz için teşekkür ederiz, gönderiyi iOS 12 ve daha düşük sürümleri destekleyecek şekilde düzenledim
Wilfried Josset

1
Bu cevabı en iyi ve en bilgilendirici buluyorum. Ancak iPadOS13.5'te önerilen kullanımı test etmek func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)işe yaramadı. Kullanarak çalıştırdım func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator).
Andrej

12

Swift 4+: Bunu yumuşak klavye tasarımı için kullanıyordum ve nedense UIDevice.current.orientation.isLandscapeyöntem bana öyle olduğunu söylüyordu Portrait, işte bunun yerine kullandım:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    if(size.width > self.view.frame.size.width){
        //Landscape
    }
    else{
        //Portrait
    }
}

iMessage uygulamamda da benzer bir sorun yaşıyorum. Gerçekten tuhaf değil mi? Ve çözümünüz bile tam tersi şekilde çalışıyor !! ¯_ (ツ) _ / ¯
Shyam

UIScreen.main.bounds'u kullanmayın, çünkü size geçişten önceki ekran sınırlarını verebilir (yöntemdeki 'Will'e dikkat edin), özellikle de çoklu hızlı rotasyonlarda. 'Size' parametresini kullanmalısınız ...
Dan Bodnar

@ dan-bodnar nativeBounds parametresini mi kastediyorsunuz?
asetniop

3
@asetniop, hayır. sizeViewWillTransitionToSize enjekte edilir parametre: withCoordinator: Yukarıda yazılı yöntemi. Bu, görünümünüzün geçişten sonra sahip olacağı tam boyutu yansıtacaktır.
Dan Bodnar

Çocuk görüntüleme denetleyicileri kullanıyorsanız, bu iyi bir çözüm değildir, kabın boyutu yönü ne olursa olsun her zaman kare olabilir.
bojan

8

Swift 4.2, RxSwift

CollectionView'ı yeniden yüklememiz gerekirse.

NotificationCenter.default.rx.notification(UIDevice.orientationDidChangeNotification)
    .observeOn(MainScheduler.instance)
    .map { _ in }            
    .bind(to: collectionView.rx.reloadData)
    .disposed(by: bag)

Hızlı 4, RxSwift

CollectionView'ı yeniden yüklememiz gerekirse.

NotificationCenter.default.rx.notification(NSNotification.Name.UIDeviceOrientationDidChange)
    .observeOn(MainScheduler.instance)
    .map { _ in }            
    .bind(to: collectionView.rx.reloadData)
    .disposed(by: bag)

5

Doğru cevabın aslında her iki yaklaşımın bir kombinasyonu olduğuna inanıyorum: viewWIllTransition(toSize:)ve NotificationCenter's UIDeviceOrientationDidChange.

viewWillTransition(toSize:)geçişten önce sizi bilgilendirir .

NotificationCenter UIDeviceOrientationDidChangesonra size haber verir .

Çok dikkatli olmalısın. Örneğin UISplitViewController, aygıt belirli yönlere döndüğünde DetailViewController, UISplitViewController" viewcontrollersdiziden " çıkar ve ana dizinin üzerine itilir UINavigationController. Rotasyon bitmeden detay görünüm kontrolörünü aramaya giderseniz, mevcut olmayabilir ve çökebilir.


5

Swift> = 3.0 sürümünü kullanıyorsanız, diğerlerinin de söylediği gibi uygulamanız gereken bazı kod güncellemeleri vardır. Süper aramayı unutma:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

   super.viewWillTransition(to: size, with: coordinator)

   // YOUR CODE OR FUNCTIONS CALL HERE

}

Resimleriniz için bir StackView kullanmayı düşünüyorsanız, aşağıdakiler gibi bir şey yapabileceğinizi unutmayın:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

   super.viewWillTransition(to: size, with: coordinator)

   if UIDevice.current.orientation.isLandscape {

      stackView.axis = .horizontal

   } else {

      stackView.axis = .vertical

   } // else

}

Interface Builder kullanıyorsanız, sağ paneldeki Identity Inspector bölümünde bu UIStackView nesnesi için özel sınıfı seçmeyi unutmayın. Ardından, özel UIStackView örneğine IBOutlet başvurusunu (ayrıca Arayüz Oluşturucu aracılığıyla) oluşturun:

@IBOutlet weak var stackView: MyStackView!

Fikri alın ve ihtiyaçlarınıza göre uyarlayın. Umarım bu size yardımcı olabilir!


Süper'i aramakla ilgili iyi bir nokta. Geçersiz kılınmış bir işlevde süper yöntemi çağırmamak gerçekten özensiz bir kodlamadır ve uygulamalarda öngörülemeyen davranışlar üretebilir. Ve potansiyel olarak izlenmesi gerçekten zor olan hatalar!
Adam Freeman

UIView altsınıflandırırken viewWillTransition'ı neden geçersiz kılamadığımı biliyor musunuz?
Xcoder

@Xcoder Bir nesnenin geçersiz kılma uygulamamasının nedeni (genellikle) çalışma zamanı örneğinin özel sınıfla değil, bunun yerine varsayılanla yaratılmasında yatar. Interface Builder kullanıyorsanız, sağ paneldeki Identity Inspector bölümünde bu UIStackView nesnesi için özel sınıfı seçtiğinizden emin olun.
WeiseRatel

5

Swift 4

ViewControllers görünümünü kullanırken UIDevice.current.orientation, alt görünümlerin dönüşü veya animasyonu sırasında tablo görünümü hücrelerinin kısıtlamalarını güncellemek gibi bazı küçük sorunlar yaşadım.

Yukarıdaki yöntemler yerine şu anda geçiş boyutunu görünüm denetleyicilerinin görünüm boyutuyla karşılaştırıyorum. Bu, kodun bu noktasında her ikisine de erişilebildiğinden, gitmenin doğru yolu bu gibi görünüyor:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    print("Will Transition to size \(size) from super view size \(self.view.frame.size)")

    if (size.width > self.view.frame.size.width) {
        print("Landscape")
    } else {
        print("Portrait")
    }

    if (size.width != self.view.frame.size.width) {
        // Reload TableView to update cell's constraints.
    // Ensuring no dequeued cells have old constraints.
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }


}

İPhone 6'da çıktı:

Will Transition to size (667.0, 375.0) from super view size (375.0, 667.0) 
Will Transition to size (375.0, 667.0) from super view size (667.0, 375.0)

Oryantasyon desteğini proje seviyesinden açmam gerekir mi?
Ericpoon

3

Önceki tüm katkılar iyi, ancak küçük bir not:

oryantasyon Plist, yalnızca dikey veya örnekte ayarlanırsa a) Sen edilecektir değil viewWillTransition aracılığıyla bildirilir

b) Kullanıcının cihazı döndürüp döndürmediğini bilmemiz gerekirse (örneğin bir oyun veya benzeri ...) yalnızca şunları kullanabiliriz:

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

Xcode8, iOS11 üzerinde test edildi


2

Geçiş tamamlandıktan SONRA arayüz yönünü almak için kullanabilir viewWillTransition(to:with:)ve dokunabilirsiniz animate(alongsideTransition:completion:). Etkinliğe girmek için buna benzer bir protokol tanımlamanız ve uygulamanız yeterlidir. Bu kodun bir SpriteKit oyunu için kullanıldığını ve sizin özel uygulamanızın farklı olabileceğini unutmayın.

protocol CanReceiveTransitionEvents {
    func viewWillTransition(to size: CGSize)
    func interfaceOrientationChanged(to orientation: UIInterfaceOrientation)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)

        guard
            let skView = self.view as? SKView,
            let canReceiveRotationEvents = skView.scene as? CanReceiveTransitionEvents else { return }

        coordinator.animate(alongsideTransition: nil) { _ in
            if let interfaceOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation {
                canReceiveRotationEvents.interfaceOrientationChanged(to: interfaceOrientation)
            }
        }

        canReceiveRotationEvents.viewWillTransition(to: size)
    }

Bu işlevlerde kesme noktaları ayarlayabilir ve bunların interfaceOrientationChanged(to orientation: UIInterfaceOrientation)her zaman viewWillTransition(to size: CGSize)güncellenmiş yönelimle çağrıldığını gözlemleyebilirsiniz .


1

Uygulama başlangıcında doğru yönlendirmeyi elde etmek için kontrol etmeniz gerekir viewDidLayoutSubviews(). Burada açıklanan diğer yöntemler işe yaramayacaktır.

İşte bunun nasıl yapılacağına bir örnek:

var mFirstStart = true

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if (mFirstStart) {
        mFirstStart = false
        detectOrientation()
    }
}

func detectOrientation() {
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
        // do your stuff here for landscape
    } else {
        print("Portrait")
        // do your stuff here for portrait
    }
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    detectOrientation()
}

Bu her zaman, uygulamanın ilk başlangıcında ve uygulama çalışırken döndürüldüğünde çalışacaktır .


0

Aygıt yönelimlerini saptamanın başka bir yolu traitCollectionDidChange (_ :) işlevidir. Sistem, iOS arayüz ortamı değiştiğinde bu yöntemi çağırır.

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
{
    super.traitCollectionDidChange(previousTraitCollection)
    //...
}

Ayrıca, yönlendirme uygulanmadan hemen önce bilgi almak için willTransition (to: with :) işlevini (traitCollectionDidChange (_ :)) işlevini kullanabilirsiniz.

 override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)
{
    super.willTransition(to: newCollection, with: coordinator)
    //...
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.