UINavigationController with UIView and UITableView - objective-c

I'm creating a navigation-based app which displays a graph, rendered with openGL, and a tableview listing disclosure buttons of all of the elements that are displayed on the graph, and a settings disclosure button.
The navigation controller is also a tableview delegate and datasource, and the tableview is added to the view programatically and has its' delegate and datasource set to 'self'. The OpenGL based graph view is added via IB.
The problem I'm having is that I'm trying to push a view controller (either settings or graph element properties) within the didSelectRowAtIndexPath method. The method registers and the new view is pushed on, but the tableview stays and obscures part of the view that was pushed on, as if it has a different navigation controller.
I can't seem to set the tableview's navigation controller to be the same as the rest of the UINavigationControllers' view.
Does anyone know how I could fix this?
My navigation controllers' initWithCoder method, where the tableview is added, appears as follows:
elementList = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStyleGrouped];
elementList.dataSource = self;
elementList.delegate = self;
[self.view addSubview:elementList];
Further in the source file, the DidSelectRowAtIndexPath method where the navigation controller is pushed appears as follows:
Settings* Controller = [[Settings alloc] init];
[self pushViewController:Controller animated:YES];
[Controller release];

Fixed by just adding a UITableView in IB, adding IBOutlet to elementList, and setting the UIViewController as the delegate and datasource via IB.
Stack Overflow can be really useful for putting your problems to words so the solution becomes obvious.

Related

Add UITableViewController to other View

Is it possible to embed (add as subview) a UITableViewController into another View Controller programmatically? I found a few answers here on StackOverflow but none worked for me with ARC and iOS6 SDK.
I know you can do this easily with Navigation and TabBar controllers but I am more curious about adding tableviews to a regular View controller. I need my tableview to occupy the lower part of the available screen space (I need the rest for other purposes, for which neither tab nor navigation types are suitable).
When I tried to do it, however, it did not work. I instantiated a subclassed UITableViewController, but when I added it to my self.view as a subview, the compiler said I tried to use "incompatible pointer types." Not only that, my instantiated UITableViewController does not have a .frame property, so I cannot set it dimensions, which would be the whole point of this exercise.
Building upon ogres answer, you should add the tableViewController's view as a subview, but it is not everything. The tableViewController is a viewController and it needs to know its parent and its children, to do its viewController-job correctly. I do not know any details here, but there is an entire talk about this from WWDC 2011 called "Implementing UIViewController Containment".
One problem I have experienced when only adding the view as a subview is that target actions don't seem to work. Tapping a UIButton or similar causes either a EXC_BAD_ACCESS or a unrecognized selector sent to instance.
So I recommend that you do something like this:
UIViewController *vc = //Your tableViewController
[self addChildViewController:vc]; //Important
[self.view addSubview:vc.view];
//If you want to set the frame, set the frame of the tableViewController's view
vc.view.frame = ...
yes , you can use tableview in another view without any problems
UITableViewController, but when I added it to my self.view as a subview
are you trying to add viewcontroller as subview or its view ? ( viewcontroller.view )
UITableViewController does not have a .frame property,
of course it does not have .frame property , it is a view CONTROLLER , you should see .view.frame

I lose access to my IBOutlets when making view controller new detail view controller

