Tespit sayfası iOS 13'te kapatıldı


115

İOS 13'ten önce, tüm ekranı kaplamak için kullanılan görünüm denetleyicileri sunuldu. Ve kapatıldığında, üst görüntü denetleyici viewDidAppearişlevi çalıştırıldı.

Artık iOS 13, görüntüleme denetleyicilerini varsayılan olarak bir sayfa olarak sunacak, bu da kartın alttaki görünüm denetleyicisini kısmen kapsayacağı anlamına geliyor, bu da bu viewDidAppearçağrılmayacağı anlamına geliyor çünkü üst görünüm denetleyicisi hiçbir zaman kaybolmadı.

Sunulan görünüm denetleyici sayfasının reddedildiğini tespit etmenin bir yolu var mı ? Bir tür temsilci kullanmak yerine üst görünüm denetleyicisinde geçersiz kılabileceğim başka bir işlev ?



Öyleyse, tüm modal sayfaları tek seferde vc köküne atmanın bir yolu var mı?
Weslie


Ne zaman reddedildiğini neden bilmeniz gerekiyor? Verileri yeniden yüklemek ve kullanıcı arayüzünü güncellemekse Bildirimler veya KVO iyi bir alternatif olabilir.
martn_st

Yanıtlar:


58

Sunulan görüntüleme denetleyici sayfasının reddedildiğini tespit etmenin bir yolu var mı?

Evet.

Bir çeşit temsilci kullanmak yerine üst görünüm denetleyicisinde geçersiz kılabileceğim başka bir işlev?

Hayır. "Bir çeşit temsilci" bunu nasıl yapacağınızdır. Kendinizi sunum denetleyicisinin temsilcisi yapın ve geçersiz kılın presentationControllerDidDismiss(_:).

https://developer.apple.com/documentation/uikit/uiadaptivepresentationcontrollerdelegate/3229889-presentationcontrollerdiddismiss


