RESOLVED
I was performing the segue from inside a block and it was another thread. When I specifically move the operation back to the main thread it works great.
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self performSegueWithIdentifier:#"LoadToDisplay" sender:self];
}];
QUESTION
I want to programmatically present a view controller.
My app downloads some data from the internet, carries out some data handling and then loads the next view.
I use performSegueWithIdentifer successfully throughout the rest of the app however for some reason it is adding a huge delay with this specific transition.
I've used presentViewController with no delay but I can't use this, it was just to test whether I was missing something obvious.
I have an NSLog when the last data handling method completes and one when the next view controller is loaded. Using 'presentViewController' the time between logs is 14ms. When using performSegueWithIdentifer is a staggering 8.5secs!
I literally commented out one line and tested with the other. No other code changes.
Has anyone else experienced this or know what might be going on?
Thanks.
This is how my application is looking now:
After I perform a database update in my detail controller in view number 7 in the image above as soon as the save button is clicked the details are saved the the database. I'm taken back to tableView number 5 and expect the associated row to show latest updates by calling a special method from the parse.com framework that reloads objects and refreshes the table view e.g. [self loadObjects].
I use an unwind segue. In view 7 I make a connection between the save button and the exit symbol of it's controller window in interface builder and then in tableView number 5 I have my segue method that corresponds to this connect.
Unwind segue method:
-(IBAction)saveDetailsButtonTapped:(UIStoryboardSegue *)segue {
// alert goes here
[self performSelector:#selector(didTapRefreshButton:) withObject:self afterDelay:1.0];
}
This method clears the table and loads the first page of objects:
- (IBAction)didTapRefreshButton:(id)sender {
[self loadObjects];
}
When save is clicked on view number 7 the details are saved to the db and user is bought back to table view number 5 then the method above runs after 1.0 delay. I thought this was ok but didn't feel too right. I tried it on my phone and sometimes the delay wasn't long enough, meaning a failed refresh.
I then decided to try using a UIAlertView delegate method to detect when the ok button of the alertview was pressed and it worked ok most times but then the times I pressed OK to dismiss the alert really quickly upon arriving back on the view and the data wasn't reloaded.
Is there a better solid reliable way to refresh my data?
I need some way of knowing that the database update was successful and only then run the [self loadObjects] method and maybe do that automatically.
I have two methods that detect when objects will load (e.g. like when a button has been tapped) and when they have loaded. I have put some spinner code in there to show a spinner while loading is happening and take it away once it's done.
Isn't there some sort of way to queue methods, like some how in one method make it so one thing doesn't happen until another thing has happened?
If so, I'd really appreciate some insight and examples as I could just mark the app as complete but even though I'm not being paid and it's charity work I still have the urge to do my best.
Thanks for your time.
Kind regards
I have put some spinner code in there to show a spinner while loading is happening and take it away once it's done.
You should do something like that here.
I need some way of knowing that the database update was successful and only then run the [self loadObjects] method and maybe do that automatically.
Because you're saving to parse, it should be the parse SDK that tells you when the save is complete. If you're saving in the background (which you should be) then use the save method when provides you with a callback block that is called when the save has completed. This block being called is your trigger to remove the spinner and segue.
Side note :-
Yes, there are several different kinds of queues, most better than using performSelector:..., but there are also other ways of working with asynchronous activities and you should look at the asynchronous activity for guidance. i.e. can I get a callback when this is done, rather than how long should I wait and hope that it is done.
This is a two part question.
The first question:
So I have an app that transitions from one screen to another, using nib files. heres the code i use to switch screens:
self.transitionView = [[TransitionViewController alloc] initWithNibName:#"TransitionViewController" bundle:nil];
[self.view addSubview: self.transitionView.view];
my problem is i have about 30 - 40 lines of code in the viewdidload method that could conceivably be slowing the transition down slightly, as some of the code uses NSURL to read data in, but its a 2 - 3 second wait between when it enters the viewdidload method and when the view actually finishes loading. the line [super viewdidload] is at the very top of all of this too, which is the most confusing part. so why is my program taking 2 - 3 seconds to load? i think thats a pretty long time considering how little code i have there.
part 2 of the question is i want to create a transition screen that appears for 2 - 3 seconds before the actual screen loads. Ive taken that code out and for some reason my nib file still takes forever to load so i know it has nothing to do with that, but the transition screen needs to pop up BEFORE screen 2 for 3 seconds, even if the app is being opened after 10 minutes. i know there was a function in appdelegate that could assist in this endeavor but im at a loss for what the code should look like. it doesnt seem to be switching with
self.transitionView = [[TransitionViewController alloc] initWithNibName:#"TransitionViewController" bundle:nil];
[self.view addSubview: self.transitionView.view];
and aside from that, im at a loss for an understanding about how the code should be constructed to switch between screens in that fashion. any articles or suggestions anybody can give me?
EDIT
I also tried to separate the NSURL data in the viewdidload method from the method itself. i put it all in a method called initialize, and called it from screen 1 right after calling the 2nd view. it didnt help the switching time at all. the reason i did that is because i didnt want to receive all the nsurl data over again when the person opens the app. i just want them to retrieve it one time and be done with it. am i doing somethign wrong here? this is extremely frustrating
You should not put your transition effects in viewdidload , you should put them in viewdidappear or viewwillappear, that is the the next step that the controller will take after it is loaded. That is your best choice, as for the screen to to put it there, you need to load it using the nib file or in the viewdidload.
I hope that was helpful
I have a seemingly simple problem that I cannot for the life of me seem to figure out. In my iOS App, I have a UICollectionView that triggers network operation upon tapping it that can take a few seconds to complete. While the information is being downloaded, I want to display a UIView that fills the cell with a UIActivityIndicatorView that sits in the square until the loading is done, and the segue triggered. The problem is that it never appears. Right now my code looks like:
myLoadView.hidden = NO;
//Network Operation
myLoadView.hidden = YES;
The App simply stops for a couple seconds, and then moves on the the next view. I'd imagine Grand Central Dispatch has somthing to do with the solution, however please keep in mind that this code takes place in prepareForSegue, and the network info needs to be passed to the next View. For this reason not finishing the download before switching scenes has an obvious problem. Any help would be VASTLY appreciated. Thanks!
iOS commits changes in the interfaces after working out a routine. Hence you should perform your network operation in a background thread and then get back back on the main and perform the "show my view now thing". Have a look the below code for reference.
myLoadView.hidden = NO;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//Network Operation
dispatch_async(dispatch_get_main_queue(), ^{
myLoadView.hidden = YES;
});
});
Your network operation seems to be carried out on the main thread, aka UI thread. This blocks all further UI calls, including the call to unhide a view, until completion.
To resolve this, make your call asynchronous.
You should read this in full, if you haven't already.
As mentioned by other answers, the problem is that the UIView change doesn't happen until the current method finishes running, which is where you are blocking. Before GCD was available I would split methods in two and use performSelector:withObject:afterDelay (to run the second part also on the UI loop) or performSelectorInBackground:withObject: at the end of the first method. This would commit all the waiting animaations first, then do the actual tasks in the second method.
Well the better option for this type of indication is by using the custom HUD libraries like SVProgressHUD or MBProgressHUD
This may be impossible, but I'm trying to save the state of my application between scene transitions, but I can't figure out what to do. Currently I love the way that when you have an application running and hit the home button, you can go back to that application just where you left off, but if you transition between scenes (in a storyboard), once you get back to that scene the application state was not saved.
I only have two different scenes that need to be saved (you transition back and forth from one to the other). How can I go about saving a storyboard scenes state without taking up precise memory?
More Detailed: Here is my entire storyboard. You transition back and forth between scenes using the plus toolbar button. On the second scene the user can tap on the table view cells and a real image will fill the image view (See figure 1.2)
Figure 1.1
In figure 1.2 you see what happens when you tap inside one of the many table view cells (an image view pops up.)
Figure 1.2
THE PROBLEM: When you tap a table view cell, which fills an image view (shown in figure 1.2) it works fine if you stay on that scene or even hit the iPhone home button (if you hit the iPhone home button and then reopen the app the scene's state was saved and the image view filled with a simple image still shows just like we left it), but if I transition (using the plus button) back to the first scene, and then use the plus button on the first scene to get back to the second scene the image view that I created (shown in figure 1.2) disappears and the second scene loads without saving the state and image views we filled.
EDIT: I tried using the same view controller for both scenes, but it didn't solve the problem.
UPDATE: I just found the following code (that I think stores a views state). How could I use this and is this what I've been looking for?
MyViewController *myViewController=[MyViewController alloc] initWithNibName:#"myView" bundle:nil];
[[self navigationController] pushViewController:myViewController animated:YES];
[myViewController release];
I would suggest a combination of two things:
1. Take DBD's advice and make sure that you don't continuously create new views
2. Create a shared class that is the data controller (for the golfers, so that the data is independent of the scene)
The correct way to make the segues would be to have one leading from the view controller on the left to the one on the right. However, to dismiss the one on the right you can use
-(IBAction)buttonPushed:(id)sender
[self dismissModalViewControllerAnimated:YES];
}
This will take you back the the view controller on the left, with the view controller on the left in its original state. The problem now is how to save the data on the right.
To do this, you can create a singleton class. Singleton classes have only one instance, so no matter how many times you go to the view controller on the right, the data will always be the same.
Singleton Class Implementation (Of a class called DataManager) - Header
#interface DataManager : NSObject {
}
+(id)initializeData;
-(id)init;
#end
Singleton Class Implementation (Of a class called DataManager) - Main
static DataManager *sharedDataManager = nil;
#implementation DataManager
+(id)initializeData {
#synchronized(self) {
if (sharedDataManager == nil)
sharedDataManager = [[self alloc] init];
}
return sharedDataManager;
}
-(id)init {
if(self == [super init]) {
}
return self;
}
#end
Then, inside your view controller code you can grab this instance like this
DataManager *sharedDataManager = [DataManager initializeDataManager];
This way you will have the same data no matter how many times you switch views.
Also, you can better adhere to MVC programming by keeping you data and your view controllers separate. (http://en.wikipedia.org/wiki/Model–view–controller)
Figure 1.1 has a fundamental flaw which I believe the basis of your problem.
Segues (the arrows between controllers on the storyboard) create new versions of the UIViewControllers. You have circular segues. So when you go "back" to the original screen through the segue is really taking you forward by creating a new version.
This can create a major problem for memory usage, but it also means you can't maintain state because each newly created item is an empty slate.
Since your are using a UINavigationController and pushViewController:animated: you should "pop" your controller to get rid of it.
On your "second" scene, remove the segue from the + button and create an IBAction on a touchUpInside event. In the IBAction code add the "pop"
- (IBAction)plusButtonTapped {
[self.navigationController popViewControllerAnimated:YES];
}
I see what you mean. This should happen to every application, as when the last view controller in the navigation stack is transitioned away from, it is deallocated and freed. If you need to save values such as text or object positions, a plist may be the way to go. See this related question for how to use a plist.
Apple isn't going to do this for you. You should probably just save the state of each view using NSUserDefaults and each time your application launches re-load your saved data.
If you are storing everything in CoreData you would only need to save the active view and a few object ids, if not you would need to save any data you have.
Don't expect iOS to save anything that you have in memory between launches. Just store it in NSUserDefaults and load it each time.
Store the state of the scene in NSUserDefaults or inside a plist file then when loading up the scene just load it with the settings from there. If the images are loaded from the internet you might also want to save them locally on your iphones hard drive so it runs a bit smoother.
I don't think you should cycle the segues, just use one that connects viewcontroller 1 from viewcontroller 2 should be enough and that way you make sure that no additional viewcontrollers are being made (memory problems maybe?)
However for your particular problem, I believe that you should use core data to save the exact state of your table, view because ios doesn't save the exact state of view at all times. it will require work but you will achieve what you want. You will need to save the exact photo( using a code or enums that will be saved), the location in the table view, the score or well whatever data you need to save that state.
The best of all is that coredata is so efficient that reloading the data when the app is relaucnhed or into foreground it takes no time, and ive used core data to load more than 5k of records until now and works just fine and its not slow at all.
When i get back home ill provide a code you might use to get an idea of what i mean.
The key here is to:
Have some sort of storage for the data that your application needs. This is your application's data model.
Give each view controller access to the model, or at least to the part of the model that it needs to do its job. The view controller can then use the data from the model to configure itself when it's created, or when the view is about to appear.
Have each view controller update the model at appropriate times, such as when the view is about to disappear, or even every time the user makes a change.
There are a lot of ways that you can organize your data in memory, and there are a lot of ways that you can store it on disk (that is, in long term storage). Property lists, Core Data, plain old data files, and keyed archives are all possibilities for writing the data to a file. NSArray, NSDictionary, NSSet, and so on are all classes that you can use to help you organize your data in memory. None of that has anything to do with making your view controllers feel persistent, though. You'll use them, sure, but which one you choose really doesn't matter as far as updating your view controllers goes. The important thing, again, is that you have some sort of model, and that your view controllers have access to it.
Typically, the app delegate sets up the model and then passes it along to the view controllers as necessary.
Something else that may help is that you don't have to let your view controller(s) be deleted when they're popped off the navigation stack. You can set up both view controllers in your app delegate, if you want, so that they stick around. You can then use the ones you've got instead of creating new ones all the time, and in so doing you'll automatically get some degree of persistence.