Call UINavigationController from subview in UITableViewCell - iphone

I have a table view in which I have subclassed the cells. In these cells I add a subview of a UIView. When sliding the cell I add another UIView to the subclass of UITableViewCell.
I would like to present a ModalViewController when pressing a button inside the second UIView (subview in UITableViewCell). I do not have a navigation controller in this view, therefore I am passing the navigation controller from the view controller my table view is inside of and down to my second UIView.
Here, I call it as you normally would but nothing happens.
ComposeCommentViewController *ccvc = [[ComposeCommentViewController alloc] initWithNibName:#"ComposeCommentViewController" bundle:nil];
[navController presentModalViewController:ccvc animated:YES];
Does anyone have an idea what I might do wrong or have another solution?
EDIT: This is how I set navController
First I pass it to my subclass of UITableViewCell.
if (feedCell == nil)
{
feedCell = [[FeedCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[feedCell setNavigationControllerForSlidedView:[self navigationController]];
}
The subclass has the method setNavigationControllerForSlidedView: which looks like this:
- (void)setNavigationControllerForSlidedView:(UINavigationController *)navController
{
[feedSlidedView setNavController:navController];
}
In my FeedSlidedView I have declared and synthesized UINavigationController *navController;

The way you are going about this runs contrary to MVC (model-view-controller) design practices. You have a number of mechanisms for accomplishing what you want within the MVC framework that Apple provides in its SDK. Probably the simplest, in my opinion, would be to add a target-action to the button in the subview of your UITableViewCell. In your view controller's tableView:cellForRowAtIndexPath: method, add something like the following:
[button addTarget:self action:#selector(presentComposeComment:) forControlEvents:UIControlEventTouchUpInside];
In this case, self would be the UIViewController that is responsible for the UITableView in question. You would then include the method for the selector above in that view controller:
- (void)presentComposeComment:(id)sender {
ComposeCommentViewController *ccvc = [[ComposeCommentViewController alloc] initWithNibName:#"ComposeCommentViewController" bundle:nil];
[self presentModalViewController:ccvc animated:YES];
}
Note that I am not sending the presentModalViewController:animated: message to the navigation controller, but rather the view controller.

Related

Add subview to another view

With two ViewControllers, MyView 1 and MyView 2, is there possible to add a subview to MyView2 from MyView1.m?
I have tried:
MyView2 * screen = [[MyView2 alloc]initWithNibName:nil bundle:nil];
[screen.view addSubView:mySubView];
But my new instance of MyView 2 has no connection to the 'visible' ViewController on MyView2, right?
To clarify, the ViewController that is showing, is MyView1. I want MyView1 to be able to add a subview to the MyView2 view.
Thanks
I think you're confusing viewControllers with views, or at least your question is. Maybe it's something like this you're looking for -
MyViewController2 *myViewController2 = [[[MyViewController2 alloc] initWithNibName:nil bundle:nil] autorelease];
[myViewController2.view addSubView:mySubView];
// add any other views to myViewController2's view
[self.view addSubView:myViewController2.view]; // adding the view to VC1's view
If you want to be able to continue adding stuff throughout MyViewController1, you should declare either myViewController2 or its view as a retained property.
You could have MyView1 controller save some information in a common object in your app so that when MyView2 reappears it can add the subview to its view if needed.
Assuming MyView2 is a subclass of UIViewController, there is no addSubView: method on the viewcontroller itself. Instead, you want to add the subView to your view controller's view, like this:
[screen.view addSubview:mySubView];
try this,
[MyView1.view addSubView:MyView2];
[self.view addSubview:MyView1];

iOS4 - custom uiview accessing UINavigation stack

Bit confused with this one so bear with me...
I have a Navigation-based project which is working fine. I'm trying to create my first custom UIView to make a couple of buttons which I will use in multiple places. One of the buttons needs to push a viewcontroller into the navigation when it's clicked but I'm not sure how to do this.
When I had the button set up within a view controller I was using:
LocationViewController *controller = [[LocationViewController alloc] initWithNibName:#"LocationViewController" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
but the self.navigation controller won't work now, will it? How do I access the navigation controller of the viewcontroller that this uiview will be added to?
Hope at least some of that makes sense, as I said it's my first go at subclassing the uiview and adding it to multiple pages so I'm a bit lost.
EDIT TO ADD - I have the button click events inside the custom UIView, so that is where I'm trying to change the viewcontroller from. Should I instead wire up the events in whichever viewcontroller I add the view to?
Usually your appDelegate has a UINavigationController property. You can access it in your custom view like this:
UINavigationController *navController = (MyAppDelegate *)[[[UIApplication sharedApplication]
delegate] navigationController];
But more effective way is to make delegate method for your custom view and handle button action in your viewController.
MyCustomView.h
#protocol MyCustomViewDelegate
#interface MyCustomView : UIView {
id<MyCustomViewDelegate> cvDelegate; }
#property(nonatomic, assign) id<MyCustomViewDelegate> cvDelegate;
#protocol MyCustomViewDelegate #optional
-(void)didClickInCustomView:(MyCustomViewDelegate*)view withData:(NSObject*)data;
#end
MyCustomView.m
- (void)myButtonClick:(id)sender
{
[self.cvDelegate didClickInCustomView:self withData:someData];
}
So now you can handle this event in any place where is your custom
view.
Add the button from the interface builder or from the view controller's viewDidLoad using code:
CGRect frame = CGRectMake(0, 0, 24, 24);
UIButton *button = [[UIButton alloc] initWithFrame:frame];
[button addTarget:self action:#selector(handleMyButton:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
Then implement -(void)handleMyButton:(id)sender {}; in your view controller. Or you could instead write -(IBAction)handleMyButton:(id)sender {}; and link method and button using the interface builder.
Then inside the method just paste the block of code you posted above. If you started with the Xcode navigation controller template project it should work.
I think it's cleaner to hide the designated initializer initWithNibName: because it is an implementation detail.
When you say you are subclassing the UIView I don't know exactly what you mean. If you want to add another view controller with a custom view just use the UIViewController template and customize the XIB file, no need to subclass an UIView unless you are really modifying its behaviour, which I guess you are not. The view is a view, and the controller stuff like handling buttons should be in the controller.
The actual controller need to be in the navigation controller stack to be able to push another controller.
Or you can make a new navigation controller instance and push your LocationViewController.

Memory management, addSubview for a subclass of UIViewController

I have a view that shows a map. I have a custom subclass of UIViewController (DetailViewController) that gets shown when the detailDisclosureButton of the callout above the pin is pressed. While in my map class, I create my detailview and add it to the subview like this:
DetailViewController *detailView = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:nil];
detailView.locationPoint = locationPoint;
detailView.locationCoordinate = locationCoordinate;
[self.view addSubview:detailView.view];
[detailView release];
My DetailViewController has a TableView and parses the data in DetailViewController. However I get an error of sending the numberOfSectionsInTable message to a dealloc'd instance. I'm assuming it is this since I originally had this as a property and it worked fine with (nonatomic, retain). I'm assuming that I'm releasing it before the next view is done with it. If that is the case, when would I clean up the memory??? It seems like this would be the place to do it. Thanks.
I am not sure what makes you adding the view of DetailViewController into this mapviewcontroller's view. Don't you think right approach would be to either presentModalViewController or pushNavigationController?
DetailViewController *detailView = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:nil];
detailView.locationPoint = locationPoint;
detailView.locationCoordinate = locationCoordinate;
//[self.view addSubview:detailView.view];
[self.navigationController pushViewController:detailView animated:YES];
//OR
[self presentModalViewController:detailView animated:YES];
[detailView release];
You are getting the error because you are only using the view and deallocating the view controller immediately and hence tableview datasource and delegates are hitting a nil object.
Views do not retain their view controllers. Someone needs to retain the VC or else it will get released, and then the app will crash when the view makes a call into its delegate. When you use a navigation controller, the navcon has a stack of view controllers that it retains. Likewise with presentModalViewController, the OS takes care of retaining the detail VC.
Adding a detail view as a subview is not the normal way to navigate to a new view. Instead, one either uses a navigation controller and [navcon pushViewController::], or a modal subview and [self presentModalViewController::]. If the detail view occupies only a portion of the parent view, then it is normal to retain the view controller for the subview within the parent controller. That is, within the parent VC (your map class) add a property for the detail VC. Actually, it's more common to not even use a VC for a subview, but rather for screen-filling detail views.

How to add uiviewcontroller's view as content view to uitableviewcell?

I want to add a uiviewcontroller's view which has a button and few labels as a content view of a uitableviewcell.
Though i am able to add but the button action causes a crash.
MyViewController *controller = [[MyViewController alloc] initwithnibname:#"MyView" bundle:nil];
[cell.contentview addsubview:controller.view];
[controller release]; // if i comment out this part the button action is received by my view controller.
However there are memory leaks when its removed from view. The dealloc of myviewcontroller is not called.
What is the correct way to do this?
Add a view to a uitableview cell
which has a button and is handled by
the viewcontroller
How to assure memory is released
when the view goes out of scope?
TIA,
Praveen
I think the problem is, that you are releasing the controller and just using the subview which is retained by its superview. The action pattern needs a target which I assume is the released controller. And why should you release your viewController if you only need the view of it? Retain it and keep a reference through a property to it.
My way of adding subviews to a tableview cell would be in a subclass of UITableViewCell. Let's assume you are having a subclass of UITableViewCell, say ButtonTableViewCell. The init of the of cell creates and adds a UIButton to your cell and puts it nicely in its contentView. Decalre a property which references to the button. Like UIButton *myButton. What should be done in the cellForRowAtIndexPath is something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ButtonTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyButtonCell"];
if (cell == nil) {
cell = [[ButtonTableViewCell alloc] initWithReuseIdentifier:#"MyButtonCell"];
}
[cell.myButton addTarget:self action:#selector(onDoSomething) forControlEvents:UIControlEventTouchUpInside];
// Do more cell configuration...
return cell;
}
I've made up the initializer initWithReuseIdentifier which can be easily implemented.
You assure release of memory with the viewDidUnload method of the UIViewController and the dealloc method.
"button action causes a crash" - What is the crash when you tap the button?
Also, you only appear to be using the view of MyViewController (since you add the view to the cell and then release the controller)- what is this controller supposed to do other than contain a view? Why not just use a view?
Also, (wild guess here) the usual constructor of a button does not have new/alloc/copy, and therefore does not warrant a release. I've seen a lot of code crash from inappropriately releasing UIButton's.
Adding a view of a controller as a subview to any other view does not retain the controller.
The controller is released immediately after the release call and any button actions will be sent to deallocated instance.
We can avoid this by maintaining a strong reference to the controller
#Property(nonatomic,strong)MyViewController *controller;
or by adding a view controller as the ChildViewController
[self addChildViewController:controller];

Does this cause memory leak and how do I stop it?

I use a UIViewController XIB to create my UITableViewCell in IB then I implemented it in code like this:
if(cell == nil)
{
UIViewController *viewController = [[UIViewController alloc] initWithNibName:#"TotalViewCell" bundle:nil];
cell = (TotalViewCell *)viewController.view;
//[viewController release];
}
[[cell totalButton] setTitle:#"$100,000" forState:UIControlStateNormal];
// Action when totalButton is tapped
[[cell totalButton] addTarget:self action:#selector(showTotalDetail:) forControlEvents:UIControlEventTouchUpInside];
return cell;
Usually, I would release the viewController but I put a UIButton inside that cell and when the user taps the button, showTotalDetail gets called. This is the code for showTotalDetail:
-(void)showTotalDetail:(id)sender
{
// Move the totalTableView up!
CGRect totalDetailTableViewFrame = CGRectMake(20, 200, 280, 200);
[totalTableView setFrame:totalDetailTableViewFrame];
// Reload the new totalTableView
[self viewWillAppear:YES];
}
The function basically resizes the tableView and moves it to a different location on the screen. So, if I release the viewController, I get EXC_BAD_ACCESS error. It works if I don't release but I'm afraid I will have memory leaks.
Any advice?
Thanks.
You’re creating a UIViewController for each UITableViewCell? That’s pretty non-standard behavior. If you need to customize the behavior of a UITableViewCell, it’s probably better to subclass it than to use a view controller.
If you need to load a table view cell from a nib, look at Apple’s Table View Programming Guide for iOS. That example is in there without using a view controller.
As to your question: you’re right that in this scenario, the UIViewController will leak.