Tam ekran olsun ya da olmasın, sunulan bir görünüm denetleyicisinin reddedildiğini size bildiren genel bir çalışma zamanı tarafından oluşturulan olayın olmaması gerçekten de zahmetlidir; ancak bu yeni bir sorun değil, çünkü her zaman tam ekran olmayan görünüm denetleyicileri vardı. Sadece şimdi (iOS 13'te) daha fazlası var! Başka bir yerde bu konuya ayrı bir soru-cevap ayırıyorum: Birleşik UIViewController "en öndeki" algılama oldu mu? .


6
bu yeterli değil. Sunulan VC'nizde bir nabber ve görünümünüzü programlı olarak kapatan özel bir çubuk düğmesi varsa, sunum denetleyicisi aranmaz.
Irina

14
Merhaba @Irina - programlı görünümünüzü görevden eğer programlı görünümünüzü görevden dolayı, bir geri arama gerekmez - sen biliyorsun çünkü senin yaptığını sen yaptın. Temsilci yöntemi yalnızca kullanıcının yapması durumunda geçerlidir.
mat

6
@matt Cevap için teşekkürler. Görüş programatik olarak reddedildiğinde bu çağrılmaz (Irina'nın dediği gibi) ve bunu yaptığımızı bildiğimiz konusunda haklısınız. Sadece iOS13'teki yeni modal sunum stiliyle bir tür 'viewWillAppear' elde etmek için yazmak için gereksiz miktarda standart kod olduğunu düşünüyorum. Yönlendirmenin çıkarıldığı (örneğin, MVVM + koordinatörlerinde veya VIPER'de bir yönlendirici türünde) bir mimari aracılığıyla yönlendirmeyi yönetirken özellikle dağınık hale geliyor
Adam Waite

3
@AdamWaite Katılıyorum ama bu sorun yeni değil. Bu sorunu yıllardır, popover'larla, tam ekran olmayan sunulan görünüm denetleyicileriyle, uyarılarla vb. Yaşadık. Bunu Apple'ın "olaylar" repertuarındaki ciddi bir kusur olarak görüyorum. Sadece gerçeğin ne olduğunu ve neden olduğunu söylüyorum. Doğrudan buradaki sorunla
matt

1
PresentationControllerDidDismiss (_ :). Child VC'de geri düğmesini tıkladığımda çağrılmıyor. Herhangi bir yardım var mı?
Krishna Meena

41

Alt görünüm denetleyicisi bir sayfa olarak sunduğunda (yani varsayılan iOS 13 biçiminde) reddedildiğinde bildirilen bir üst görünüm denetleyicisinin kod örneğini burada bulabilirsiniz :

public final class Parent: UIViewController, UIAdaptivePresentationControllerDelegate
{
  // This is assuming that the segue is a storyboard segue; 
  // if you're manually presenting, just see the delegate there.
  public override func prepare(for segue: UIStoryboardSegue, sender: Any?)
  {
    if segue.identifier == "mySegue" {
      segue.destination.presentationController?.delegate = self;
    }
  }

  public func presentationControllerDidDismiss(
    _ presentationController: UIPresentationController)
  {
    // Only called when the sheet is dismissed by DRAGGING.
    // You'll need something extra if you call .dismiss() on the child.
    // (I found that overriding dismiss in the child and calling
    // presentationController.delegate?.presentationControllerDidDismiss
    // works well).
  }
}

Jerland2 cevabı levha olduğunda, (a) orijinal sorgulayıcı bir işlev çağrısını almak istedim çünkü kafası karışık işten (o aradığında kullanıcı çalışır edilir presentationControllerDidAttemptToDismiss uygulamaya oysa ve başarısız , ve (b) ayarını isModalInPresentation sayfasını kapatmak için) tamamen ortogonaldir ve aslında sunulan sayfayı gözden çıkarılamaz hale getirecektir (bu, OP'nin istediğinin tam tersidir).


6
Bu iyi çalışıyor. Sadece bir ipucu, denilen VC'nizde bir nav denetleyicisi kullanıyorsanız, nav denetleyicisini PresentationController?, Delege olarak atamanız gerektiğine dair bir ipucu.
instAustralia

@instAustralia, nedenini açıklayabilir veya bir belgeye referans verebilir misiniz? Teşekkürler.
Ahmed Osama

PresentationControllerDidDismiss Kullanıcı geri düğmesine bastığında nasıl çağrılır?
Krishna Meena

@AhmedOsama - gezinti denetleyicisi sunum denetleyicisidir ve bu nedenle işten çıkarmaya yanıt verecek olan delege olacaktır. Gezinme Denetleyicisine gömülü olan VC'yi de denedim, ancak burası benim kapatmam gereken ve yanıt veren asıl düğmelerimin bulunduğu yer. Doğrudan Apple belgelerinde bulamıyorum ama burada referans gösteriliyor sarunw.com/posts/modality-changes-in-ios13
instAustralia

26

Başka bir seçenek geri almak için viewWillAppearve viewDidAppearkümesidir

let vc = UIViewController()
vc.modalPresentationStyle = .fullScreen

bu seçenek tam ekranı kapsar ve kapatıldıktan sonra yukarıdaki yöntem çağrıları


2
Teşekkürler PiterPan. Bu çalışıyor. Bu harika ve en hızlı çözüm.
Erkam KUCET

Eski varsayılan davranışı geri yüklemenin bu hızlı ve güvenilir yolu için teşekkür ederiz. Bu düzeltmeyi anında uygulayabilmek ve ardından yeni davranışa rasyonel bir şekilde geçişi planlayabilmek harika.
Ian Lovejoy

12
Bu bir çözümden çok geçici bir çözümdür. Herkesin iOS 12 stil sayfalarına geri dönmesi harika değil. İOS 13 olanlar harika! :)
Matt

1
iPad, mod olarak sunulduğunda varsayılan olarak bir pageSheet olarak sunulduğundan, bunu iPad için kullanırken dikkatli olun. Bu, iPad'i fullScreen olarak göstermeye
zorlayacak

benim için çalışmıyor. Modal denetleyiciyi açtım. işten çıkarma ile kapatın, ancak willAppear çağrılmadı. Neden? teşekkürler
neo999

21

Gelecekteki okuyucular için burada uygulama ile daha eksiksiz bir cevap var:

  1. Kök görünümde denetleyiciler segment için hazırlanırlar (Modal'inizin bir nav denetleyicisine sahip olduğunu varsayarak)
    // Modal Dismiss iOS 13
    modalNavController.presentationController?.delegate = modalVc
  1. Modal görünüm denetleyicisinde aşağıdaki temsilci + yöntemini ekleyin
// MARK: - iOS 13 Modal (Swipe to Dismiss)

extension ModalViewController: UIAdaptivePresentationControllerDelegate {
    func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {


        print("slide to dismiss stopped")
        self.dismiss(animated: true, completion: nil)
    }
}
  1. Modal View Controller'da, temsilci yönteminin çağrılması için aşağıdaki özelliğin doğru olduğundan emin olun
    self.isModalInPresentation = true
  1. Kar

1
self.isModalInPresentation = true, ardından sürükle reddetme çalışmıyor. bu satırı kaldır delege yöntemi hala tamam olarak adlandırılır. teşekkür ederim.
Yogesh Patel

2
Bu, (a) orijinal sorgulayıcı sayfa kapatıldığında bir işlev çağrısı almak istediğinden (kullanıcı sayfayı kapatmayı denediğinde ve reddettiğinde çağrılan PresentationControllerDidAttemptToDismiss uyguladığınızda) ve (b) isModalInPresentation ayarının yapıldığından dolayı kafa karıştırılmıştır. tamamen ortogonaldir ve aslında sunulan sayfayı gözden çıkarılamaz hale getirecektir (bu, OP'nin istediğinin tam tersidir).
Matt

1
@Matt'ın cevap noktasını takip edin (a): Kullanmak presentationControllerDidDismissişe
yaramalı

5

Swift

Çağrısına Genel Çözümü viewWillAppear de iOS13

class ViewController: UIViewController {

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            print("viewWillAppear")
        }

        //Show new viewController
        @IBAction func show(_ sender: Any) {
            let newViewController = NewViewController()
            //set delegate of UIAdaptivePresentationControllerDelegate to self
            newViewController.presentationController?.delegate = self
            present(newViewController, animated: true, completion: nil)
        }
    }

    extension UIViewController: UIAdaptivePresentationControllerDelegate {
        public func presentationControllerDidDismiss( _ presentationController: UIPresentationController) {
            if #available(iOS 13, *) {
                //Call viewWillAppear only in iOS 13
                viewWillAppear(true)
            }
        }
    }

1
Bu, işlevi çağırarak değil, yalnızca üstten slaytı kullanarak kapatmaları işler dismiss(_).
Pedro Paulo Amorim

3

SÜRÜKLEME VEYA ÇAĞRI KAPATMA FONKSI aşağıdaki kod ile çalışacaktır.

1) Kök görünüm denetleyicisinde, aşağıdaki kod gibi sunum görünümü denetleyicisinin hangisi olduğunu söylersiniz

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "presenterID" {
        let navigationController = segue.destination as! UINavigationController
        if #available(iOS 13.0, *) {
            let controller = navigationController.topViewController as! presentationviewcontroller
            // Modal Dismiss iOS 13
            controller.presentationController?.delegate = self
        } else {
            // Fallback on earlier versions
        }
        navigationController.presentationController?.delegate = self

    }
}

