I am trying to show and hide three controls like UIBUtton, UILabel etc. in some scenario. I am doing this using below two functions.
- (void) hide
{
usernameField.hidden=YES;
passwordField.hidden=YES;
myLabel.hidden=YES;
}
- (void) show
{
usernameFieldField.hidden=NO;
passwordField.hidden=NO;
myLabel.hidden=NO;
}
But, when i call these functions under some server response code, that is i'm trying to show under connectionDidFinishLoading in success/failure server response..Its not doing that. i.e. Its not showing or hiding these controls in these situations..But same time, if i call these functions under a button click, its showing/hiding controls..So, Would these functions be called only under some events like button click? Can't we call from anywhere like i'm trying to do?
Please advise!
in your connectionDidFinishLoading
[self performSelectorOnMainThread:#selector(show)
withObject:nil
waitUntilDone:wait];
the reason is UI update show be called on main thread in order to get redraw.
Changing the hidden state of a control will not immediately draw the change to the screen, they will only flag it as needing to be drawn at some point in the future.
If the main thread is busy, then they will not get a chance to draw themselves.
Is your NSURLConnection code running on the main thread? You should move it to a background thread (but beware, changing the hidden property must be done on the main thread, not on a background thread! Look up grand central dispatch.)
Related
I know how to animate,display the Activity Indicator.
But I want to know how to immediately show the Activity Indicator.
Now When I am click the button it will load another view after 5 or many seconds. Clicking that button is loading a subview. When that Button Click method is called, it will call more than 7 methods after that and then it will load the subview. But the ActivityIndicator is displayed only after it complete executing all the 7 methods.
What I am trying to do is , I want to display the ActivityIndicator immediately after that Button Click method.
Any Idea ?
-(IBAction)button_click{
..
..
..
[self performSelector:#selector(afterDelay) withObject:nil afterDelay:0.3];
}
-(void)afterDelay{
YOUR_Code
}
The problem is that you perform your massive calculations (or loading from the internet?) on the main thread, but all the view mutation operations are not called immediately — on the start of the runloop the framework creates a new implicit Core Animation transaction, then it collects all the information about views' mutations, then commits the transaction on the end of the runloop. By blocking the main thread you are not allowing the transaction to commit and start your indicator's animation.
You can read about this architecture in the documentation: Core Animation Programming Guide: Transactions.
There are three options:
(Preferred) Perform your operations on the background.
Link your binary against QuartzCore.framework, then #import <QuartzCore/QuartzCore.h> and call [CATransaction commit] after your startAnimating call. This way you commit the implicit transaction.
Create your own explicit CATransaction.
If you have put the code to display or unhide the activityIndicator but it is not shown you have to use performSelector:withObject:afterDelay: method for doing any thing after unhide or displaying the activityIndicator with the delay of 0.001 will show the indicator immediately after clicking the button
Happy Coding :)
If your are talking about the system activity indicator, use this :
[[UIApplication sharedApplication] performSelector:#selector(setNetworkActivityIndicatorVisible:) withObject:[NSNumber numberWithBool:YES ] afterDelay:0.0];
I'm trying to understand how things work in regards to concurrent programming and calling setNeedsDisplay. I basically have Three objects.
Main View - container with different UIView objects, the main one being a UIScrollView
Small Map View - a small UIView that draws a miniature version of one of the other UIView items on screem
Processor - a delegate of the Main View that calculates what's on screen and calls the Main View back with what's in view.
So a simple use case of what's going on is the user touches the ScrollView and then the processor updates what's in view of the scrollView (like calculating coordinates, center point, etc) It does this using blocks and does it asynchronously. This then posts a notification to the MainView object.
When the MainView receives the notification, it just calls
[smallMap setNeedsDisplay]; // example 1
I put some logs around this call, and I do see it gets called right away. However, the drawRect: of this function does not get called right away. It gets called after 2 seconds or so.
I remember reading that setNeedsDisplay just marks the view for redraw to happen on the next event of the run loop.
But if I add this code instead:
// example 2
dispatch_async(dispatch_get_main_queue(), ^{
[smallMap setNeedsDisplay];
});
My view gets redrawn right away.
I guess I'm confused as to why I have to ask for the main event loop to call setNeedsDisplay to immediately redraw something. Like in example 1, by me calling setNeedsDisplay, is that done in the background or something and that's why it doesn't get redrawn right away? I'm trying to understand the difference in what's going on behind the scenes so I know what to look for the in future. Like should I have all my calls that need to be immediately redrawn in something similar to the example 2 block? Or is it because I'm processing my data asynchronously that I need to then ask for the main queue? Thanks!
My guess is 1 of 2 things:
Your code that is running on a separate thread is calling your MainView methods from the separate thread instead of using performSelectorOnMainThread or a GCD call that invokes the code on the main thread. Thus your call to setNeedsDisplay is actually taking place on a background thread, which is a no-no, as the other poster said.
The second possibility is that your MainView code is running on the main thread, but it gets busy doing time-consuming processing, or waiting for a synchronous call to another thread to finish, and doesn't service the event loop.
You can rule out the first possibility by setting a breakpoint on your call to setNeedsDisplay and looking at the call trace in the debugger to see what thread it's running from.
Figuring out the second possibility will take a little more work. You might need to delve into instruments.
setNeedsDisplay is a UIKIT API call and has to be called from the main thread of the application, also known as the UI thread. That's why calling it in a background thread doesn't have any immediate effect and scheduling it on the main queue has immediate effects.
See this related question https://stackoverflow.com/a/6988115/172690 for a more detailed answer.
I am dealing with and odd bug, I have created a custom segmented control that comprises of uibuttons (the controller itself is a subclass of UIView. I change the button's images for the selected and normal control state to indicate selection.
In my parent view in IB I have set the identity of the view to my custom class. The issue is I have a tableView, when I scroll it and the scroll animation the button inside my custom view does not change its image immediately, instead it waits for the table to finish scrolling and then it updates. Any ideas?
You're not giving a lot of detail, but it sounds as if you're trying to do too much work within the current run loop. If so then the answer will be relevant:
iOS waits until your code has finished executing before it does any display updating. So any updates are, in effect, queued until your current chunk of code is completed. To get around this, the most common trick is to allow the current run loop to end and the pick up execution again after a very short delay.
So, in your case, call the code to update your custom segment control. And then, instead of calling the code to update your table, park that code in another method and call that method using [self performSelector: #selector(delayedUpdate) withObject: nil afterDelay:0.1];
To illustrate how you might change code:
BEFORE
[self updateSegmentController];
[self updateTableScrollPosition];
return;
AFTER
[self updateSegmentController];
[self performSelector: #selector(updateTableScrollPosition) withObject: nil afterDelay: 0.1];
return;
Often a delay of 0.0 works just fine; it still achieves the effect of letting the current run loop complete and the display update before calling the nominated method. Sometimes adding an extra delay improves your animation appearance.
I've been trying to implement this for a long time and I have gotten no favorable results.
Say I have a method in the which an HTTP request is performed (specifically, a twitter update), and say I want to display a UIActivityIndicatorView while the HTTP request is in progress (I know when it is done because there are delegate methods that are called when the request is done, either with positive results or negative ones).
I've seen many answers that say that threading is necessary for the implementation of this class. At first I tried calling the startAnimating method in a different thread and the stopAnimating method directly (without starting a new thread). After that I saw how this guy does it and I thought this was safer as I was starting and stopping the indicator in two different methods (the delegate methods for the twitter update).
However, none of this two ways of doing this have given me the results I want (the activity indicator does not show up at all). Is there anything I'm missing?
Thank you in advance and I apologize if my question is too long.
Your help is very much appreciated.
Threading is absolutely forbidden when working with UIKit subclasses. You may have seen reports that UIActivityIndicatorView uses threading internally, but in no way does that mean you can access the object from multiple threads. All UIView subclasses (including UIActivityIndicatorView) must only be accessed from the main thread. This includes calling -startAnimating and -stopAnimating.
If you rewrite your code such that you're only ever accessing the activity view on the main thread, and it still isn't working, then I would guess that the view was either not added to a visible view, is covered up by another view, or has a frame that puts itself outside of the visible area of its superview.
You cannot perform UI stuff in a secondary thread.
You should perform your HTTP request in a secondary thread, while calling the activity view from the main thread.
I recommend using DSActivityView which is so much easier to use. Just 1 line to show an activity view, 1 line to hide.
MBProgressHUD it's also easy and shows and hides itself when the secondary thread has started/finished. Something like this:
[HUD showhileexecuting:"yourstuff" animated:YES]
"Yourstuff" will run on a separate thread.
You should not perform UI activities in a secondary thread.
How do I create a loading screen that can be reused at any given time. I'm aware of the Default.png but I need the flexibility to plug in a loading screen at any point during the application life cycle.
This is what I have thus far.
//inside a method that gets called by a UIButton
LoadingViewController* loadController = [[LoadingViewController alloc] initWithNibName:#"Loading" bundle:nil vertical:NO];
[self.view addSubview: loadController.view];
//some method call that takes a few seconds to execute
[self doSomething];
//This loads some other view, my final view
[self.view addSubview: someOtherView]
but it seems that the loading view is never displayed. Instead the previous view stays there until the "someOtherView" gets added. I put trace logs and the code does seem to get executed, I even replaced [self doSomething] with a sleep(2), but the intermediate loading view is never displayed.
If I remove [self.view addSubview:someOtherView]; then after a few seconds...(after doSomething finishes executing) the load view is displayed since there is no view that is pushed on top of it, however this is obviously not the functionality I want.
Can explain this behavior? Is there something about the rendering cycle that I am misunderstanding because it doesn't seem like the view (on the screen at least) is instantly updated, even though I call a [self.view addSubview: loadController.view];
Would I need to create a separate thread?
In general, for changes in the UI to be made visible to the user, control must return to the main runLoop. You are only returning to the runLoop after taking the loading view down and replacing it with the other view. One strategy for dealing with this is to move the code that does the loading onto another thread. NSOperation and NSOperationQueue can be used for this.
An even simpler approach is to use performSelectorInBackground:withObject to do the processing. Once processing is complete the UI can be updated again to show the data. It is important to remember that the UI updates must be carried out on the main thread. Use performSelectorOnMainThread:withObject:waitUntilDone: to accomplish this from the loading thread.
This sounds like a lot of complication but it is really as simple as breaking your single method up into three separate methods as follows:
Display the loading view and start the background process - this is the button action method.
Do the background loading - called from the button action function with performSelectorInBackground:withObject.
Remove the loading view and update the display with the data - called from the background thread with performSelectorOnMainThread:withObject:waitUntilDone.
I created a subclass of UIView where I initialized how my loading-view should work and look like. (My view appeared and slided in from the bottom with an nice animation).
I then added code that handled whether the loading-view should be visible or not in a subclass of UIViewController.
I then let all my viewcontrollers be an subclass of my new viewcontrollerclass which made it possible for me to do:
[self showloadingMessage:#"loading..."];
in all my viewcontrollers...