Лист обнаружения был отклонен на iOS 13

До iOS 13 представлены контроллеры представления, используемые для охвата всего экрана. И, когда отклонено, родительский контроллер представления viewDidAppear Функция была выполнена.

Теперь iOS 13 будет представлять контроллеры представления как лист по умолчанию, что означает, что карта будет частично покрывать базовый контроллер представления, что означает, что viewDidAppear не будет вызван, потому что родительский контроллер представления фактически никогда не исчезал.

Есть ли способ обнаружить, что представленный лист контроллера представления был отклонен? Какую-то другую функцию, которую я могу переопределить в родительском контроллере представления вместо использования какого-то делегата?

13 ответов

Решение

Есть ли способ обнаружить, что представленный лист контроллера представления был отклонен?

Да.

Какую-то другую функцию, которую я могу переопределить в родительском контроллере представления вместо использования какого-то делегата?

Нет. "Какой-то делегат" - это то, как вы это делаете. Сделайте себя делегатом и переопределением контроллера представления presentationControllerDidDismiss(_:),

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

Вот пример кода родительского контроллера представления, который уведомляется, когда дочерний контроллер представления, который он представляет в виде листа (то есть, в стиле iOS 13 по умолчанию), отклоняется:

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 запутан, поскольку (а) исходный вопросник хотел получить вызов функции, когда лист отклонен (тогда как он реализовал presentationControllerDidAttemptToDismiss, который вызывается, когда пользователь пытается и не может закрыть лист), и (б) параметр isModalInPresentation полностью ортогонален и фактически сделает представленный лист недоступным (что противоположно тому, что хочет OP).

Другой вариант вернуться viewWillAppear а также viewDidAppear установлен

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

эта опция охватывает весь экран и после закрытия вызывает вышеуказанные методы

Для будущих читателей вот более полный ответ с реализацией:

  1. В корневом представлении контроллеры готовятся к segue, добавьте следующее (при условии, что ваш модал имеет контроллер nav)
    // Modal Dismiss iOS 13
    modalNavController.presentationController?.delegate = modalVc
  1. В модальном контроллере представления добавьте следующий делегат + метод
// MARK: - iOS 13 Modal (Swipe to Dismiss)

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

        print("slide to dismiss stopped")
    }
}
  1. Убедитесь, что в модальном View Controller указано следующее свойство для вызова метода делегата
    self.isModalInPresentation = true
  1. прибыль

Если вы хотите что-то сделать, когда пользователь закрывает модальный лист изнутри этого листа. Предположим, у вас уже есть кнопка «Закрыть» с @IBActionи логика для отображения предупреждения перед закрытием или чем-то еще. Вы просто хотите определить момент, когда пользователь нажимает на такой контроллер.

Вот как:

      class MyModalSheetViewController: UIViewController {

     override func viewDidLoad() {
        super.viewDidLoad()

        self.presentationController?.delegate = self
     }

     @IBAction func closeAction(_ sender: Any) {
         // your logic to decide to close or not, when to close, etc.
     }

}

extension MyModalSheetViewController: UIAdaptivePresentationControllerDelegate {

    func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
        return false // <-prevents the modal sheet from being closed
    }

    func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
        closeAction(self) // <- called after the modal sheet was prevented from being closed and leads to your own logic
    }
}

Swift

Общее решение для звонка viewWillAppearв 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)
            }
        }
    }

Переопределить viewWillDisappear на отклоняемом UIViewController. Он предупредит вас об увольнении черезisBeingDismissed логический флаг.

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

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

** Если пользователь на полпути проведет пальцем вниз и снова проведет карту вверх, она все равно будет зарегистрирована как отклоненная, даже если карта не закрыта. Но это крайний случай, который может вас не волновать.

Функция перетаскивания или прекращения вызова будет работать с приведенным ниже кодом.

1) В контроллере корневого представления вы указываете, что является его контроллером представления представления, как показано ниже.

 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) Опять же, в корневом контроллере представления вы указываете, что вы будете делать, когда его контроллер представления представления отключен.

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

1) В контроллере представления презентации, когда вы нажимаете кнопку отмены или сохранения на этом рисунке. Ниже будет вызван код.

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

в SwiftUI вы можете использовать закрытие onDismiss

      func sheet<Item, Content>(item: Binding<Item?>, onDismiss: (() -> Void)?, content: (Item) -> Content) -> some View

Если кто-то не имеет доступа к представленному контроллеру представления, он может просто переопределить следующий метод представления контроллера представления и изменить modalPresentationStyle к fullScreen или можете добавить одну из упомянутых выше стратегий с этим подходом

 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)
}

если представленный контроллер представления является контроллером навигации, и вы хотите проверить корневой контроллер, можете изменить указанное выше условие, чтобы оно было похоже

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

Если вы использовали ModalPresentationStyle в FullScreen, поведение контроллера вернется в обычное состояние.

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

С моей точки зрения, Apple не должна устанавливать pageSheet по умолчанию modalPresentationStyle

Я бы хотел принести fullScreen стиль по умолчанию, используя swizzling

Как это:

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)
    }
}

А затем поместите эту строку в свой AppDelegate

UIViewController.preventPageSheetPresentationStyle()

Не было бы просто вызвать PresentingViewController.viewWillAppear? перед увольнением?

self.presentingViewController?.viewWillAppear(false)
self.dismiss(animated: true, completion: nil)
Другие вопросы по тегам