Objective-c - Getting objects from XIB file into a view controller - iphone

So I have a XIB file, which contains a view. In that view, I want to add custom objects and then being able to get those objects, and use them in a view controller I have where I crate other things programmatically.
My XIB file is called "MyXibLibrary.xib"
My view controller where I want the objects to be added to is called "ContactDetailsViewController"
My view controller is being pushed from a UITableViewController like this:
ContactDetailsViewController *detailViewController = [[ContactDetailsViewController alloc] init];
And inside my ContactDetailsViewController viewWillAppear I have this code to get the XIB objects:
UIView *xibView = [[[NSBundle mainBundle] loadNibNamed:#"MyLib" owner:self options:nil] objectAtIndex:0];
[self.view addSubview:xibView];
Now, a for instance, the loadNibNamed property should be? Name of the XIB file? Name of the view in that XIB file? Or what?
All this is bringing me errors and the app trows exeption.
I have no clue what so ever on how to work with XIB files since I super new to Objective-c coding.
Any help would be really appreciated!!!
Thanks!

The XIB, which is referred to as a NIB (as a matter of history), defines at least one view that is to be "controlled" by a view controller. This view can represent the whole user interface or simply a subview of another view (e.g. your XIB could represent a reusable table row). Thus, you should not be using a XIB as a sort of container for pre-built interface elements in the manner you describe.
However, it is simple to work with the components of the XIB provided your controller knows about them. That is, the elements of your XIB should connect to properties of your view controller class.
For example, let's say you have the following view controller interface:
#import <UIKit/UIKit.h>
interface MyViewController : UIViewController {
}
#property (strong, nonatomic) IBOutlet UITextView *textEntry;
#property (strong, nonatomic) IBOutlet UIButton *enterButton;
A corresponding NIB would be named MyView.xib. In the interface builder, you would set the "File's Owner" for the NIB to be "MyViewController". You would then link the interface elements, a UITextView and a UIButton, to MyViewController's properties (in whatever method you prefer - usually an option+click & drag from the interface element to the File's Owner object).
Having done this, you can then instantiate the view controller anywhere you please and work with the properties of that object. For example, let's pretend this code is in a file named "SomeOtherController.m":
- (void)aMethodOfSomeOtherController
{
MyViewController *myView = [[MyViewController alloc]
initWithNibName:#"MyViewController"
bundle:nibBundleOrNil];
NSString *buttonLabelText = [[[myView] enterButton] titleLabel] text];
NSLog(#"Button label text = %#", buttonLabelText);
[myView release];
}
When this method is invoked, an instance of MyViewController will be created which will automatically load the stored objects from the NIB and bind them to the view controller object's properties. It will then retrieve the text of the button's label and write it to the log console.

If you want to load an UIView from NIB, this code would be more correct (since you don't know the index of a needed object in the xib file).
- (id)loadViewFromNIB:(NSString *)nibName owner:(id)owner class:(Class)_class
{
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:nibName owner:owner options:nil];
for (id object in objects) {
if ([object isKindOfClass:_class]) {
return object;
}
}
}

If you look at the documentation for UIViewController, there's a method called initWithNibName:bundle:. It lists five different sample code projects that Apple provides to demonstrate how to use xib files and view controllers. You should read a few of those to get a basic understanding.

Open MyLib.xib in interface builder and check that you actually have a UIView component in that file.
What error message do you get from the exception?

Related

Calling a single view in different UIViewControllers

I have declared a UIView inside a UIViewController class. Can I call this UIView in another UIViewController class?
If its possible how can I call it?
Yes, you can use a single instance of a view in a number of views/viewControllers. Typically I do the same with Views that carry advertisements.
You pass them along as you would do with any other object.
If you do not create it in Interface Bulder (I suggest creating it programmatically) then you may want to define it within your application delegate rather than a view controller and pass it to the individual view controllers that make use of it.
Within the view controller just add it as sub view accordingly, as you would do it with any other view too.
There is one thing though. When you add this view to another super view for the second time or more then it will be removed from its prior super view. That means that you will have to add it as super view again, when its prior super view becomes visible again. A view can only be part of one view hierarchy at a time.
Sample code, thanks to Gordon:
/* Untested and simplified */
AppDelegate.h:
#property ( strong, nonatomic) ReuseableView reuseableView
;
AppDelegate.m
#synthesize reuseableView;
/* in didFinishLaunchingWithOptions ...*/
reuseableView = [[alloc] init]; // or init from nib, initwithframe, etc.
viewController.m
/* In each view controller that uses the view */
- (void) viewWillAppear:(BOOL)animated
{
[self.view addSubview:((AppDelegate*)[UIApplication sharedApplication].delegate).reuseableView];
}
- (void) viewWillDisappear:(BOOL)animated
{
[((AppDelegate*)[UIApplication sharedApplication].delegate).ReuseableView removeFromSuperview];
}
I am not quite sure whether this removeFromSuperview is really required. The next addSubview will remove it from its existing superview anyway and if addSubview is called on the same super view twice in a row then it does not do any harm. However, it is save using removeFromSuperview at this point.
Well, summarized that is basically it. You define, create and store your shared view (the reusableView in Gordon's example) at a common place. The application delegate is a good place for that. In each view's controller that uses the shared view you fetch it from the delegate and add it as subview to the current view.
I would subclass UIView and import it on the ViewControllers where I wanna use it
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"YourView" owner:self options:nil];
yourView = (YourView *)[nibArray objectAtIndex:0];
Then you set its frame and [self.view addSubview:yourView]

Adding UIView subView not working

I have a UIViewController class MyClass that initially had no XIB, and was initialized programmatically. Now, I don't want to load it from an XIB, but I do want to make a small 50x50 UIView (settings overlay view), and I want to add it MyClass, so instead of programatically declaring the new settings overlay view, I thought I would create an XIB file, set the file's owner to MyClass, declare in MyClass.h an IBOutlet UIView *settingsOverlay, and link it in the XIB.
Then in the viewDidLoad method, I do [self.view addSubview:settingsOverlay], but for some annoying reason it doesn't work. It just doesn't appear. I tried creating a simple UIImageView programmatically and adding it to the subView, and it works just fine, but when done through the XIB, it doesn't work. Can anyone point out what could possible be wrong? Please let me know what other details I might need to include.
If you are trying to add a view using xib then you need to use loadNibNamed method.
[[NSBundle mainBundle] loadNibNamed:#"settingsOverlay" owner:self options:nil];
You can refer developer link for more info - http://developer.apple.com/library/IOs/#documentation/UIKit/Reference/NSBundle_UIKitAdditions/Introduction/Introduction.html
Creating an XIB for the settings overlay view and setting it's owner to MyClass does not implicitly cause that XIB to be loaded as a result of manually instantiating MyClass. You would have to manually load the settings overlay view XIB in MyClass viewDidLoad and add it as a subview. Loading it and passing owner as self will cause it to be bound to the IBOutlet you created, but you still have to add it as a subview.
The code in viewDidLoad would look like this:
[[NSBundle mainBundle] loadNibNamed:#"OverlayView" owner:self];
[self.view addSubview:self.overlayView];
You need to load the MyClass xib before settingsOverlay will be set. Try adding this line before your addSubview call.
[[NSBundle mainBundle] loadNibNamed:#"MyClass" owner:self options:nil];

UITableviewController losing reference to tableview

I have been working on this for 2 days now, cant seem to get a grasp. I'm missing something very basic I guess.
Here's what I have:
A UIViewController as the Apps root controller.
There's a ContainerView, a subclass of UIView which I add to my root controller view.
Within that I want a UITableView.
Since there are several different Containers, I have different Nibs for each.
Heres how its wired: Nib with content, has the container as its file's owner. There's an outlet to the UITableView, it has the container as source and delegate.
The container implements the protocol methods.
Now I can't call reloaddata on the UITableView since it's nil. I type po in the consolo and it says 0x0 but I don't know why.
I have been trying different approaches, but all ended up in losing the reference to the tableView.
It's not like it's my first tableview I create but I have no clue on what I'm doing wrong here.
Any HELP please!!!!
Code:
This is my Outlet:
IBOutlet UITableView *contactsTV;
File's owner has a connection to it, the tableview vice versa.
I create the nib by doing:
Contentview *v = [[Contentview alloc] initWithFrame:[[contentViewArray objectAtIndex:i] CGRectValue]];
while contentViewArray is some array storing Framevalues as strings.
Then I do:
[v prepareView];
and it looks like this:
- (void) prepareView {
NSArray *mediaPlayerViews = [[NSBundle mainBundle] loadNibNamed:#"MyView"
owner:self
options:nil];
UIView *v = (UIView *)[mediaPlayerViews objectAtIndex:0];
[self addSubview:v];
}
Just experienced a similar issue -- everything appeared wired up correctly (Xcode 4) but the outlet reference was nil.
When I created the file, I used the "New File" -> subclass of UITableViewController. with NIB (automatic) process to set up the file. This resulted in the controller being declared as a UITableViewController and the NIB had a UITableView as it's root.
Although the TableDataSource and TableDelegate methods got called as expected, the outlet for the TableView was never being set when the nib was loaded.
To fix this, I basically had to change the controller from being a subclass of UITableViewController to just UIViewController and set the NIB accordingly: I cleared the NIB, added a UIView with a UITableView as a child, reconnected the outlets (View, TableView, TableDataSource, and TableDelegate), and it all worked as planned.
I think this may be a bug with XCode, when creating a subclass of UITableView with NIB.
Set it as a UITableViewController it should work.
Do you have property set in header file along with
IBOutlet UITableView *contactsTV
?
If you don't have setter method for your contactsTV, then your contactsTV isn't retained by your object.
Try to replace your code with
//header file
UITableView *contactsTV;
#property(nonatomic, retain) IBOutlet UITableView *contactsTV;
//implementation file
#synthesize contactsTV;

iPhone SDK: CustomControls as in C#

I have a UIViewController. The UIViewController has a NIB with one outlet - a UIView, containing several buttons and labels. Imagine, it is something like a UIDatePicker.
In order to not be forced to copy and paste all the controlling code into a new environment, I was trying to encapsulate the UIView into a separate UIView subclass with an own NIB, sort of a C# CustomControl approach.
From a controlling (other) UIViewController I'm instantiating the view from the NIB
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil];
selectorView = (DateTimeSelectorView*)[nibObjects objectAtIndex:0];
selectorView is a property in the using UIViewController class. The problem: "initWithFrame" of my UIView is never called. My UIView is covering the whole space (320x480), whereas it should have a smaller size. How can I achive this? Furthermore the UIView seems to hide all other controls, instantiated from the UIViewController class.
Regards
When a view is instantiated by the nib loader, initWithFrame: isn't called. The nib loader calls initWithCoder:. You should implement initWithCoder to perform any initialization, including setting the frame.
So here is a "HowTo" for the question: How to embed a self-contained UIView into my UIViewController with NIB?
1) Say you have a UIView with a button and a textfield, making some login. You have the UI in a separate NIB called Login.xib. The functionality is in Login.m and Login.h, a subclass of UIView. The class name is "Login". Take care, that the class is set properly in Login.xib. Everything is fine.
2) Now you want to use this "out of the box" in a new app.
3) Drag the three files (Login.m, Login.h and Login.xib") into your new UIView based project
4) Add a property in your UIViewController class, pointing to your Login class (of course, include the Login.h first)
5) Open IB with Login.xib and set the file's owner to your current UIViewController class
6) Connect the main view of your Login.xib with the property defined in UIViewController (!! this is important !!)
7) Add the following to your viewDidLoad in UIViewController (supposed, the name of your property is "myLogin")
[[NSBundle mainBundle] loadNibNamed:#"Login" owner:self options:nil];
myLogin.frame = CGRectMake(0 ,100, 320, 200); // Optional, you may also use the initial bounds
[self.view addSubview:myLogin];
The view will appear where you let it appear. Other controls from your superview will be available too.
It took me several hours to find that out. There is here and there some scattered info, but I didn't find a complete "how to" for that simple task up to now.
Regards

Using a custom UIView from multiple view controllers

I have created a custom UIView that I would like to use on multiple different view controllers in my iPhone application. Currently, I have copied the UIView that contains the controls from the nib file for the first view controller, pasted it into the other view controllers, and then wired up all of the actions to each view controller. This works fine, but is not the optimal way that I would like to use this custom view.
My custom view is reasonably simple, it consists of some UIButtons with labels, and tapping on these buttons fires actions that changes the contents of controls on my view controller's view.
What would be a strategy to consolidate the definition and usage of this UIView? Ideally, I would like to just reference this custom view from the nib of view controllers, and have my view controller respond to actions from this custom view.
EDIT: OK, based on J.Biard's suggestions, I have tried the following with not much luck.
I created another UIView based nib file with the contents (for now just some UIButton objects) of my reusable view and UIView subclass .m and .h files, and then set the nib File's Owner class to my newly created class name.
Then, I added most of the code from J.Biard (I changed the rect to 50,50,100,100, left out the setDelegate out for now, as I am just trying to get it working visually for now, and i found that [self.view addSubview:view] worked much better than [self addSubView:view]) to the end of the viewDidLoad method of the first view controller that is displayed when the app fires up.
And what I get now is my main view with a black square in it. Have I missed an outlet somewhere, or is there some initialization needed in initWithFrame or drawRect in the UIView subclass?
Create your MyCustomView class and nib file.
In the nib file, set Files Owner to MyCustomView - then design your view as normal, with a top level UIView. Create an IBOutlet in MyCustomView to link to your top level UIView in your nib file.
In MyCustomView add this method:
- (BOOL) loadMyNibFile {
if (![[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:self options:nil]) {
return NO;
}
return YES;
}
In your view controllers you can use this custom view like so
- (void)viewDidLoad {
MyCustomView *customView = [[MyCustomView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
[customView loadMyNibFile];
[self.view addSubview:customView.view]; //customView.view is the IBOutlet you created
[customView release];
}
You could also create a convenience class method on MyCustomView to do this if you liked.
If it is very simple I would recommend that you create a subclass of UIView in code and create instances of this class (or you can use Interface Builder to create the custom UIView that is then archived into the NIB file and restored later on also using code).
Using the code solution you could create instances of your custom UIView in your controller by calling something like:
#import "MyCustomView.h"
// let the superview decide how big it should be or set it as needed.
CGRect sizeRect = CGRectMake(0,0,0,0);
// create an instance of your view
MyCustomView *view = [MyCustomView alloc] initWithFrame:sizeRect];
// set a custom delegate on the view or set callback methods using #selector()...
[view setDelegate:self]; // self = view controller
// add the view to the controller somewhere... (eg: update CGRect above as needed)
[self addSubView:view];
// don't forget to release the view somewhere ;-)
This example assumes that you create a delegate protocol that your View Controller can respond to or you can wire up events dynamically using #selector. If you don't want to create instances of the view in code you could add a "UIView" to your NIB file and set it's class type in the inspector window (command -> 4 -> class dropdown).
If you want to do everything in interface builder you can create your custom UIView and use something like "- (NSArray *)loadNibNamed:(NSString *)name owner:(id)owner options:(NSDictionary *)options" (see NSBundle) to load the NIB file dynamically. This presents it's own challenges though it is also feasible.
The most involved option would be to create your own xcode custom UI library / plugin so that your custom control / view could be dragged into each NIB file from the Library window like any other control shipped by Apple.
Hope this clarifies or eliminates some options for re-using controls for you.
Cheers-