In my nib file I have several controls that I placed with xcode. Is there a way I can find how many outlets there are in the nib file. since I placed all the objects in the default view of the nib file maybe I can get the child of the default view and those will be the IBOutlets. I plan on latter adding functionality to those IBOUtlets.
In short I am trying to create a connection with code.... Thats cause I have so many objects in every nib file. I am creating an app that is like a power point presentation and I have several slides..
IBOutlets arent' actually anything. That is just syntactical sugar to allow Interface Builder introspection into your code files to match up variable names.
Anyways, you can just look through all the subviews of your view in code if you need to. Not ideal, but i think this is what you are asking for.
SubclassedViewController *controller = [[SubclassedViewController alloc]
initWithNibName:#"SubclassedViewController" bundle:nil];
// actually, [[... alloc] init]; does the same
for (UIView *aView in [controller.view subviews]) {
// do stuff
}
You could identify the Views with tags, an integer properties that you can attach to any UIView (also in Interface Builder).
Related
I am making a custom widget that I would like to use in multiple nibs. So I make a new view nib Screen3, add some buttons, and now want my UIAwesomeSauce widget.
If I just add a view and then change the Class Identity, it doesn't get the subelements from the UIAwesomeSauce nib. Same thing if I go to the Library and switch to Classes. It seems only a UIViewController has the field for "Load from nib", which would be beautiful.
I know I can load the UIAwesomeSauce nib from code, get the top level objects, and place it by hand. But the point of IB is so you don't have to place things in code. Even better would be if I could get UIAwesomeSauce to show up in the Library list itself.
SOLVED - by Nimrod - READ ON FOR EXPLANATION AND CODE
Anyway, dood, that is great. I can make my own widget classes now for goofy Awesome stuff. Make the FileOwner of your UI nib your class, and in it just have a normal UIView with all your stuff. (The single view in the widget's nib can't be the class itself, or you get recursive in initWithCoder.) Then in the nib you want to use it, make a vanilla UIView and change its class. You won't be able to actually see the widget inside that square, but deal.
Self is now a blank view, and tMyActualSelf is the single view that you did the work in in the other nib. Yay!
- (id)initWithCoder:(NSCoder*)coder
{
if ((self = [super initWithCoder:coder]))
{
UIView *tMyActualSelf = nil;
// Initialization code
NSArray *tNibItems = [[NSBundle mainBundle] loadNibNamed:#"UIAwesomeSauce" owner:self options:nil];
for (id object in tNibItems)
{
if ([object isKindOfClass:[UIView class]])
tMyActualSelf = (UIView *)[object retain];
}
if( tMyActualSelf )
{
tMyActualSelf.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
[self addSubview:tMyActualSelf];
}
}
return self;
}
I feel your pain when it comes to the UIViewController being the only thing you can specify a nib load with, but I think it's because there has to be a File's Owner that's a controller, or something like that, though I'm not sure why a UIView can't be a File's Owner.
I think you can put things in the library under Custom Objects but I think you'd be limited to putting a UIView in there with the class just set to a custom subclass of UIView. I actually looked into trying to extend interface builder and it looks like you can do it for OS X stuff, but not for iPhone stuff. It involves compiling code for the new widget in interface builder to tell it how to draw the widget just within IB. I think there's actually an OS X template for this.
I think what I'd probably do is create a main view in a nib file that contains all the subviews, then in initWithCoder: in the UIAwesomeSauce thing open the nib file, load the main view, and just place it inside self so that you have an "unnecessary" view between the UIAwesomeSauce view and the subviews (or sub subviews as the case would be.) I'm not sure how inefficient that is, but in messing with some stuff it looks like some views like UIWebView seem to have one of these seemingly unnecssary intermediate views (IIRC).
If you come up with a better solution please tell me because I'd like to know too.
EDIT
Now that I look at it, I think the UIView CAN be the file's owner, you just can't set it up in IB. Basically you'd call the nib load something like this in the UIView subclass:
[bundle loadNibNamed:#"UISpecialSauce" owner:self options:...]
I think if you create outlets in UISpecialSauce or whatever then they should be assigned correctly because of the owner:self.
Theres a cocoapod that might actually help with that:
https://github.com/mobilejazz/NibWrapper
It provides a wrapper view that loads a child view from another NIB. You can specify the name of that NIB inside interface builder via a runtime attribute.
I have an iphone app that uses table view as the interface. Each time that the user taps on one of the table cells, I want to show another window to the user. However the user interface of the window that I push into the navigation controller are extremely similar. Therefore I decided to make a "generic nib file" to be used across all the subclasses of the file owner of this generic nib file.
However what I'm confused (and what's not working) is that I can't seem to be able to customise this generic nib file. I have this code at the initialisation of the files:
In the .h file:
#import <UIKit/UIKit.h>
#import "primeViewController.h"
#interface subclass1 : primeViewController { //subclassing from a generic view controller that owns a generic nib file
}
In the .m file:
#import "subclass1.h"
#implementation subclass1
- (id) initWithLabelAndButton {
if(self = [super initWithNibName:#"primeViewController" bundle:nil]) {
self.label.text = #"Title of the first subclass";
}
return self;
}
But then when I instantiate the class in the table view:
//somewhere in rootviewcontroller.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0:
{
checkPrime* checkPrimeViewController = [[checkPrime alloc] initWithLabelAndButton];
[self.navigationController pushViewController:checkPrimeViewController animated:YES];
[checkPrimeViewController release];
break;
}
default:
break;
}
}
So can anyone tell me what I'm doing wrong? Or am I wrong assuming that xcode allow me to use nib file multiple time across its subclasses? I don't see why I can't do it, but I can't figure out how...
When nib file is loaded, it creates view controller of exactly the same class that is written in .xib (UIViewController, or PrimeViewController, whatever else). What actually is saved in your .nib file?
You'll succeed if you will store UIView and all the corresponding objects in xib, and will load only them by using [[NSBundle mainBundle] loadNibNamed:#"YourNibFile" owner:class1ViewController options:nil], while view in nib is connected to the corresponding File owner outlet.
If you put all your views in one NIB, then when your application launches, it has to load the entire NIB into memory and construct all of the objects for all of the controls, and this takes a lot of time.
If you separate your views into separate NIBs, then your app will start up much faster, because only the NIB containing the initial view will be loaded. Then, when the view changes, you can load the NIB containing the new view. This will incur a minor hitch when opening a view for the first time.
Also if you're trying to optimize memory usage, you can unload the precious view when switching to a view
I'd suggest subclassing UITableViewController and add any methods, instance variables, and properties common to your different view here. Make your nib and have it have a reference to an instance of one of these objects.
Then, subclass your subclass to get customized functionality, like
GenericSubclassVC* checkPrimeViewController = [[SpecificSubclassVC alloc] initWithNibName:#"GenericNib" and Bundle:nil];
[self.navigationController pushViewController:checkPrimeViewController animated:YES];
You should stick to overriding the designated initializer. Put any custom initialization code into awakeFromNib or viewDidLoad methods, depending on if they need to modify UIView objects.
However, as others have mentioned, it's not terribly efficient or elegant. If all your ViewControllers are table view controllers and have the same data model to display, then there are other ways to get code reuse, like defining a datasource object.
I'm contemplating writing some helper functions to make it easier to do simple changes to the UI elements in my iPhone NIB. Mainly - I want to access a UILabel, or other element, via its Name in Interface Builder. Is this possible? Is there a smarter approach?
Example
Say I want to make a super simple iPhone app that displays 'Hello World'. I start a new project and then open the NIB and drag a UILabel into my view and give it a 'Name' of 'LblMain'. Now, presuming that I've included my handy helper function, I'd like to assign the label some new text something like this:
[helper setText:#"Hello World" forLabel:#"LblMain"];
-or-
UILabel *ObjTmp = [helper getUILabel:#"LblMain"];
ObjTemp.text = #"Hello World";
Now - the trick is that I didn't add a:
IBoutlet UILabel *ObjLblMain;
anywhere in .h file - I'm just accessing that label dynamically - wouldn't that be nice?!
Now, for simple apps, to add some more labels or images, I could drag them into my NIB, assign them names in that element's inspector window, and then immediately access them inside code without the stuttering & hassle of adding them in the .h file.
Motivation
Basically, I'm frustrated that I have to wire every element in my NIB - it's a lot of stuttering and bookkeeping that I'd rather avoid.
I could give a design some naming conventions, and they could generate a NIB without needing to be intimate with the implementation.
Name is 100% not accessible after the object is loaded, something I always thought was odd too.
What is accessible is "tag", if you really want to access an element without defining an outlet you can set the (integer only) "tag" value, and then within the superview that holds the tagged element call viewWithTag: passing in the same integer. Don't forget the default is "0" so use something else.
You can definitely load the NIB programmatically, find all the objects and query them to work out what points to what. Just look at Loading Nib Files Programmatically. But the problem is that the Interface Builder Identity Name isn't exposed outside of IB. So I'm not sure what you would use as the "forLabel" parameter. The "Name" field is just a convenience for the IB document window. It's not a property on NSObject.
It can be done by the element tag:
Lets say you have UIView xib file called "yourView" and inside it there is UILabel that you want to access it without wiring.
Set a tag to the UILabel in "yourView" xib file, lets assume you set UILabel tag to 100.
After loading "yourView" anywhere you can get UILabel without having any wiring by using this code.
UILabel* yourlabel =(UILabel*) [yourView viewWithTag: 100];
//do whatever you want to your label.
I think you can try opening the xib in some external editor as XML and see how the compiler sees it, then you might possibly do the same way
For iOS6+ you can use restorationId instead of tag, to make it more "readable", for example you can set the same name in your nib file and in restoration id.
If you do not want to link all the outlets from your nib to your viewcontroller, you still can access them by searching in your current view subviews tree. Note that subviews arrangement is a tree (the same tree that you can see in your nib file), so you will need to do some recursion if you have nested views.
For example:
UIButton *nibButtonView = nil;
for (UIView *view in [self.view subviews]){
if ([view.restorationIdentifier isEqualToString:#"myNibButtonView"]){
nibButtonView = (UIButton *)view;
}
}
[nibButtonView setTitle:#"Yeah" forState:UIControlStateNormal];
In your nib file you should have a button with a restorationId equals to "myNibButtonView", you can find the restorationId textfield in your identity inspector (third column of utilities)
You may use this if you have a huge number of outlets a you don't want to linked them all.
I have a UIViewController that is loading several subviews at different times based on user interaction. I originally built all of these subviews in code, with no nib files. Now I am moving to nib files with custom UIView subclasses.
Some of these subviews display static data, and I am using loadNibNamed:owner:options: to load them into the view controller. Others contain controls that I need to access.
I (sort of) understand the reasons Apple says to use one view controller per screen of content, using generic controller objects (NSObjects) to manage subsections of a screen.
So I need a view controller, a generic controller, a view class and a nib. How do I put this all together?
My working assumptions and subsequent questions:
I will associate the view class with
the nib in the 'class identity' drop
down in IB.
The view controller will coordinate
overall screen interactions. When
necessary, it will create an instance
of the generic controller.
Does the generic controller load the
nib? How?
Do I define the outlets and actions
in that view class, or should they be
in the generic controller?
How do I pass messages between the
view controller and the generic
controller?
If anyone can point me to some sample code using a controller in this way, it will go a long way to helping me understand. None of the books or stackoverflow posts I've read have quite hit the spot yet.
Okay, I think I figured it out:
Extend NSObject to make your CustomController
Define your outlets & actons in CustomController.h, including a reference to the UIView in your nib
Set the File's Owner of your nib to CustomController
Hook up all your outlets & actions as usual, including the UIView outlet
In your CustomController.m init, load the nib
- (id)init {
self = [super init];
if (self != nil)
[self loadNib];
return self;
}
- (BOOL)loadNib {
NSArray *topLevelObjs = nil;
topLevelObjs = [[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil];
if (topLevelObjs == nil) {
NSLog(#"Error! Could not load nib file.\n");
return NO;
}
return YES;
}
The new NSObject based controller will work very much like a view controller.
It sounds like what you want is what I've coined "reusable UIView widgets" -- reusable widgets that do something / present a display that you can incorporate in your app screens wherever and how many times as you want -- you can create them purely in code or instantiate them by placing their frame in another xib file (but you don't get to change the widgets' internal parameters in the xib file, that would require an IB plugin).
This is an organization that I've never seen discussed anywhere. Part of my frustration early on with iOS programming is wanting something like this but not seeing any way to express it in any example.
See my answer in this question to see how it can be structured:
UIView and initWithFrame and a NIB file. How can i get the NIB file loaded?
I recommend placing all widget-internal handling of direct/low-level events in the uiview widget subclass, and implememnt a delegate protocol for a highler level interaction with the widget client (i.e., "loginRequested:userName:passWord" instead of manually accessing button and textfields internal to the widget).
The (optional but recommended) xib file for the widget has an owner of the widget, and the init code in the widget owns the duty of loading the xib file. The customer of the widget simply instantiates the widget and implements whicever widget delegate functions makes sense for it.
In my application,I need to load different .xib in different tableView cells depending upon the category of data which I'm getting from parser. I wanted to ask that is it possible to create different .xibs belonging to same class as it'll reduce the load as I have almost 13 categories so 13 .xib files.
Thanks in advance.
#"I wanted to ask that is it possible to create different .xibs belonging to same class as it'll reduce the load as I have almost 13 categories so 13 .xib files."
The xib files are not a burden on memory unless they are loaded, in which case, the file's owner object is created. So keeping this in mind, it doesnt matter how many nibs you have for your class, for an object of each viewController class, the corresponding xib is loaded. So ultimately you have to put in a check condition as stated by RaYell, it would be better to introduce that check where you spawn the viewController object instead checking the condition for loading appropriate xib.
Dont bother about creating 13 viewControllers, you will find it easier to make changes in your project later if there are changes in requirements. You will appreciate this approach.
If you create only one UIViewController sub-class and load one of 13 xib's based on some condition, say, there comes a requirement that you add a button / label / textField in the 13th xib ONLY and need its reference in your viewController class. How would you achieve it, you maintain an IBOutlet in the common viewController class and introduce the if-else check to see if it is the 13th category. The code becomes untidy with lots of if else conditions.
If you mean that you'd want to have several NIBs for the same view controller then it's most certainly possible. In fact that's how application localization is done. You can then load the specific NIB when you initialize your controller.
NSString *nibName = #"DefaultNibName";
if (someCondition) {
nibName = #"SomeOtherNib";
}
YourViewController *controller = [[YourViewController alloc]
initWithNibName:nibName bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
But how will you make connections(outlets) which will be different in different .xib files?
will you keep lot of outlets and actions in a single controller? If so, then think accidenlty you try to access the outlet which is suppose to be of some other nib. Then what will happen?
If you try to do so then you view controller will look like a garbage. So please dont try to use only one controller for loading more than one .xib files.