List all UIViewControllers of project - objective-c

For automation of screenshot capture, and for testing purposes, I would like to list all UIViewController classes of my project.
I do not wish to get a list of all UIKit view controllers, I just want the ones I created in my project.
I want to do this in Objective-C code, at runtime, because I then will need to instantiate some of the listed classes.
For example, in my unit tests, I might want a test that assert that all UITableViewCell subclasses return the same height that the height of the item in the xib associated, and this object is not a UIView subclass but a UITableViewCell subclass.
An other intended use is to add to the documentation of the project a screenshot of all my UIViewController classes.
Note that this code will not be shipped to customer. It will only be used in testing and scripting on the developer machine.
I guess I could parse the files included in pbxproj, but that feels wrong and not robust.
A simple ls *ViewController.h on my project works too, but same feeling about it.
Any other idea?
Bonus if I can then extend this way on other classes, to for example get all the UITableViewCells I created, or all UIViews.

Using my NSObject+Subclasses category, you can easily get all subclasses of UIViewController.
To get your view controllers only, filter them like this:
NSSet *myViewControllerClasses = [[UIViewController subclasses_xcd] filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [[NSBundle bundleForClass:evaluatedObject] isEqual:[NSBundle mainBundle]];
}]];
Then you can do whatever you want with the content of myViewControllerClasses that contains Class objects.

Using the runtime, it's a bit hard. You can use the definitions of class_t and class_rw_t (data-member of class objects) to explore subclass trees efficiently.
To filter your classes, you might need to look at the beginning of the class names (prefix) or maybe a base VC class if have created one for your project.
See this article

Related

Is there design pattern for showing same view controller in several places with some customization?

Very frequently we reuse same view controllers when developing universal apps both for iPhone and iPad. But frequently some customization is needed, like:
IF iPad THEN
...
ELSE
...
So, in order to achieve such customization the controller might have some property that is set during initialization of the controller, or there might be custom constructors. Just curious is there design pattern that suites for such situations.
Don't.... :) Use a common class called for instance MyClass and then sub-class it MyClass-iPad & MyClass-iPhone and use two different XIB for each. Avoid using this kind of stuff (there is no need for it).
Explanation:
The iPad version should only be aware of classes of the type Something-iPad this makes the code clean and creates a well defined architecture. If I jump into your code and someone tell's me: "Ok Jacky Boy, you have to make changes on the iPad version". I won't care to look ath the Something-iPhone classes. Most of the logic (business) should be on super class Something where the small tweaks should be on the sub-classes.
On side note, on most of my projects, normally I don't have anything on the Something-iPhone classes, because the design is done on the XIB. On the Something-iPad I would normally keep a reference to a UIPopOverController (just an example) and some rotations tweaks.
Edit 1:
I would receive an NSDictionary on the init of the UIViewController, like this:
initWithNibName:bundle:configurationDictionary:
After receiving this configurationDictionary, I would then use it on the viewDidLoad (for example). You could then do some cool stuff like this:
- (void)viewDidLoad
{
[[self view] setBackgroundColor:[[self configurationDictionary] objectForKey:BACKGROUND_COLOR_KEY]];
}
If you have different initializers or larger chunks of different functionality then it makes sense to define a base class with the core functionality and then an iPad-specific subclass and an iPhone-specific subclass.
But in cases where you only have a trivial difference (for example, displaying an action sheet), then I would simply use something like:
- (void)someMethod {
// a bunch of stuff that is the same
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// one or two lines for iPad
} else {
// one or two lines for iPhone
}
}
I have plenty of situations where I do both - subclass for the bigger differences and use UI_USER_INTERFACE_IDIOM for trivial differences in the base class.
If you need the same VC in N places but each time initialized a little differently I would move the logic in a specific factory class / category on the VC
like its done with for example SLRequest objects / in ShareKit

IOS: is it possible to bind one story board view controller to several different classes?