2) Yine, kök görünüm denetleyicisinde, sunum görüntüleme denetleyicisi dağıtıldığında ne yapacağınızı söylersiniz.

public func presentationControllerDidDismiss(
  _ presentationController: UIPresentationController)
{
    print("presentationControllerDidDismiss")
}

1) Sunum görünümü denetleyicisinde, bu resimdeki iptal veya kaydet düğmesine bastığınızda. Aşağıdaki kod çağrılacaktır.

self.dismiss(animated: true) {
        self.presentationController?.delegate?.presentationControllerDidDismiss?(self.presentationController!)
    }

görüntü açıklamasını buraya girin


1
navigationController.topViewController'ı PresentationViewController'a dönüştürmek gerekli midir? Öyle olmadığını
anladım

Çocuk VC'yi İptal Et düğmesinden çıkardıktan sonra ana VC'deki verileri nasıl yeniden yükleyebilirim?
Krishna Meena

3

Reddedilen UIViewController'da viewWillDisappear'ı geçersiz kılın. isBeingDismissedBoole bayrağı ile sizi bir işten çıkarma konusunda uyaracaktır .

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if isBeingDismissed {
        print("user is dismissing the vc")
    }
}

** Kullanıcı aşağı kaydırmanın yarısındaysa ve kartı tekrar yukarı kaydırırsa, kart kapatılmamış olsa bile yine de reddedilmiş olarak kaydedilir. Ancak bu, umursamayabileceğiniz bir uç durumdur.


Peki yaself.dismiss(animated: Bool, completion: (() -> Void)?)
iGhost

0

Birisi sunulan görünüm denetleyiciye erişimi yok, onlar sadece görünüm denetleyicisi sunan aşağıdaki yöntemi geçersiz kılmak ve değiştirebilir modalPresentationStyleiçin fullScreenya stratejilerinden biri bu yaklaşımla yukarıda belirtilen ekleyebilir

 override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
    if let _ = viewControllerToPresent as? TargetVC {
        viewControllerToPresent.modalPresentationStyle = .fullScreen
    }
    super.present(viewControllerToPresent, animated: flag, completion: completion)
}

sunulan görünüm denetleyicisi gezinme denetleyicisiyse ve kök denetleyiciyi kontrol etmek istiyorsanız, yukarıdaki koşulu istediğiniz gibi değiştirebilirsiniz