I have an iPad master-detail where the main detail controller is a navigation controller and depending on the table row (in the master view controller) selected, I may or may not replace the view controller managed by the detail navigation controller.
This seems to work fine, except that I lose access to the new detail view controller's IBOutlets when this move is made.
Here's me switching the navigation controller's view controller:
CustomerDetailViewController *subview = [[CustomerDetailViewController alloc] initWithNibName:#"CustomerDetailViewController" bundle:nil];
[subview setTitle:#"Testing"];
AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
app.splitViewController.delegate = subview;
[app.detailNavigationController setViewControllers:[NSArray arrayWithObjects:subview, nil]];
[subview setData:[custData objectForKey:#"name"]];
custData is an NSDictionary containing my view information. Here is the setData command:
- (void)setData:(NSDictionary *)cust {
NSLog(#"%#\n", [cust valueForKey:#"name"]);
self.nameLabel.text = [cust valueForKey:#"name"];
NSLog(#"%#\n", self.nameLabel.text);
}
So what happens is, subview becomes the new view controller but the label does not get changed - however, those two log commands are executed. The label is synthesized and wired up using IB and works if I push subview as a new view controller instead of replace it.
I'd say, the view is not yet initialized. Outlets are first connected in the viewDidLoad method. Try putting a log statement in the viewDidLoad to find out, which one gets called first.
If the viewDidLoad is called after your setData method, you can only set a local variable of the CustomerDetailViewController which is then read by viewDidLoad which sets the label accordingly.

Modal UIViewController will not push to next UIViewController

The start of the structure is as follows...
UITabBarController -> UINavigationController(s)
From each of the UINavigationControllers, I have a UIBarButtonItem that modally presents a UIViewController.
This UIViewController has a MKMapView with pins at multiple locations. When clicked, they display an annotation with a disclosure button.
Within this UIViewController, it is my intention to push a detail page (UITableViewController) when pressing the disclosure button of the annotation. The method calloutAccessoryControlTapped: receives the appropriate pin, but the transition to the next controller fails.
I have tried every combination of the following methods...
[self.navigationController ...]
[self.parentViewController ...]
[self.parentViewController.navigationController ...]
with the method being either...
presentModalViewController:
pushViewController:
I have done all of these with the UIViewController being on its own, and also with it embedded inside of a UINavigationController.
All of these properties return null...
self.navigationController
self.parentViewController
self.parentViewController.navigationController
This is the first time I've used storyboard for an Xcode project. Am I missing a step?
Try getting rid of the code and implementing the transitions in storyboard by control dragging from the button to the view controller you wish to load modally. When the "Storyboard Segue" menu pops up select "modal". In the modal view controller, I like to use code to return from the modal by calling:
[self dismissModalViewControllerAnimated:YES];
To Presenting Storyboard View Controllers Programmatically scroll to that section in gravityjack on the link provided.
For example, I have a view controller that I created in storyboard which I can call programmatically with the following two statements:
SettingsViewController *settingsVC = [self.storyboard instantiateViewControllerWithIdentifier:#"settingsVC"];
[self.navigationController pushViewController:settingsVC animated:YES];

View Controller behaves differently when set as 'initial view controller' vs. loading with presentModalViewController

My app has a map that tracks the user's location. This map will only appear under certain circumstances, and will dominate the user's attention until a particular task is complete, which is why the map isn't part of a navigation or tab bar UI.
If my map VC is set as the initial view controller in storyboard, it works fine. But if I try to load the map VC from elsewhere like this;
MapViewController *mapVC = [[MapViewController alloc] init];
[self presentModalViewController:mapVC animated:YES];
I just get a black screen.
I can confirm with NSLog that the VC is calling viewDidLoad and viewDidAppear, but the 'map' property of the VC is (null). I don't understand why (or how) I need to create the map property manually when using this technique, but it gets done for me when it is the initial VC.
The MapViewController instance in your storyboard is configured with a view hierarchy, including an MKMapView, and whatever else you did to configure that particular instance in the storyboard.
Now in this code which you show here, you are creating a completely new instance of MapViewController. It has no relationship to the instance in the storyboard other than they happen to be of the same class. So the one you create here with [[MapViewController alloc] init] has no view hierarchy (which is why you see a black screen), and none of the outlets or other configuration you may have made to the other MapViewController in your storyboard.
So what you want is to load that MapViewController that you've already set up from the storyboard. Assuming you are doing this from within a method in another view controller loaded from the same storyboard already, you can just do this:
// within some method on another vc from a scene in the same storyboard:
// given an identifier for the map view controller we want to load:
static NSString *mapVCIdentifier = #"SomeAppropriateIdentifier";
NSLog(#"Storyboard: %#",self.storyboard); // make sure this vc(self) was loaded from a storyboard
MapViewController *mapVC = [self.storyboard instantiateViewControllerWithIdentifier:mapVCIdentifier];
[self presentModalViewController:mapVC animated:YES];
And then back in the storyboard, just make sure you set the identifier for this map view controller to "SomeAppropriateIdentifier".
Hope that helps.

presentModalViewController on Parent from UITableView inside UIViewController

This one is probably something simple, still learning the ins-and-outs on this but I've run out of searches for this one with no available answer.
I've got a UIViewController with several elements displayed on it, one such element is a UITableView. The UITableView has it's own class and is allocated in the UIViewControllers viewWillAppear
- (void)viewWillAppear:(BOOL)animated
{
UITableView *insideTableView = [[UITableView alloc] init];
tableView.delegate = insideTableView;
tableView.dataSource = insideTableView;
}
Everything is working fine in regards to the tableview. Today I am experimenting with a few additions, one of which is a new view popup on cell selection within that tableview.
Inside my TableView Class, I have the following:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Cell Pressed, Present View");
PopupView *popupView = [[PopupView alloc] initWithNibName:#"PopupView" bundle:nil];
popupView.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:popupView animated:YES];
}
Now it gets called fine, verified by the NSLog, however the view doesn't appear. I know the problem is related to the fact that I want PopUp to appear over the TableViews Parent rather than itself.
I'm just not sure how to properly call it in this instance.
The delegate is a UIViewController which doesn't have its view property set, which is why presentModalViewController:: doesn't work.
You need the view controller containing the table view to present the modal view controllers, but note that that view controller is not the parent of the table view delegate. This is because you have no view controller hierarchy in place.
The easiest way to fix this is to put those methods inside the view controller whose view contains the table view. Alternatively the table view delegate needs to hold a reference to the view controller so it can call presentModalViewController:: on it.
The latter approach can lead to retain cycle, so you have to use a non-retaining reference. The nicest implementation is the delegate pattern.
Also, you don't want to do the instantiation in viewWillAppear: because that can be called multiple times during the lifecycle of a view controller. Put the code in viewDidLoad and balance it in dealloc. Right now you are leaking memory every time your view appears, which when your modal view controller is working will be every time the modal view controller is presented and dismissed.