I had the same issue in my app where I needed to know when a view controller started and stopped being peeked at and came up with the following.
In order to monitor the lifecycle of a peek, you can track the lifecycle of the view controller being peeked at, starting with the view controller being created in previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController
, and ending with its viewDidDisappear()
.
I created a callback handler in the view controller being peeked at, PeekingViewController
,
var viewDidDisappearHandler: (()->())? = nil
and placed it in PeekingViewController
's viewDidDisappear
as such:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
viewDidDisappearHandler?()
}
Back in OriginalViewcontroller
, where we're peeking at PeekingViewController
from, keep a weak reference to the instance of the view controller being peeked at like so:
weak var peekingViewController: PeekingViewController?
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
self.peekingViewController = PeekingViewController()
return peekingViewController
}
Then, you can observe changes to the weak reference to the view controller being peeked at by filling in didSet in the peekingViewController
instance like so:
weak private var peekingViewController: PeekingViewController? {
didSet {
peekingViewController?.viewDidDisappearHandler = { [weak self] in
self?.peekingViewController = nil
}
if peekingViewController == nil { // Peek ended
handlePeekEnded()
} else { // Peek began
handlePeekBegan()
}
}
}
Note:
This logic will be triggered if a peak is performed and cancelled, but also if a peak is performed, the segue is completed, and then the newly presented PeekingViewController
is popped.
If you need logic regarding the peek being cancelled to only be triggered with an incomplete peek, and not a full peak and then a dismissal, you can achieve this by:
including a new boolean in OriginalViewController
to track if a view controller was completely pushed (OriginalViewController
's viewDidDisappear
will fire on a complete push, but not on a peek), and checking that boolean in peekingViewController
's didSet
, to determine if any action should be taken, and setting peekingViewController
to nil in OriginalViewController
's viewWillAppear
.