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

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.

Related

How to create simple table view controller in code only

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.

Should I not override -[UIViewConvtroller view]?

I'm told not to override -(UIView *)view ever. Why not? I want to use a custom UIView subclass for my custom UIViewController. What's a better way to do so?
You're not supposed to override -view. To accomplish the behavior you want, you can set up your view controller to load from a nib, or you can override the -loadView method. In the latter method you create whatever view you want and assign it to self.view.
If you're using a nib or storyboard, simply set the root view's class to your new, custom UIView subclass. If you're not using a nib or storyboard, create your custom subclass in the -loadView method and set the view controller's view property to it. So, for example, if you had a custom UIView subclass named MyView, and you're creating it in code (not in a nib or storyboard), you'd do something like:
// This code sample assumes compiling with ARC
- (void)loadView
{
// You should adjust the initial frame to be whatever's appropriate for this
// view controller
MyView* view = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 360, 480)];
[self setView:view];
}

UIViewController and UIImagePickerController: Unable to create and managing views as expected

I have a UIViewController subclass that contains an instance of UIImagePickerController. Let's call this controller CameraController. Among other things, the CameraController manages the UIImagePickerController instance's overlayView, and other views, buttons, labels etc. that are displayed when the UIImagePickerController, let's call this instance photoPicker, is displayed as the modal controller.
The photoPicker's camera overlay and the elemets that are part of the CameraController view hierarchy display and function as expected. The problem I'm having is that I cannot use UIViewController's default initializer to create the CameraController's view heirarchy.
I am initializing CameraController from within another UIViewController. Let's call this controller the WebViewController. When the user clicks on a button in a view managed by WebViewController, the launchCamera method is called. It currently looks like this:
- (void) launchCamera{
if (!cameraController) {
cameraController = [[CameraController alloc] init];
// cameraController = [[CameraController alloc] initWithNibName:#"CameraController"
// bundle:[NSBundle mainBundle]];
cameraController.delegate = self;
}
[self presentModalViewController:cameraController.photoPicker animated:NO];
}
I want to be able to create CameraController by calling initWithNibName:bundle: but it's not working
as I'll explain.
CameraController's init method looks like this:
- (id) init {
if (self == [super init]) {
// Create and configure the image picker here...
// Load the UI elements for the camera overlay.
nibContents = [[NSBundle mainBundle] loadNibNamed:#"CameraController" owner:self options:nil];
[nibContents retain];
photoPicker.cameraOverlayView = overlay;
// More initialization code here...
}
return self;
}
The only way I can get the elements to load from the CameraController.xib file is to call loadNibNamed:owner:options:. Otherwise the camera takes over but no overlay nor other view components are displayed. It appears that a side-effect of this problem is that none of the view management methods on CameraController are ever called, like viewDidLoad, viewDidAppear etc.
However, all outlets defined in the nib seem to be working. For example, when the camera loads a view is displayed with some instructions for the user. On this view is a button to dismiss it. The button is declared in CameraController along with the method that is called that dismisses this instructions view. It is all wired together through the nib and works great. Furthermore, the button to take a picture is on the view that servers as photoPicker's overlay. This button and the method that is called when it's pressed is managed by CameraController and all wired up in the nib. It works fine too.
So what am I missing? Why can't I use UIViewController's default initializer to create the CameraController instance. And, why are none of CameraController's view mangement methods ever called.
Thanks.
Your problem is easy but need some steps.
Well... First, if overlay is an IBOutlet, it can not be loaded at init time. So move picker and co in viewDidLoad. Place also here all other items that your say that they are not loaded. They should be loaded there (viewDIDLoad). Check that outlets are connected.
Second, call
cameraController = [[CameraController alloc] initWithNibName:#"CameraController"
bundle:nil];
and ensure that CameraController contains (just) a view, and CameraController inherits UIViewController. Check also file's owner.
And at some time, you may consider that calling :
[self presentModalViewController:cameraController.photoPicker animated:NO];
does not make the CameraController control your picker. Does that make sense to you ?
What does that do regarding your problem ?
It seems you are confusing some things. I try to explain in another way :
The one that controls the picker is the one that is its delegate. Your may consider creating in a MAIN view.
The controller of the overlay (added as subview) is the one that own its view in File's Owner. That may be created from the MAIN view, adding its view as subview of the controller. Basically, it is loaded just to get the overlay, but viewDidLoad, ... won't be called.
That's all and I belive those steps are not ok in your code.
That should give something like :
MainController
Loadcamera {
self.picker = [UIImagePicker alloc] init.....];
self.picker.delegate = self;
SecondController* scnd = [[SecondController alloc] initWithNibName:#"SecondController" bundle:nil];
[self.picker addOverlay:scnd.view];
[self presentModalViewController:self.picker animated:NO];
}
/// And here manage your picker delegate methods
SecondController
// Here manage your IBActions and whatever you want for the overlay

UIViewController with no Xib showing blank when using pushViewController

I have subclassed UIViewController like so:
#interface MyClass : UIViewController
I dont have a xib file for the controller, instead I would just like to use a blank UIView and I will layout elements programmatically on that. The problem arrises when I try and push this view controller.
MyClass * thing = [[ImageGallery alloc] init];
[self.navigationController pushViewController:thing animated:YES];
A new title bar is animated in, but there is no view, its see through, so I end up seeing a static background I set on my main "window/view". How should I properly subclass a UIViewController without a xib?
What you have described is correct behaviour so far.
You will want to override the -(void)loadView method, and after the [super loadView]; call, you can set your background colour and begin to place the objects in programatically, that you desire.

Subclassing in objective c and viewWillAppear message delegates?

I might be confused here and asking the wrong question.
If I use a class like the UISplitViewController inside the appdelete.m, will the only message i will receive is the message the UISplitViewController calls and not any VIEW message? for example:
in my myappdelegate.m
....
UISplitViewController *mySplitViewController = [[UISplitViewController alloc] init];
mySplitViewController.viewControllers = [NSArray arrayWithObjects:leftside,rightside,nil];
...
mySplitViewController.delegate = self;
....
[windows addSubView:mySplitViewController.view];
....
-(void) viewWillAppear:(BOOL) animated {
}
in myappdelegate.h I included UISplitViewControllerDelegate
I expected viewWillAppear to fire but it is not. I assume if I had subclass UISplitViewControler it would have fire. right?
BTW: I am doing this without using IB. Do I need to set the target for the mySplitViewController?
What I want to do is setup the orientation of the splitviewcontroller when it rotates.
the viewWillAppear method and other view related methods will be called on the view or view controller themselves, not on the delegate.
That means that if you make a subclass of UISplitViewController called SplitViewControllerSubClass, the view... methods will be called on the instance of SplitViewControllerSubClass, not on the delegate object.
But considering you are creating the views and displaying them programmatically, you already know exactly when the view will appear (i.e., right before you add it to the window), so I believe you could do whatever setup you want at that point.