Reputation: 634
I have a bug (which I meet second time already) in our project where I simply add a view at a top of UIViewController's view. Nothing outstanding, something like this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.displayEmailNotificationsIfNeeded()
}
The bug is that for some reason Auto Layout works incorrectly and doesn't add it at the top, but around 60pt lower than needed. I suspect that ~60pt comes from manual top constraint adjustment to include navigation bar and status bar, but that's not really important.
The fact is that the problem disappears if I run the method explicitly on the main queue, like this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
print("is main thread: ", NSThread.isMainThread())
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print("is main thread inside block: ", NSThread.isMainThread())
self.displayEmailNotificationsIfNeeded()
})
}
Print statements return true
for both cases. It's not really awful, but just out of curiosity I want to understand what causes this. Is there a way to debug this situation and understand why explicitly performing operations on main thread fixes some UI glitches?
Upvotes: 0
Views: 80
Reputation: 296
Try running it in viewWillLayoutSubviews()
. Your view controllers .view
should be fully laid out at this point. Note that this method may be called multiple times (ex: if .view
gets resized).
Upvotes: 0
Reputation: 1949
This is not a certain answer as I need to reproduce the bug to be certain but here is what I think might be happening.
When you execute the code directly, layout is not fully ready (it depends on other code you might have) and thus wrong layout. When you explicitly run the code in dispatch_async
you are running it on main thread too but on a later point in time and till then layout gets ready and you see correct result.
Also, execute this code and you should be able to understand.
print("1");
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print("2");
})
print("3");
Upvotes: 0
Reputation: 14304
An educated guess - it's not that the displayEmailNotificationsIfNeeded
isn't running on the main thread anyway without you adding it to the queue explicitly, it's more a matter of timing. There might be elements moving around as a result of other constraints in your storyboard that are in a different state once viewDidAppear
finishes executing. Adding the execution block asynchronously lets viewDidAppear
finish (and anything else running synchronously on the main queue) before executing your code.
Hope this helps.
Upvotes: 1