Custom view with nib as subview doesn't seem to be loading - iphone

I've created a custom view that loads its content from a nib, like this:
/* PricingDataView.h */
#import <UIKit/UIKIt.h>
#interface PricingDataView : UIView {
UIView *contentView;
}
#property (nonatomic, retain) IBOutlet UIView *contentView;
#end
/* PricingDataView.m */
#import "PricingDataView.h"
#implementation PricingDataView
#synthesize contentView;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[[NSBundle mainBundle] loadNibNamed:#"PricingDataView" owner:self options:nil];
[contentView setFrame:frame];
[self addSubview:contentView];
}
return self;
}
/* ... */
In the nib file I set PricingDataView as the type of the File's Owner, and connected the contentView outlet in IB. I placed a regular UIView from the Interface Library onto the full-sized view shown to the user, and then changed it's class name to PricingDataView. It all builds, but at runtime, nothing is rendered where my custom view is supposed to be.
I put breakpoints in PricingDataView.initWithFrame, but they don't hit, so I know I'm missing something that would cause the view to be initialized. What I'm curious about is that in the process of loading my other views from nibs, all the initialization happens for me, but not with this one. Why?

Are you sure your other views aren't using a UIViewController? Here's a quote from the documentation for initWithFrame: from UIView:
If you use Interface Builder to design
your interface, this method is not
called when your view objects are
subsequently loaded from the nib file.
Objects in a nib file are
reconstituted and then initialized
using their initWithCoder: method,
which modifies the attributes of the
view to match the attributes stored in
the nib file. For detailed information
about how views are loaded from a nib
file, see Resource Programming Guide.

when you add files in Interface Builder at runtime it calls the
-(id)initWithCoder:(NSCoder *)aDecoder and not the initWithFrame.

just Undo the class name (PricingDataView) of the view that you add to UIView. In this case the initWithFrame: method would be called.
previously awakeFromNib was called in loop. As your customView and the View you are adding via Nib as a subView are of the same class type.

Related

In Objective-C with no nib file is MVC to have the logic together with the view in same class?

When I work in Objective-C programatically with out nib files, and have the logic in my:
appViewController.m
having in the same class What is going on with that view, as well as with the View elements? Is this against the MVC pattern?
Do I have to create another class and message both classes?
It's up to you! If you want to separate layers (M,V,C) you can create your own view programmatically and, by using composite design pattern, build it in your UIView subclass, by removing drawing code from your controller.
That is...
You create a "CustomCompositeView" that extends UIView
in layoutSubview (hinerited from UIView) you will draw all your UI elements
in your CustomViewController you will display your view using loadView:
code:
- (void)loadView
{
CustomCompositeView *mainView = [[CustomCompositeView alloc] initWithFrame:aFrame];
[self setView:mainView];
[mainView release]; // remove this line if you are using ARC!
}
Technically, it is going against the MVC pattern. Your V and C and combined into a single object. You can seperate the code that handles layout and drawing into a seperate UIView subclass. Then load it with loadView:
// MyViewController.m
- (void)loadView {
MyView* myView = [[[MyView alloc] init] autorelease];
myView.delegate = self;
self.view = myView;
}
#pragma mark - MyViewDelegate Methods
- (void)myViewSaveButtonWasPressed:(MyView *)myView {
// do something
}
To communicate between the view and the view controller, you can define a delegate protocol.
// MyView.h
#class MyView;
#protocol MyViewDelegate <NSObject>
- (void)myViewSaveButtonWasPressed:(MyView *)myView;
#end
#class MyView : NSObject
#property (nonatomic, assign) id<MyViewDelegate> delegate;
// ...
When a button is pressed in the view (or something else along those lines) pass that on to the delegate. The ViewController should conform to the delegate method and handle the actual logic itself that way.

Best Practice: Presenting a Subclassed UIView (with own xib) as subview of UIViewControllers