I know it's possible to attach a custom view controller class to several different view controllers on a storyboard, but can it be done in the other direction; that is, depending on the situation, I want to bind different custom classes to a single view controller on the storyboard that will be instantiated using:
[self.storyboard instantiateViewControllerWithIdentifier:]
background: I used to have several view controllers on my storyboard that are almost identical. In fact, the custom classes that they each bind to are very similar as well. In an effort to clean this up, I refactored my custom classes into one base class and several subclasses. I then removed all the similar view controllers from the storyboard leaving only one which I've bounded to my base class. I then call:
MySubclass* mySubclass = [self.storyboard instantiateViewControllerWithIdentifier:#"StoryboardControllerBoundToBaseClass"];
Unfortunately, my subclass code is being ignored and only the base class code is ever run. Does anyone know how I can make it work without duplicating view controllers on the storyboard and binding each one to a different subclass?
It's not possible. Just because you say MySubclass *object = something doesn't magically convert object into a MySubclass object. It's stored in the storyboard with whatever class was assigned at storyboard compile time.
Rather than using subclassing, I figure I can reuse a view controller on the storyboard by using a delegate/proxy model. More specifically, I can bind the storyboard's view controller to a custom class that delegates all of its methods/events to others classes to handle. This isn't as elegant as subclassing but at least I can keep my storyboard leaner, not having to keep several copies of pretty much the same view controller. Plus, I won't need to duplicate future changes to every copy of these controllers to boot.
As guylegend writes. Apple doesn't support the way to do that. There are many workarounds e.g. with delegates but I finally found the answer and answered in another topic. Hope it helps!
https://stackoverflow.com/a/32103618/1943053

IOS - The correct way for using #import classes

I am writing a test project to learn to write everything in code. I want to do this way so that I can better understand what the Elements in Storyboard do for me behind the scene's
In my project I have several UIViewControllers which share the same UI elements. For Example I have a UITool bar at the top of the screen with several buttons in it.
Now I want to put that UIToolbar in a separate class so that I do not have to write it all out or copy n paste it in every controller.
Now I know I can achieve this by doing this:
#import "General_Add_ons.h" //the UIToolbar is properly set up in this file
#interface FirstViewController : General_Add_ons
and then I just use [self topToolBar]; //topToolBar is the name of the toolbar
Now I just want to clarify is this the best and or only way to this?
Somehow I feel I should be able to do this:
General_Add_ons *gao = [[General_Add_ons alloc] init];
[gao topToolbar];
Am I thinking about this the wrong way? The first way seems to be limiting in case I have multiple external classes.
Thanks in advance.
Your approach seem correct. If you have several UIViewController instances that need the same UI elements or other controller code, you can put those elements in a UIViewController subclass. In your case, I believe the class General_Add_ons is the UIViewController from which your subclasses will inherit.
This class (your General_add_ons) should have a property for the reusable toolbar, e.g.
#property (nonatomic, strong) UIToolbar *toolbar;
As an aside, class names in Cocoa, by convention are: prefix+capitalized words without underscores. Apple has a great reference on naming conventions.
EDIT
For clarification, you can subclass your custom subclass as many times as you need. For example in my code, I have a class CCFViewController that encapsulates common properties and behaviors that I want all of my view controllers to have. In the project, then, all of the view controllers inherit from that parent controller. Similarly, your toolbar will live in the superclass and the other controllers inherit from it.
I am not 100% sure but i think you should try it this way:
General_Add_ons *gao = [[General_Add_ons alloc] init];
[gao.topToolbar anypossiblemethodcall];

Why can't new ObjC classes descend from UIViewController?

So, I've been making iOS apps since the first iPod touch came out, but something has always flabbergasted me; why is the list of new Cocoa Touch classes restricted to subclasses of NSObject, UIView, and UITableView? I routinely make subclasses of UIImageView and UIViewController.
Am I "Doing It Wrong™?" Have I totally misunderstood MVC to the point where I make Controller classes where I shouldn't? What is the philosophical reasoning for requiring classes to never descend from a basic controller class?
What gives you the idea that you aren't supposed to subclass UIViewController? This is directly from the documentation for UIViewController:
In a typical iPhone application, there is usually at least one custom subclass of UIViewController and more often there are several.
The C of MVC is supposed to be the least re-usable part it's whole job is to mediate between M & V. If you find something that is in the C section of your code that you have to copy and paste into several subclasses of a given object or into several projects that code should be moved elsewhere.
If you are just basing this off the fact that there is not a nice popup menu item that says UIViewController, don't worry about it Apple has just not bothered to write a template file for that class yet.
Uhm... maybe it's just me, but I see a UIViewController subclass template when I choose new File.
UIViewController template http://files.me.com/aclark78/obnp83
Like #theMikeSwan says, there simply aren't GUI templates for this when you create a new class in Xcode GUI. But you can always create a new subclass whose parent is initially NSObject. After that, you just go to your code and change the parent class to whatever you like.
So... no, you are not doing it wrong in the sense that you rightly understand that often you want to subclass UIViewController; but yes, you are doing it wrong since you assume you shouldn't do this only because Xcode GUI does not support it :)

What would you do instead of using NSViewController to be compatible with 10.4?

All I need to do is load and swap some nibs in a NSView of a window. I know how to do it with NSViewController and have it working perfectly with 10.5-10.6, but I don't know what to do for 10.4.
Tutorial links very welcome, I have trouble finding legacy stuff.
(Yes, I really do need to support 10.4.)
From working with NSViewController in Leopard, I can tell you that its functionality is very basic, and that you should be able to replicate it with fairly minimal effort.
Essentially, it has a view property/outlet, and an initWithNibName:bundle: method. Beyond that, it doesn't do anything especially fancy. It has some convenience things, like adopting NSEditor, and a representedObject property. You should be able to bang out an equivalent class in an hour or two.
Now, what you will give up if you do this is compatibility with later versions of Cocoa. Eventually, you'll probably drop 10.4 support and you'll be left with your class and the real NSViewController. When that happens, I'd recommend re-basing your custom view controller on Cocoa's NSViewController. If you've named the properties with the same names/data types as NSViewController, you should only have to drop the properties and methods you've declared yourself.
Use NSBundle to load the nib:
YourController *controller = [[YourController alloc] init];
BOOL success = [NSBundle loadNibNamed:#"YourNibName" owner:controller];
Basically, you write your own controller class that does the same things that NSViewController does. The controller classes were added to the AppKit because so many of us were writing essentially the same code over and over.