by Morten Bek Ditlevsen

Synchronizing asynchronous methods

In this post I am going to discuss something that is kind of a hack. Please let me know if you think that this is really ugly code, or if it actually a practical workaround. It may of course be both. :-)

Background

When iOS 8 came out there were some subtle changes that broke parts of the code in a project at work.
These changes had to do with the UIViewController method:

- (void)dismissViewControllerAnimated:(BOOL)flag
                           completion:(void (^)(void))completion

In iOS 7, when calling this method with animated: NO, the call would be synchronous. This means that on the line after calling this method, the previously presented view controller would be removed from the view controller hierarchy.

On iOS 8 this is no longer the case – and if you wish to be certain that the view controller is gone from the view hierarchy, your continuing code ought to be placed in the completion block.

This is not always practical. If you have many different code paths where one path ends up in a situation with an asynchronous call, then you have to create a block to send to the asynchronous call in one code path – and also call the block synchronously in the other path.
Nest this pattern a few times and you end up with pretty messy code, in my opinion.

In the above case I would rather ensure that the call behaved in a synchronous way – like it did on iOS 7.

The Core Foundation Run-Loop

There is an easy workaround for this: The Objective-C runtime lets you call a C-function (CFRunLoopRunInMode), to start a new run-loop. As long as this run-loop is running, the caller of the function is blocked, and will only continue execution when this new run-loop is terminated.

This means that if you have a method that is guaranteed to call a completion block asynchronously, then you may use the following pattern:

[self asynchronousCallWithCompletion: ^(void) {
    CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
    CFRunLoopStop(runLoop);    
}];
 
CFRunLoopRunInMode((CFStringRef)NSDefaultRunLoopMode, 2, NO);

What happens above is: First you call the method with a block that will be executed some time in the future.
Then you create a new run-loop, which will process touch events, update the UI, etc. etc. just like the main run-loop.
At some time in the future the completion block will be called (inside the new run-loop). This completion block will end the new run-loop, thus returning execution to the place where the CFRunLoopRunInMode was called.
Neat!

Notice that the second parameter to CFRunLoopRunInMode is the maximum amount of seconds that the newly created run-loop should run. This means that if for instance the completion block is never called by the asynchronous method, then the newly created run-loop will still only run for at most 2 seconds in the above case.

Perhaps already synchronous?

(more…)

February 17th, 2015

Tags: , , , , , , ,

4 Comments

UIVisualEffectView – detecting if blur is available

The UIVisualEffectView is a great UIView subclass that was introduced in iOS 8. So far the only supported effect is the ‘UIBlurEffect’, but from the look of the API it appears that more types of visual effects will be added in the future:

- (instancetype)initWithEffect:(UIVisualEffect *)effect

There are many great guides to creating the UIVisualEffectView so I will not bother you with that. If you would like to see how to use it, please have a look at Mastering Blur and Vibrancy on the Applidium site: http://applidium.com/en/news/mastering_blur_and_vibrancy_with_iOS_8/

The site has great examples of the different blur styles, and also of adding vibrancy effects to elements inside the blurred view.

Here is an example of the UIVisualEffectView using UIBlurEffectStyleLight

Skærmbillede 2015-01-28 kl. 21.35.19

I especially like it with a very slight drop shadow effect, which I think adds to the effect that the blurred view is slightly raised above the background.
(more…)

January 26th, 2015

Tags: , , , , , ,

2 Comments