Passing a value between view Controllers - uinavigationcontroller

Objective C - iPhone Application
I have 2 programatically instantiated UINavigationControllers
In controller 1 I push this View
-(IBAction) showStartDateCalendar
{
ModalSetDateController *setStartDate = [[ModalSetDateController alloc] init];
[self.navigationController pushViewController: setStartDate animated:YES];
[setStartDate release];
};
To push it back I was planning on doing something like this:
-(IBAction) PopMyViewControllerBack;
{
[self.navigationController popViewControllerAnimated: YES];
}
But how would I be able to get the value of the datepicker on ModalSetDateController
Is this the best way to handle adding a UIPickerview. I would prefer to not even have a UINavigationController do it.
Thanks for your help!

Use:
-(IBAction)popMyViewControllerBack:(id)sender
and sender will be an instance of ModalSetDateController.

Related

Split View App with Multiple Detail Views

I'm trying to create a split view app with multiple detail view controllers. I'm having trouble with the basic setup/skeleton of the app. First I tried using xcode's Master-Detail Application template. The problem is that the classes from the template look something like this:
MasterViewController.h
MasterViewController.m
DetailViewController.h
DetailViewController.m
But what I want is something like this:
MasterViewController.h
MasterViewController.m
TitleViewController.h
TitleViewController.m
DateViewController.h
DateViewController.m
...
I can't figure out how to get my view controllers to load when user selects a new row.
I also tried using the sample MultipleDetailViews app from Apple, but the sample app has multiple issues for me including the fact that it uses nib files, which I don't want.
Can anyone help? Is there some tutorial about how to set up a split view app with multiple detail view controllers (without nibs)? Thank you!
*response:
Thanks! Could you post the link for the BigNerdRanch one? I couldn't find it. Also, I couldn't follow the Raywenderlich one because it uses an older version of xcode. Following your sample project, I think I'm close, but I'm getting a "terminating with uncaught exeption...". Here's what I did:
Create new project from Master-Detail template.
Add files MyTableViewController.h and MyTableViewController.m.
In MasterViewController.m, set the number of sections to 1 and the number of rows to 2.
In MasterViewController.h add property "MyTableViewController *myTableViewController".
(When user clicks 1st row, detailViewController should show, when he clicks 2nd row, myTableViewController should show.)
In MasterViewController.m, change didSelectRowAtIndexPath to:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int row = [indexPath row];
if( row == 0 ) {
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self.detailViewController];
NSArray *vcs = [NSArray arrayWithObjects:[self navigationController],nav, nil];
[[self splitViewController] setViewControllers:vcs];
}
else {
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self.myTableViewController];
NSArray *vcs = [NSArray arrayWithObjects:[self navigationController],nav, nil];
[[self splitViewController] setViewControllers:vcs];
}
}
The project runs, but when I click on the 2nd row (row == 1) I get the exception.
*update 2
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
int row = [indexPath row];
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
UINavigationController *detailNav = [delegate.splitController.viewControllers objectAtIndex: 1];
NSArray *viewControllers = nil;
switch (row) {
case 0:
viewControllers = [[NSArray alloc] initWithObjects: delegate.dateController, nil];
break;
case 1:
viewControllers = [[NSArray alloc] initWithObjects: delegate.repeatController, nil];
default:
break;
}
[delegate.splitController removeFromParentViewController];
detailNav.viewControllers = viewControllers;
[delegate.window addSubview: delegate.splitController.view];
}
There's actually a great tutorial by the BigNerdRanch on how to use UISplitViewControllers. Honestly, there's no difference between using them to push a different UIViewController as the Detail or Master viewController. They just need a reference to one another and when an event happens, you push a new viewController to one side or the other. I've attached a sample project below for you to reference.
sample project: link
Here's another tutorial on setting one up via Raywenderlich: link
So the following happenins inside a UITableViewController which is the MasterViewController.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 1. get the controllers from the split view [master, detail]
let controllers = split.viewControllers
// 2. get nav controller for the detail + the current storyboard
let navigationController = controllers[controllers.count-1] as!
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// 3. create the appropriate view controller (vc) then just replace the navigation controllers root vc with your new one using 'navigationController.setViewControllers([vc] ...'
switch indexPath.row {
case 1:
let vc = storyboard.instantiateViewController(withIdentifier: "DetailTableViewController") as! DetailTableViewController
navigationController.setViewControllers([vc], animated: false)
case 2:
let vc = storyboard.instantiateViewController(withIdentifier: "DetailCollectionViewController") as! DetailCollectionViewController
navigationController.setViewControllers([vc], animated: false)
default:
let vc = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
navigationController.setViewControllers([vc], animated: false)
break
}
}
You can do the same thing with segues. From the MasterViewController in the storyboard, drag a segue to a navigation controller and set the segue kind as a 'Show Detail (e.g. replace)'. Then from there in you didSelectRowAt you can call performSegue with identifier. Make sure that your segue has an 'Identifier'
Have a look at this video