My goal is, to have a subclassed UIView (lets call it infoView) designed in his own XIB so that I can present it in many UIViewController's.
The Problem:
So far, when I was adding UIView's to a UIViewController I always had to make an UIViewController the file's owner of the UIView's .xib file to load the view with something like:
...
//this is inside the calling UIViewController's method
// InfoView *infoView is ivar and a subclass of UIView
infoView = nil;
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:#"InfoView"
owner:self options:nil];
for (id object in bundle) {
if ([object isKindOfClass:[InfoView class]])
infoView = (InfoView *)object;
}
[[self view] addSubview:infoView];
...
But I want to use the same UIView in many different UIViewController's, so I actually don't want a file's owner except maybe the class itself. In ThomasM's question he was setting the UIView itself to be the file's owner but without success.
In the answers there I found a solution to set the file's owner to nil. To do so I had to add all calling UIViewController objects from the Interface Builder object library to the InfoView.xib file and connect them with their infoView outlets.
But this doesn't feel right. So here I would like to collect solutions to
encapsulate a UIView together with his xib-file to use it in many different view controllers. How do you guys handle that?
Thx for any help.
EDIT:
The infoView is something like an overlay which appears when the user presses a button on one of the view controllers. It's NOT the View controllers "main" view. It gives detailed informations about the view of his superviews view controller and will disappear afterwards. I only fill the infoView with different contents threw out all the calling view controllers.
Like Hollance answer was pointing out I am using UINib.
To use it, leave the .xib files owner nil and place all customization of the infoView inside the initWithCoder: method of your InfoView class implementation. This will get called if you obtain the InfoView.xib like:
// here InfoView is the name of the .xib file
UINib *infoNib = [UINib nibWithNibName:#"InfoView" bundle:nil];
NSArray *topLevelObjects = [infoNib instantiateWithOwner:self options:nil];
QInfoView *infoView = [topLevelObjects objectAtIndex:0];
So you want to load a UIView from a nib that you wish to use in more than one UIViewController, and you want to connect it to an outlet on each of those view controllers. Is that correct?
Then make a UIViewController subclass (let's call it FakeViewController) with an IBOutlet property. Set that FakeViewController as the nib's File's Owner and connect your UIView to its outlet.
Done.
You just need to make sure all your other view controllers also have these outlet properties (although they don't need to be IBOutlets), but the nib loader doesn't actually check to make sure the class that you pass into the owner parameter equals the class name you specified in Interface Builder. So you can fake it.
Oh, and if you're OS 4.0 and higher, use UINib to load the nib file.
And yet another way is to create your own "controller" based on NSObject to define your own life-circle (instead of standard UIViewController life-circle).
For example:
BaseSubview.h:
#interface BaseSubview : NSObject {
UIView* _view;
}
#property (nonatomic, retain) IBOutlet UIView* view;
- (void)myMethod;
#end
BaseSubview.m:
#import "BaseSubview.h"
#implementation BaseSubview
#synthesize view = _view;
- (id)init
{
self = [super init];
if (self) {
// ...
}
return self;
}
- (void)dealloc
{
[_view removeFromSuperView];
[super dealloc];
}
- (void)myMethod
{
// view specific logic here
_view.backgroundColor = [UIColor redColor];
}
#end
InfoView.h:
#import "BaseSubview"
#interface InfoView : BaseSubview {
UILabel* _labelInfo;
}
#property (nonatomic, retain) IBOutlet UILabel* labelInfo;
#end
InfoView.m:
#import "InfoView.h"
#implementation InfoView
#synthesize labelInfo = _labelInfo;
- (id)init
{
self = [super init];
if (self) {
// ...
}
return self;
}
- (void)myMethod
{
// view specific logic here
_labelInfo.text = #"current time...";
[super myMethod];
}
#end
InfoView.xib:
file owner is InfoView
assign of outlets as usual
view is parent all other controls (such as labels, etc)
HugeAndComplicatedViewController.h:
// ...
// among other var definitions
InfoView* _infoView;
// ...
HugeAndComplicatedViewController.m, most interesting part:
// when you decide to show your view
// probably in loadView
_infoView = [[InfoView alloc] init];
[[NSBundle mainBundle] loadNibNamed:#"InfoView" owner:_infoView options:nil];
[self.view addSubview:_infoView.view];
// possibly perform specific logic
[_infoView myMethod];
// no need sub-view any more
// probably in dealloc
[_infoView release];
So now you have your own sub-view with logic and design separated from "Huge & Complicated" view-controller. It can have any life-circle you need for your current project.
does infoView need to be a subview?
in your viewController:
-(id) init {
self = [super initWithNibName:#"myNib" bundle:nil];
if (self) {
// code here
}
}

How to make a .xib show up in a custom class

I was wondering how to connect my custom class and my .xib. In Interface Builder I have changed the class of the Files Owner to my custom class. I then created an IBOutlet and connected it to the view of the .xib. I then added an instance of my custom class to my UIViewController. My custom class is a subclass of UIView, so I set the background of it to black and can see it appear on top of my UIViewController. What I can't see is my .xib???? Here is my code for my custom class... (FreeThrow is my custom class... FetView is my .xib)
#interface FreeThrow : UIView {
IBOutlet UIView *mySquareView;
}
#property(nonatomic, retain)IBOutlet UIView *mySquareView;
-(void)createMe;
#end
#implementation FreeThrow
#synthesize mySquareView;
-(void)createMe {
[self addSubview:mySquareView];
[self setBackgroundColor:[UIColor blackColor]]; // I did this to know my UIViewController is showing this class... which it is
}
#end
Here is my code for what I call from my UIViewController...
freeThrowView = [[FreeThrow alloc] init];
[freeThrowView createMe];
freeThrowView.frame = CGRectMake(8, 42, 335, 230);
[self addSubview:freeThrowView];
What code do I need to add? What do I need to do? What is the problem? Why is my .xib not showing up.
Sadly apple doesn't give us a way to truly link a xib to a view via IB alone. You'll have to do it via instantiation. Here are some past answers to similar questions:
How do I associate a nib (.xib) file with a UIView?
Loading .xibs into a UIView

Load a UIView from a NIB as a subview of another UIView loaded also from a NIB

The idea basically is:
Inside of myViewController NIB I have a UIView as a subview, with connections to an IBOutlet on MyViewController class. I want to load the UIView subview from a NIB also. The subView also has a class associated with it. But the subview is not showing. Here is what I do
on MyViewController.h
#interface MyViewController : UIViewController {
IBOutlet SubView *subView; // this outlet is connected to an empty UIView on IB
}
#end
on MyViewController.m
-(id)init {
if ((self = [super init])) {
// load the main view
[[NSBundle mainBundle] loadNibNamed:#"myViewController" owner:self options:nil];
// load the sub view from another NIB
self.subView = [[[NSBundle mainBundle] loadNibNamed:#"subView" owner:self options:nil] objectAtIndex:0];
}
}
the SubView.h is defined as:
#interface SubView : UIView {
}
#end
What am I doing wrong?
What you need to do is set your file owner to be a UIViewController, hook up your view to the view property of the file owner, save your nib. In your code, load your nib like this:
UIViewController* c = [[UIViewController alloc] initWithNibName:#"Foo" bundle:nil];
SubView* subView = [c view];
Should be off to the races.
One final note, is that the type of subView should be the same as whatever is defined as the class name in interface builder. If it's a SubView there, then it's safe to declare it as a SubView* in code.
GianPac - shameless self-promotion, but I did a blog post about this about a month ago. Feel free to ignore the drop-shadow related code.
http://nathanhjones.com/2011/02/20/creating-reusable-uiviews-with-a-drop-shadow-tutorial/
Also, I (thanks to advice from several people) avoid ever having more than one view controller on my 'view'. The method I outline above allows you to accomplish this and still use IB to do the layout/outlets.

Reusing a uiview created from a nib in multiple viewcontrollers

OK, sorry, In a nutshell: What is the best/easiest/recommended way of reusing a view created from a nib in multiple viewcontrollers that are also created from nibs?
Use the - (NSArray *)loadNibNamed:(NSString *)name owner:(id)owner options:(NSDictionary *)options method of NSBundle to load your nib.
Create a nib file and set the File's owner to one of your view controllers. It shouldn't matter which one as long as all the relevant properties are present in both.
Set up the nib, linking your views/buttons/etc. to the file's owner properties.
In your view controllers do the following
#interface MyViewController : UIViewController {
}
#property (retain, nonatomic) IBOutlet UIView *myView;
// Add whatever other outlets you need for your nib.
#end
#implementation MyViewController
#synthesize myView;
- (void)viewDidLoad {
[super viewDidLoad];
// You set up your other views/ivars/etc. here
[[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:self options:nil];
// Assuming that your bundle contains a single top-level object that is linked to the
// 'myView' property in your view controller, everything should be properly retained
}
#end