presenting view controllers using ios5 and storyboard - objective-c

Does using [self presentModalViewController:videoViewController animated:YES]; stack up multiple views?
On videoViewController I'm loading in several UIWebViews and each one of them loads in a video. And I have a button on that view which allows the changing of categories. When the category button is clicked, it shows a popover view and inside that view are several buttons for different categories. When one of them buttons are pressed, I'm doing the following to get back to videoViewController:
VideoViewController *videoViewController= [self.storyboard instantiateViewControllerWithIdentifier:#"VideoVC"];
[self presentModalViewController:videoViewController animated:YES];
Which works, it dismisses the category popover view and goes back to videoViewController loading in different videos. But then, changing the category a few times my app is starting to crash after getting 'did receive memory warning' messages.
I have a category 'all' which loads in all the videos and don't get no such message, but then changing the category several times (each category of course loads in less videos as it's being filter) I'm getting warnings.
So my real question really is, when I'm changing category and calling [self presentModalViewController:videoViewController animated:YES]; is it just racking up a view every time I change category? So for instance if I change category 5 times, will I have 5 videoViewController views just sat on top of each other? If so, what's the best way about going to avoid this?
I tried [self presentModalViewController:videoViewController animated:YES]; instead to see if that would make a difference, but the viewViewController doesn't reload and the category popover view doesn't dismiss.
This is for an iPad app if that makes a difference.
Thanks.

Every time you use this line:
VideoViewController *videoViewController= [self.storyboard instantiateViewControllerWithIdentifier:#"VideoVC"];
You are creating a new instant of VideoViewController and all that UIWebviews. What you need is using Objective-C's delegate pattern. When user selected one of the buttons in the popover view, you need to make a callback to the original instant of VideoViewController and passing back which button (some type of number, for example) that user has selected and handling the changes from there.

Related

Determine if viewcontroller is already on stack and if so, go to or dismiss it

I have an app I am working on that has a main screen with two buttons. One will take you to a view of a GPS (map) and then once there (new VC) it has options for setting that position or bringing up a list (tableview, another VC) of all locations already tagged.
At the list VC, if you click on the table cell, it will bring up the VC with the map. Problem is, this then adds the same VC bak on the stack. If a user clicks the Cancel button, they go back ones screen, then cancel goes back another screen, etc... until back to the main.
I know I can do the [self.navigationController popToRootViewControllerAnimated:YES]; to pop back to root but that is not always what I want.
Also, I know I can do: [[[self presentingViewController] presentingViewController] dismissModalViewControllerAnimated:YES];
I guess what I am saying is I want to "reuse" the GPS map view so I can call it from other VC's, so that is why I didn't go with the "pass back" to calling VC. So, is there away to either when a button is pressed and is to present a new VC, can I dismiss the prior one after the new one is shown? This way, a dismiss of current VC would take me back to where I need to be.
I hope makes sense and also that this question doesn't fall into the "Not an actual question" category.
Any help or better suggestions is greatly appreciated. Thx
Geo...
If you want to jump back some number of levels in a navigation controller's VC stack, you'll probably want to use its popToViewController:animated: method. To figure out if a particular view controller is on that stack, look at the navigation controller's viewControllers property. Be careful, though, as this kind of jumping around is a rather nonstandard UI behavior (even though there's API for it) which might confuse your users.
Also, using navigation controllers and presenting modally aren't the only ways to manage multiple view controllers -- you can always set the window's rootViewController yourself (and animate the change with UIView animations), even wrapping up your custom transition type in a custom UIStoryboardSegue if you like.
You can put a delegate in the table view. So that when a cell is pressed the info is passed to the delegate method in the VC which will dismiss the table view and reloads itself with the new info. You will have to implement refresh method in that VC.

Increasing number of living Views