useLayoutToLayoutNavigationTransitions in iOS7 crash

I am building an iOS7 app and I am trying to make use of the new useLayoutToLayoutNavigationTransitions effect. I have 2 UICollectionViewControllers and when I do the following I get the transition effect
SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondVC.useLayoutToLayoutNavigationTransitions = YES;
[self.navigationController pushViewController:secondVC animated:YES];
this works fine but what I want to do is make an api call and then in the completion block I want to push onto the nav stack like so
[webAPI getDetailsWithParams:params andCompletionBlock:^(id dict) {
//some work on the result
SecondViewController *secondVC = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondVC.useLayoutToLayoutNavigationTransitions = YES;
[self.navigationController pushViewController:secondVC animated:YES];
} andErrorBlock:^(NSError *error) {
}];
but this crashes every time with the following msg
-[UICollectionView _invalidateLayoutWithContext:]: message sent to deallocated instance 0x17a26400
can anyone tell me what I am doing wrong in this case? How can I get the transition effect when pushing from completion block?
EDIT: by changing it to the following I was able to transition to the second viewcontroller.
MyLayout *layout = [[MyLayout alloc] init];
SecondViewController *expandedVC = [[SecondViewController alloc] initWithCollectionViewLayout:layout];
and I also deleted the nib file that went with the file. nib file just consisted of a collection view and it was the file owners view.
While I can now transition I still do not understand why I could not do the previous nib method with in a block. So I would be grateful if someone could shed some light on it for me.
In order to use UICollectionViewController's useLayoutToLayoutNavigationTransitions to make transitions, the layout of the SecondViewController must be known. However, if you use initWithNibName:bundle: initializer, layout is not internally prepared yet, making your desired transitions impossible. As you mentioned in your edited question, you have to use [UICollectionViewController initWithCollectionViewLayout:] to initialize your second UICollectionViewController. Since your xib file has the same name as your class name, SecondViewController.xib is going to be loaded automatically by UIViewController, superclass of UICollectionViewController.
I think you were making UIKit calls from a thread that wasn't the main thread

UINavigationController pushViewController memory management

I have a code like this:
MyViewController *myController = [[MyViewController alloc] init];
[self.myNavController pushViewController:myController animated:YES];
[myController release];
In above case, deallc of MyViewController gets called twice resulting in a crash.
If I remove the last line "[myController release];" everything is fine.
Isn't this against the memory management guidelines?
if i understand truly, you must use initWithNibName against init like below:
MyViewController *myController = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
Please try this and reply, best regards.
Are you sure that the MyViewController's dealloc method not release someObj more than once?
You can try to clear away dealloc content,then,run the app again.

cellForRowAtIndexPath: returns nil for a generic prototype cell

