How to create simple table view controller in code only - objective-c

I need the same type of table view controllers in my app many times and would like to create a more generic table view controller which I can use over and over again.
These table view controllers are quite simple and show only the contents of an array, put a check mark to the selected table view cell and return the index of the selected table view cell to the calling view controller after the Done button in the toolbar has been tapped.
Currently I create each one of these table view controllers directly in Storyboard and instantiate them by using segues.
Would it be possible to do this in code only (without using Storyboard or xibs)?
What would be the best way to instantiate and push them onto the navigation controller stack (each one will be shown in a view controller).

It's trivial to do this in code. You create your view controller class just like you normally would (extend UITableViewController). Implement all of the same table view data source and delegate methods. All of that is the same.
When you want to use the table view controller you just do:
MyTableViewController *vc = [[MyTableViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
I would define your MyTableViewController init method like this:
- (id)init {
if ((self = [super initWithStyle:UITableViewStyleGrouped])) {
// any other initialization
}
return self;
}
BTW - I have an app with over 100 view controllers in it and I've never used Interface builder or storyboards. It's all code.

Would it be possible to do this in code only (without using Storyboard or xibs)?
Yes. Anything you can do in a storyboard or .xib file, you can do in code:
MyViewController *vc = [[MyViewController alloc] initWithNibName:nil bundle:nil];
Note: the default behavior for a view controller is to load its view from a .xib with the same name as the view controller's class when you pass nil for the .xib name, e.g. MyViewController.xib for the example above. So the line above creates the view controller in code, but will still load the view from the .xib. If you want the view created programmatically as well, override -loadView.

Related

How to addsubview to a view on class1 from class2?

I'm trying to do a really simple thing - I've got a main Xib file for the whole app and another Xib file for a small view.
I want the small view (Xib called "additionalView.xib") to appear in the first Xib ("ViewController.xib").
I have succeeded to do so in the "ViewController.m" but I want more - I want to do it from "additionalView.m"
There is a method I created called "openView:" in "additionalView.m" and it looks like this:
-(IBAction)openView:(id)sender
{
ViewController *vc = [[ViewController alloc] init];
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"additionalView" owner:self options:nil];
UIView *nibView = [nibObjects objectAtIndex:0];
[vc.view addSubview:nibView];
}
The method is being called and the lines are being read by the debugger - but nothing happens.
No crash - No error - No small view in bigger view.
Why is that?
I know that the last line is probably what's
screwing everything up but i don't know how to put it correctly.
Your problem is that ViewController *vc = [[ViewController alloc] init]; creates a new view controller. Because it's new, it's not the one that already exists in the view controller hierarchy that's managing the display.
Your method needs to access the existing view controller. How it does that depends on your app's structure and which object has a reference to the original controller object.
Try
[self.view addView:view.vc];
However, I'm not sure what is you view structure here. You say your -(IBAction)openView:(id)sender is in your "additionalView.m", but it is not the main view controller, correct? You need to do this in the main controller, so basically move the openView: method to your ViewController.m
And you normally need a separate view controller for each view to keep things neat and separate, so the additionalView.m should be an instance of UIViewController, which you can then create from your main view as follows:
-(IBAction)openView:(id)sender
{
AdditionalView *vc = [[AdditionalView alloc] initWithNibName:#"additionalView"];
[self.view vc.view];
}
You have options ... First you don't need to create a view controller vc if you just need the view . Create a uiview and add it .
Option 1: pass a ref to the app vc as suggested above and then :
[appVC.view addsubview:additionalView]
This will add it to main.
Use a view controller manager / ref in the app delegate that you can refer to as delegate and add your view to the current showing view.
Hope this helps

Accessing an instance method of a ViewController From Another view controller

Let's say I have a view controller called vc1, which a synthesized property called property1, and i wants to access it from another view controller (vc2) and change it from vc2.
Now the methods created by the #syntisize to change and get properties are instance methods, so how can I get to them fro another view controller (do view controllers have instances in the app, and if so, what are they?)
Just to be clear I am using storyboards, so I never really instantiate the view controllers...
VC1.m:
-(void) yourMethod {
...
}
VC2.m
YOURViewController * vc2 = [[YOURViewController alloc]init];
[vc yourMethod];
[vc release];
Make sure to import your YOURViewController in your other view .m file
Something like that should work.
Or if you're having problems, try this tutorial here:
Tutorial on How-To Pass Data Between Two View Controllers
Hope this helps :)
While you can do it the way you describe, I think the common technique (assuming VC1 has a segue to VC2) is a bit different, where VC2 will have a property that will be set by prepareForSegue. See Configuring the Destination Controller When a Segue is Triggered in the View Controller Programming Guide.
You will need to link the storyboard views with the viewcontrollers so the view for vc1 would use the class vc1 etc for the rest (I assume you have done this because this is important when coding for different views)
Then all you need to do is where ever you are calling the properties so lets say the viewDidLoad method, declare the view controller like this:
- (void) viewDidLoad {
vc1 *viewController;
// Now you change the variable I'll presume its a UILabel so I'll change its text [viewController.property1 setText:#"I changed a different views UILabel"];
}
Let me know whether this works... Its worked for me before so should work

In storyboard, how to assign a view controller's subview a different delegate/controller m file?

In my storyboard, I have a view (a subclass of UIScrollView called AQGridView) which is nested inside a view controller scene. I want to assign the AQGridView a controller delegate and datasource which is defined in a separate .m and .h file than the parent view controller. How do I do this? Storyboard only lets me drag connectors to AQGridView's parent view controller.
EDIT:
I've tried doing something in the parent view controller like this (where myGrid is an IBOutlet pointing to the AQGridView and myGridController is a property of the parent view controller):
- (void)awakeFromNib
{
// note: kzMyGridController is a subclass of AQGridViewController
myGridController = [[kzMyGridController alloc] init];
myGrid.delegate = myGridController;
myGrid.dataSource = myGridController;
}
But it doesn't appear to be working because none of its delegate methods are being called. What am I doing wrong?
If you are not allowed on the StoryBoard, just do it on the code. It makes more sense, because it actually forces you to have an object (the delegate) and set it when you need it.
It should be:
- (void)awakeFromNib
{
// note: kzMyGridController is a subclass of AQGridViewController
myGridController = [[kzMyGridController alloc] init];
myGrid.delegate = myGridController;
myGrid.dataSource = myGridController;
}
The answer was that I had to call reloadData on the AQGridView. The view is created before its delegate, so its initial data getting methods fire without a receiver.

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.

iPhone subview design (UIView vs UIViewController)

I'm designing a simple Quiz application. The application needs to display different types of QuizQuestions. Each type of QuizQuestion has a distinct behavior and UI.
The user interface will be something like this:
alt text http://dl.getdropbox.com/u/907284/Picture%201.png
I would like to be able to design each type of QuizQuestion in Interface Builder.
For example, a MultipleChoiceQuizQuestion would look like this:
alt text http://dl.getdropbox.com/u/907284/Picture%202.png
Originally, I planned to make the QuizQuestion class a UIViewController. However, I read in the Apple documentation that UIViewControllers should only be used to display an entire page.
Therefore, I made my QuizController (which manages the entire screen e.g. prev/next buttons) a UIViewController and my QuizQuestion class a subclass of UIView.
However, to load this UIView (created in IB), I must[1] do the following in my constructor:
//MultipleQuizQuestion.m
+(id)createInstance {
UIViewController *useless = [[UIViewController alloc] initWithNibName:#"MultipleQuizQuestion" bundle:nil];
UIView *view = [[useless.view retain] autorelease];
[useless release];
return view; // probably has a memory leak or something
}
This type of access does not seem to be standard or object-oriented. Is this type of code normal/acceptable? Or did I make a poor choice somewhere in my design?
Thankyou,
edit (for clarity): I'd like to have a separate class to control the multipleChoiceView...like a ViewController but apparently that's only for entire windows. Maybe I should make a MultipleChoiceViewManager (not controller!) and set the File's Owner to that instead?
You're on the right track. In your QuizController xib, you can create separate views by dragging them to the xib's main window rather than to the QuizController's main view. Then you can design each view you need according to your question types. When the user taps next or previous, remove the previous view and load the view you need based on your question type using -addSubview on the view controller's main view and keep track of which subview is currently showing. Trying something like this:
[currentView removeFromSuperView];
switch(questionType)
{
case kMultipleChoice:
[[self view] addSubview:multipleChoiceView];
currentView = multipleChoiceView;
break;
case kOpenEnded:
[[self view] addSubview:openEndedView];
currentView = openEndedView;
break;
// etc.
}
Where multipleChoice view and openEndedView are UIView outlets in your QuizController connected to the views you designed in IB. You may need to mess with the position of your view within the parent view before you add it to get it to display in the right place, but you can do this with calls to -setBounds/-setFrame and/or -setCenter on the UIView.
Yeah, IB on iPhone really wants File's Owner to be a UIViewController subclass, which makes what you want to a bit tricky. What you can do is load the nib against an existing UIViewController instead of instantiating one using the nib:
#implementation QuizController
- (void) loadCustomViewFromNib:(NSString *)viewNibName {
(void)[[NSBundle mainBundle] loadNibNamed:viewNibName owner:self options:nil];
}
#end
That will cause the runtime to load the nib, but rather than creating a new view controller to connect the actions and outlets it will use what you pass in as owner. Since we pass self in the view defined in that nib will be attached to whatever IBOutlet you have it assigned to after the call.