I've set up a really simple project using storyboards including two views as shown here: http://i.stack.imgur.com/iRx21.png. The navigation can be done by either selecting a cell in the custom table view or hitting the back button labelled with "<<". Everything works fine except the following:
when I switch between the views, every time an instantiation happens. The profiling shows an increasing number of view objects. I would like to keep only one of each view and instantiation should be happen only once. What am I doing wrong? (I'm using ARC.)
Thanks in advance!
You should not link your back button to the parent view controller. This is what causes the new instantiation.
The way to go is to embed the table view into UINavigationController (in IB, choose Editor -> Imbed In -> Navigation Controller. Then change your segue to a Push segue. You can of course hide the navigation bar etc. to make things look exactly as you like. Then, link the back button to the controller with an IBAction and in the handler do a simple
[self.navigationController popViewControllerAnimated:YES];
This would be the appropriate logic of what you are doing. Of course, you can also push the web view modally and then handle the button click with
[self dismissModalViewControllerAnimated:YES];

UIViewControllers problems

Hi there and thank you in advice for your help. I have a really strange problem while working with ViewControllers in Xcode4. First of all I have to say that I'm not using storyboards and I prefer to create any UI element programmatically. So I've set a UIButton and I want that, when pressed, it brings me to a new view controller. This is the code I'm using for a button:
-(void)settingsAndExportHandle:(UIButton *)buttonSender {
SettingsViewController* settingView = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
settingView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:settingView animated:YES];
}
This buttons is initialized and allocated in the viewDidLoad method of the RootViewController. I want to switch to the other view controller (in this case SettingsViewController) when I press the button.
The strange thing is that when I press the button, the animation that flips the controllers goes well, but when it finishes I obtain the EXACT same things that I had on the RootViewControllers (same custom views, same buttons, same all!). The question is: what I'm missing?? I have to say that I use ARC (automatic reference counting) so I can't release or dealloc the views and buttons I've created on my RootViewController.
Any help will be appreciated. Thank you all!
Pushing and and modally presenting view controllers does not deallocate the view controller that presented them. It simply adds the additional view controller to the stack. You'll need to implement a callback method so that when the user hits the button to flip back to root view controller, your settings view controller lets the root view controller know what's about to happen so you can call a method you've written to reset the interface back to whatever state you need it at. You may also be able to use viewWillAppear: but that's a little messy.
However, according to the Apple Human Interface Guidelines, the user expects that when they push a view controller or modally present it, the view controller they were on will save state and be exactly the way they left it when they came back. It's disconcerting and annoying when state is not preserved while in a navigation controller context. It is especially annoying when it's modally presented.
Think about this - A user is in a hypothetical Mail app. They start typing out an email and set a font size and a color. They tap on the add attachment button, which brings up a modal view controller that allows them to select a picture. They select the picture and the modal view is dismissed, and in your implementation, the mail composing interface would have reset and the email content would be gone or at the very least the selected font size and color would be back to the default. That's not a good experience.

iPad SplitView Controller crashes when after the DetailView loads when trying to access the master controller again

I'm porting a fairly simple iPhone Navigation based app to a Split View iPad app. I have two nested levels of navigation on the Master view. The user picks a value from the first table and it loads the 2nd table. Selecting a value on the second table loads the Detail item for the detail view. I've (finally) gotten that part working.
When I try to pull up the Master view again, though, whether using the popover menu button in portrait or just navigating back to it and clicking on a record in landscape view, it crashes with a GDB: Program received signal: "EXC_BAD_ACCESS" error. I can't find anyplace in the code to step into to identify the problem.
I'm following the SplitView template fairly closely. I'm only really getting off the beaten track by adding that 2nd TableViewController. My RootViewController loads the 2nd TableViewController.
Here's the code:
First, in RootViewController.m I'm loading the 2nd TableView when an item is selected on the 1st (in didSelectRowAtIndexPath):
RequestsTableViewController *requestsTableViewController=[[RequestsTableViewController alloc] initWithNibName:#"RequestsTableViewController" bundle:nil];
requestsTableViewController.selectedDepartmentID = self.selectedDepartmentID;
[self.navigationController pushViewController:requestsTableViewController animated:YES];
[requestsTableViewController release];
Then, in the 2nd TableViewController, RequestsTableViewController, I load the detail item based on its selection in didSelectRowAtIndexPath:
TrackerSplitViewAppDelegate *appDelegate = (TrackerSplitViewAppDelegate *)[[UIApplication sharedApplication] delegate];
Request *aRequest = [appDelegate.requests objectAtIndex:indexPath.row];
appDelegate.detailViewController.thisRequest = aRequest;
appDelegate.detailViewController.detailItem = [NSString stringWithFormat:#"Row %d", indexPath.row];
[appDelegate release];
The app loads and sets my values and everything is working fine. I can navigate between the two TableViewControllers just fine with the auto-generated navigation as long as I don't click on a detail. Once I click on a detail and it's loaded, though, I crash the app if I try to re-access the MasterView.
I did nothing to the nib files to wire this navigation up, it all worked ported from the iPhone app (other than this crash). I can't find any examples with multiple master views to see where my wire up is different. I suspect I have to tweak something in the interface builder or something, but as I can't tell exactly where it's crashing, I'm having a hard time getting started. Or do I just need to push the original view back on to the stack programmatically after a detail has been selected? That seems ham-fisted.
[appDelegate release];
You should not release the appDelegate unless you've retained it. And you haven't done this. So get rid of this line.

How do I create a UINavigationController with a default "back" state?

I have a UINavigationController, complete with table view and associated magic.
The data I'm populating that table view from may have items from multiple categories, but the default view for the user will be one in which they are viewing all of the items, and then they have the ability to move backwards to a different table view that would allow them to select a different category, which would then return to the original table view with the appropriate data populated.
What's the proper approach for this? I can't seem to wrap my head around how I would make the navigation controller give me a back button (with appropriately wired up actions) without having come from a previous view in the stack (which wouldn't really exist at launch time if I start the user off from what is essentially the detail view, in stack terms.)
Also, the back button should be titled "Groups", not "Back", but that's really just an implementation detail. :)
Update: This issue finally manifested itself in production code, and here’s how I fixed it:
My UINavigationController is created in a nib, with the root view set as the “groups” view. Then, in my app delegate, I push the second view onto the stack while the app is launching.
That works fine for achieving the proper stack, but that doesn’t help with the back button title, because the navigation controller didn’t seem to want to grab the title from the root view, and instead was showing a back button with “Item” as the title.
So, on the pushed view, in viewDidLoad, I set:
self.navigationController.navigationBar.backItem.title = #"Groups";
and that did the trick.
The only potential downside of doing it this way would be if the pushed view controller were ever used in a scenario where the view below it wasn’t the groups view, but since the design of this particular application ensures that never happens, I’m accepting that failure. ;)
Another update:
I’m an idiot. Just set the title property of the navigationItem provided by the navigationController in Interface Builder, and boom, no issue. Or do it in code. It doesn’t matter, just don’t do it by setting the backItem.title way I show you above. That’s just dumb.
In your application delegate's .m file in the application:didFinishLaunchingWithOptions: method just push your view controllers like you normally would with[self.navigationController pushViewController:your_view_controller animated:YES]; and it should push them on before the application's first view controller appears.
To change the text of the button to Groups just call this before pushing your controllers.:
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle: #"Groups" style: UIBarButtonItemStyleBordered target: nil action: nil];
[[self navigationItem] setBackBarButtonItem: newBackButton];
[newBackButton release];