This is driving me nuts!
I have a generic UITableViewController class with a generic prototype cell, with an identifier "myCell". Developing under ARC, iOS5 and using Storyboard, I am using the following method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"myCell"];
cell.textLabel.text = #"Title";
return cell;
}
Everything is hooked up in the storyboard, file owner, etc. I have several other UITableViewController in my project, using the same concept. All are working.
This particular class of UITableViewController doesn't want to work! keep throwing me the exception:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
Nslog'ed --> [tableView dequeueReusableCellWithIdentifier:#"myCell"] = (null)
Any idea and help is greatly appreciated!
EDIT:
For simplicity:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
Things that I have already done:
1. delete and create and set up the UITableViewController in storyboard!
2. Restarted Xcode
3. Restarted simulator
4. Restarted computer!!!!
5. Deleted and built the app over and over!
None worked.
By the way, I tried
if (cell == nil)
cell = [UITableViewCell alloc] initWithStyle....
.. and it will solve the problem, but again... this is ARC, storyboard, ... I am not supposed to do that. Xcode is supposed to take care of it!
You have to create your cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"myCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"myCell"] autorelease];
}
cell.textLabel.text = #"Title";
return cell;
}
Eventually I found the bug. I can't say I found the root cause, but investigating line by line, this is what I found and worked for me.
I have some objects in my UITableViewController that need to be alloc/init'ed before the view gets loaded, so that when callers can set them to pre-determind values. Since viewDidLoad is too late, I put them in initWithCoder method.
Commeting out and re-writing the initwithCoder method solved the problem. It seemed to me, that initWithCoder method was initing the UITableViewController as some different!
I had the same problem and just managed to overcome it.
If you are creating cells data dynamically you have to do the following things:
Select the table view.
Then go to the object inspector to the third tab from the right.
The first option is named: "Content". Change the selection from "Static Cells" to "Dynamic Prototypes".
Make sure you set the identifier of the cell properly and it is the same name you use on the "dequeueResableCellWithIdentifier"
It worked for me. I stopped getting "nil" cell values and everything seems to work properly.
if the "IF" statement is falling to true (cell == nil) for the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"myCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"myCell"] autorelease];
}
cell.textLabel.text = #"Title";
return cell;
}
then the name #"myCell" was either misspelled (Doesn't match) or is missing from the storyboard identifier field for the cell.

Need help understanding new Xcode 4.2 Master-Detail Application Template

I'm having some trouble understanding how all the "nuts and bolts" of the Master-Detail Application template works using Xcode 4.2 (without MainWindow.xib, as well as other changes). In "AppDelegate" we have the following code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
MasterViewController *masterViewController = [[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil];
UINavigationController *masterNavigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
self.splitViewController = [[UISplitViewController alloc] init];
self.splitViewController.delegate = detailViewController;
self.splitViewController.viewControllers = [NSArray arrayWithObjects:masterNavigationController, detailNavigationController, nil];
self.window.rootViewController = self.splitViewController;
[self.window makeKeyAndVisible];
return YES;
}
I see that the window is created programmatically instead of using the MainWindow.xib that was use in previous versions of Xcode in the beginning, as well as instantiating objects for both "Master" and "Detail" view controller classes that are provided with the template and using them for separate UINavigationControllers. Then the splitViewController property is assigned a new allocated UISplitViewController object assigning the detailViewController as the "delegate" and an array is created that contains both UINavigationControllers as "viewControllers." Then the window.rootViewController is assigned this splitViewController object.
The main questions I have are
1) Why do I need two "UINavigationControllers?" Couldn't I just create the "viewController" array using the "master" and "detail" view controllers themselves?"
2) What does it do setting the "detailViewController" as the "delegate?" What actually gets delegated?
3) And finally, if I wanted to push additional items onto the "DetailViewController" stack, would I just use the "DetailViewController" class to push using the "didSelectRow.." method, or would I need to do updates to self.splitViewController.viewControllers property instead?
1) Why do I need two "UINavigationControllers?" Couldn't I just create the "viewController" array using the "master" and "detail" view controllers themselves?"
You don't need. But it's a way. You have the ability to push the masterViewControlleras well as the detailViewController. Look at the layout in Storyboard. With using segue you can change the controllers on each side as you like.
2) What does it do setting the "detailViewController" as the "delegate?" What actually gets delegated?
The UISplitViewget's delegated. The detailViewController will take care of the interface changing in portrait and landscape mode. See UISplitViewDelegate in the documentation.
Showing and Hiding View Controllers
– splitViewController:shouldHideViewController:inOrientation:
– splitViewController:willHideViewController:withBarButtonItem:forPopoverController:
– splitViewController:willShowViewController:invalidatingBarButtonItem:
– splitViewController:popoverController:willPresentViewController:
3) And finally, if I wanted to push additional items onto the "DetailViewController" stack, would I just use the "DetailViewController" class to push using the "didSelectRow.." method, or would I need to do updates to self.splitViewController.viewControllers property instead?
Yes you can push in the masterViewControllerwith the tableView selection. You can push either with the new controller on the masterViewControllerstack by pushing in the
- (void)viewDidAppear:(BOOL)animated
and you can push in any way you like. You don't have to update the self.splitViewController.viewControllers property. Maybe you have to set the delegate to your new detailViewController.

Resources