Presentviewcontroller:Animated:Yes View Will Not Appear Until User Taps Again

presentViewController:animated:YES view will not appear until user taps again

I've encountered the same issue today. I dug into the topic and it seems that it's related to the main runloop being asleep.

Actually it's a very subtle bug, because if you have the slightest feedback animation, timers, etc. in your code this issue won't surface because the runloop will be kept alive by these sources. I've found the issue by using a UITableViewCell which had its selectionStyle set to UITableViewCellSelectionStyleNone, so that no selection animation triggered the runloop after the row selection handler ran.

To fix it (until Apple does something) you can trigger the main runloop by several means:

The least intrusive solution is to call CFRunLoopWakeUp:

[self presentViewController:vc animated:YES completion:nil];
CFRunLoopWakeUp(CFRunLoopGetCurrent());

Or you can enqueue an empty block to the main queue:

[self presentViewController:vc animated:YES completion:nil];
dispatch_async(dispatch_get_main_queue(), ^{});

It's funny, but if you shake the device, it'll also trigger the main loop (it has to process the motion events). Same thing with taps, but that's included in the original question :) Also, if the system updates the status bar (e.g. the clock updates, the WiFi signal strength changes etc.) that'll also wake up the main loop and present the view controller.

For anyone interested I wrote a minimal demonstration project of the issue to verify the runloop hypothesis: https://github.com/tzahola/present-bug

I've also reported the bug to Apple.

UITableView and presentViewController takes 2 clicks to display

Check this out: https://devforums.apple.com/thread/201431
If you don't want to read it all - the solution for some people (including me) was to make the presentViewController call explicitly on the main thread:

dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:myVC animated:YES completion:nil];
});

Probably iOS7 is messing up the threads in didSelectRowAtIndexPath.

View will appear is not firing when we dismiss notification settings overlay

You should be using app lifecycle events (SceneDelegate/AppDelegate), not view controller lifecycle events (viewDidLoad, viewDidAppear, etc). sceneDidBecomeActive(_:) should be fine for your purposes — for iOS 13+, you should be using SceneDelegate to listen to scene phases, like going to settings (becoming inactive) and then coming back (becoming active again).

/// SceneDelegate.swift
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.

/// your code here
}

If you want to listen to sceneDidBecomeActive directly in your view controller, try listening to the didActivateNotification notification.

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()

NotificationCenter.default.addObserver( /// add observer
self,
selector: #selector(activated),
name: UIScene.didActivateNotification,
object: nil
)
}

@objc func activated() {
print("View controller is back now")
}
}

UIPresentationController not calling containerViewWillLayoutSubviews until after display

Thanks to riadhluke for directing me to UIPresentationController changes size when another view controller is displayed on top of it

My solution is in my menu's prepareForSegue

UIViewController *destination = [segue destinationViewController];
if (destination.modalPresentationStyle == UIModalPresentationFullScreen) {
destination.modalPresentationStyle = UIModalPresentationOverFullScreen;
}

Which forces fullscreen modal presentations to be Over full screen, which causes the menu not to be lost, and therefore not re laid out post dismissal animation.



Related Topics



Leave a reply



Submit