Can you confirm that setting CALayer.contents property with CGImageRef in background thread still makes the core animation draw the contents image in main thread loop(which is UI Thread) rather than core animation thread or custom background thread which sets the contents property?
The reason I am asking this is that the core animation runs its own thread but however, it appears that when you set the CALayer.contents property the UI thread does the drawing to layer?
My experience is that if you have a visible layer and set the contents in a background thread, it is likely that will not draw immediately. The solution I used was to set the contents property asynchronously using a call to dispatch_async() on the main thread:
dispatch_async(dispatch_get_main_queue(), ^(void) {
layer.contents = (id)myCGImage;
});
In particular, my example here is for working with threads using GCD, where the thread itself may long outlive the operation that you are running. In this case, placing the assignment on the main thread, or forcing a [CATransaction flush] are two ways to indicate to the OS that you want that data to be presented to the user.
Otherwise, in the case of a GCD background thread, you are at the mercy of the thread's run loop which may not execute the [CATransaction flush] for a long time (in my experience, seconds).
Related
This is more a conceptual query than coding. I have a custom activity indicator, a custom view. The only public API, the user will have is the init(onFrame frame: CGRect), startAnimating() and stopAnimating().
So, I want to know in the startAnimating method, should I create a thread whether main or DispatchQoS to run the animation.
Also, if I don't put the animation code in a thread, will it be automatically running on main thread?
All communication with a UIView must be on the main thread. All Core Animation is automatically performed on a background thread. So do not do any explicit multithreading in connection with CABasicAnimation.
When I present a view controller or perform a segue in swift should I call it in DispatchQueue.main.async {} or is that a problem. My problem is that should I run it in the background thread or the main thread. If I load data from the database should I also present the view in DispatchQueue.main.async {} or should I run it in the background thread.
You should call all the UI related transition, changes and updates on the main thread. But where should you use DispatchQueue.main.async {} ?
It is to be used when the call is made from a background thread. Example,if you are downloading data and parsing from an API, you usually do that in a background thread, once that is completed, maybe you want a UI transition or update, So that update will take place in the main thread, and since currently you are on background thread, you require DispatchQueue.main.async {} to make the changes on UI.
All UI work should happen on the main thread, so unless you're on a background queue there is no reason to dispatch to the main queue. In the common case the segue is triggered from user interaction (e.g. a button press) which would already be on the main thread.
If loading the data for the view that is being presented takes a long time, then you can dispatch asynchronously to a background queue to load it and dispatch back to the main queue when the data has finished loading.
Since this is asynchronous, it will mean that the view will be without data from the time where it is presented to the time where the data has finished loading. This is something that you have to handle in your UI. Depending on your application, one example could be to displayed a loading indicator while the data is loading. Another could be to fetch or pass a minimal amount of data to display and load the larger amount of data asynchronously.
I have a dataTask making a webservice API call, and in the completion block for that dataTask I use the returned info to build a UIImageView and add it as a subview of the parent UIViewController. I have tried it with two API calls and I would like to know why one causes the UIImageView to be displayed so much slower than the other. Inside of the completion block, the fast call, which takes less than 1 second, is:
dispatch_async(dispatch_get_main_queue(), {
(self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
});
while the slower call, which takes roughly 13 seconds, is:
(self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
The first clearly runs on the main queue, but the second one I'm not so sure, since it's running directly in the completion block of the dataTask.
Can someone explain in detail why these two calls of the same function have such markedly different run lengths?
As these calls update the UI ,your first call is on main thread which updates your imageView immediately.But your second call directly within dataTask completion handler which generally runs on secondary thread run loops so this call
(self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
run on secondary thread runloop so it will not able to update UI. While some other call or some other event reloads the UIImageView which display the your computed image to display on UIImageView.
So as dataTask fetch data on secondary thread if your call is directly within completion handler than it is updating your imageView on secondary thread which should not be done as all UI must be update on main thread.Put your imageView updation on main thread.
Assume we have one UIViewController, call it A, in the viewDidLoad of that VC we add to it two UIViewControllers( B,C ). now to make the UI smooth in the viewDidLoad of A we do some GCD work
dispatch_queue_t queue = dispatch_queue_create("myqueue", NULL);
dispatch_async(queue, ^{
// Create webviews, do some setup here, etc etc
// Perform on main thread/queue
dispatch_async(dispatch_get_main_queue(), ^{
// this always has to happen on the main thread
[self.view addSubview:webView];
});
});
So the ParentViewController is somewhat better in UI rendenring.
My question is: Is this enough GCD work? or should I do the same thing in thew viewDidLoad of the child viewcontrollers? just because I created those child VC's on a background thread does that mean I need not do any GCD wokr on them? I am trying to make my UI as responsive as possible, but not clutter the code. I guess another way of wording this would be are GCD threads reentrant? is there a concept of reentrancy in iOS?
I don't think that adding a subview is a significant performance hit. Also, playing with views (or UIKit in general) should be done on the main UI thread. As far as I know it's considered bad practice to do such things in the background.
Try to save GCD/Async stuff to processor-intensive work or tasks of unknown durations such as downloading something from the internet.
Source and more info: Warning: UIKit should not be called from a secondary thread
I have a table view, and when the user selects a row, i push them to a new ViewController. At first, I initialized all my view objects in the ViewDidLoad method (involving web service calls) but I saw that it made the transition from my tableview to my new viewcontroller very long.
Instead, I moved most of my UI initialization in the ViewDidAppear method, and I like that it sped up my transition from tableview to new viewcontroller.
However, I cannot press any buttons in my NavigationBar at the top of the screen (like the back button) until my ViewDidAppear method completes and the UI is loaded.
What's the solution for this? Is there another way for me to load my UI without it preventing the user from interacting with the buttons in my NavigationBar?
Thanks!!
you do too much on the main thread. off load your longer operations like IO or longer computations BUT take care to not mess with the UI in the background thread.
Only touch the UI on the main thread. (Note sometimes it might seem safe, but in the long run it always end up producing weird issues)
one easy way is to use GCD:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//insert web service requests / computations / IO here
dispatch_async(dispatch_get_main_queue(),^{
//back to the main thread for UI Work
});
});
You could use grand central dispatch to make your web service calls asynchronously, which will keep the UI on the main thread responsive.
//create new queue
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.siteName.projectName.bgqueue", NULL);
//run requests in background on new queue
dispatch_async(backgroundQueue, ^{
//insert web service requests here
});
Here's a more in-depth tutorial:
http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial
Try to initialize your UI in the background by using the following method
[self performSelectorInBackground:#selector(initYourUI) withObject:yourObj];
You can call this in the ViewDidLoad