if let _ = (viewControllerToPresent as? UINavigationController)?.viewControllers.first as? TargetVC {
   viewControllerToPresent.modalPresentationStyle = .fullScreen
}

-2

ModalPresentationStyle'ı FullScreen'de kullandıysanız, denetleyicinin davranışı her zamanki gibi geri döner.

ConsultarController controllerConsultar = this.Storyboard.InstantiateViewController ("ConsultarController") ConsultarController olarak; controllerConsultar.ModalPresentationStyle = UIModalPresentationStyle.FullScreen; this.NavigationController.PushViewController (controllerConsultar, true);


Mevcut cevapları tekrarlar.
mat

-3

Benim bakış açıma göre, Apple pageSheetvarsayılan olarak ayarlanmamalımodalPresentationStyle

fullScreenKullanarak stili varsayılana geri getirmek istiyorumswizzling

Bunun gibi:

private func _swizzling(forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
    if let originalMethod = class_getInstanceMethod(forClass, originalSelector),
       let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) {
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
}

extension UIViewController {

    static func preventPageSheetPresentationStyle () {
        UIViewController.preventPageSheetPresentation
    }

    static let preventPageSheetPresentation: Void = {
        if #available(iOS 13, *) {
            _swizzling(forClass: UIViewController.self,
                       originalSelector: #selector(present(_: animated: completion:)),
                       swizzledSelector: #selector(_swizzledPresent(_: animated: completion:)))
        }
    }()

    @available(iOS 13.0, *)
    private func _swizzledPresent(_ viewControllerToPresent: UIViewController,
                                        animated flag: Bool,
                                        completion: (() -> Void)? = nil) {
        if viewControllerToPresent.modalPresentationStyle == .pageSheet
                   || viewControllerToPresent.modalPresentationStyle == .automatic {
            viewControllerToPresent.modalPresentationStyle = .fullScreen
        }
        _swizzledPresent(viewControllerToPresent, animated: flag, completion: completion)
    }
}

Ve sonra bu satırı sizin AppDelegate

UIViewController.preventPageSheetPresentationStyle()

1
Bu zekice ama buna katılamıyorum. O Sen edilmektedir iOS 13. mizacına ters gider, konuya gelince, hacky olduğunu ve sözde iOS bizden yanıt Elma beklediği 13. yılında "kart" sunumları kullanmak için "etrafında iş" değildir; "aşmak".
mat

Söylediğinize katılıyorum, bu çözüm, Apple'ın bizi teşvik ettiği gibi kart sunum stilini kullanmaya yardımcı olmuyor. Ancak, bunu varsayılan stil olarak ayarlamak, mevcut kod satırlarının bir yerlerde hata presentingViewControlleryapmasına neden olur çünkü tetiklenmezviewWillAppear
jacob

1
Evet, ancak kendi cevabımda daha önce de söylediğim gibi, bu her zaman tam ekran olmayan sunumlar için bir sorundu (iPad'deki açılır pencereler ve sayfa / form sayfası gibi), bu yüzden bu yeni bir şey değil. Sadece şimdi daha fazlası var. Güvenmek viewWillAppearbir anlamda her zaman yanlıştı. Elbette Apple'ın gelip altımdan zemini kesmesini sevmiyorum. Ama dediğim gibi, bununla yaşamalı ve işleri yeni bir şekilde yapmalıyız.
mat

Projemde, bir görünüm denetleyicisinin (çağrılan presentedController) nerede sunulduğunu bilmediğim ve tam olarak ne olduğunu bilmediğim bazı senaryolar var presentingViewController. Örneğin: bazı durumlarda UIViewController.topMostViewController(), bana geçerli pencerede en üstteki görünüm denetleyicisini döndüren kullanmak zorundayım . Bu yüzden, mevcut davranışı doğru şeyler yapmak için (verileri yenile, kullanıcı arayüzü) viewWillAppeargörünüm denetleyicilerimde tutmak için sallamayı neden yapmak istiyorum . Bunu çözmek için herhangi bir fikriniz varsa, lütfen yardım edin.
jacob

Cevabımın sonunda bağladığım çözümün bunu çözmek için işe yaradığına inanıyorum. Sunum zamanında yapılandırmak biraz çalışma gerektirir, ancak temelde her sunumcunun (bir uyarı sunan kişi dahil) sunulan görünüm denetleyicisi kapatıldığında duymasını garanti eder.
mat

-5

presentingViewController.viewWillAppear'ı çağırmak kolay olmaz mıydı? işten çıkarmadan önce?

self.presentingViewController?.viewWillAppear(false)
self.dismiss(animated: true, completion: nil)

4
Aramak senin değil.
mat
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.