nib class is different in different target? - ios

I'm trying to write a unit test that setup the view controller, I've tried two ways to get the view init, the first way is to use the bundle to load nib content and filter out the one I'm looking for, as follow:
MyViewController *controller = nil;
....
NSArray* nibContents = [[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil];
NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
NSObject* nibItem = nil;
while ( (nibItem = [nibEnumerator nextObject]) != nil) {
if ([nibItem isKindOfClass:[MyViewController class]]) {
controller = (MyViewController*) nibItem;
break;
} else {
NSLog(#"nibItem class is %#", [nibItem class]);
NSLog(#"nibItem is %#", nibItem);
}
}
After these code finished, the controller still be nil, I've insert some logs to inspect the nib class(the %# place holder), and it turns out is the same as MyViewController (at least both classes description did), and I'm so sure these code works very well in the debug/release target, but it's not worked while I run the unit tests.
So is that means the classes is different although their classes description are the same?
The second way I've tried is use the initWithNibNamed:owner:options method, just simply specify the xib file name, but Xcode reply that the nib loaded but view outlet not set, the situation just as the questions describe I found, but I have double check that things have been setup correctly:
In Interface Buildedr, specify the custom class name
Add xib file to the list of copy build resources section in my test target
Link the tableview outlet to the interface file (though I can not drag the view outlet to my class, but it's auto pointed to the tableView in the class)
till now the only way I could get the test pass is manually to set the view controller's view and table view.

MyViewController *controller;
NSArray* nibContents = [[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil];
controller=(MyViewController *)[nibContents objectAtIndex:0];
replace your existing code with this one , Hope this will help you.

Related

ViewController initialized by xib can not work?

This is a weird problem..
1.I create a custom “ view ” by xib, and initialize it by
KWView *oneView = [[[NSBundle mainBundle] loadNibNamed:#"KWView" owner:nil options:nil ]lastObject];
This Xib’s File’s Owner name is “NSObject”(then i try any other more,whatever i choose, it runs smoothly),and there, i choose the view’s Custom Class as “KWView”[This xib named "KWView.xib"]
======= That works !
2.Then i create another custom “viewController” by xib .
KWMenuVC *menuVC = [[[NSBundle mainBundle] loadNibNamed:#"KWMenuVC" owner:nil options:nil ]lastObject];// error occurs here
[self.view addSubview:self.menuVC.view];
This Xib’s File’s Owner name is “KWMenuVC”(i also try NSObject), and there, i choose the Custom Class of this VC’s view as “UIView”.[This xib named "KWMenuVC.xib"]
======= This one can not work, it stucks in “KWMenuVC *menuVC = [[[NSBundle mainBundle]loadNibNamed:#"menuVC" owner:nil options:nil ]lastObject]; “ and the error is “[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key view”
3.Later, i try another method to initialize the KWMenuVC ,which works well how matter how i change its Xib’s File’s Owner name
KWMenuVC *menuVC= [[KWMenuVC alloc]init];
[self.view addSubview:self.menuVC.view];
======= Works well !
How could this happen?? I used to construct apps' UI by codes. But xib seems more efficient in some cases. Now it seems not a easily understandable stuff.
Thx a lot for your help!
The correct way to initialize a UIViewController with a XIB is:
KWMenuVC *viewController = [[KWMenuVC alloc]initWithNibName:#"KWMenuVC" bundle:nil];
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instm/UIViewController/initWithNibName:bundle:

iOS - Custom View that can contain other views

For a while I've been using custom cells (with their own nibs) for tables without issues. Now in a new project I see the need for a reusable custom view (not a cell) the would have a title, a button, and another UIVIew to hold more views. I'll call it "Section":
The idea would be to be able to use this Section in storyboards (using a UIView and setting the custom class accordingly). That way whatever views I put inside that UIView would actually be contained in the inner UIView of the Section.
I thought the hard part would be to actually get the views put using IB and Storyboard to actually reside in that inner UIView instead of the root UIView of Section. Turns out just making the custom view (without any inner views yet) is not working as I would have expected. Here is the code, which is based off of the dozens of custom cells I've done and have worked (though adjusted for the specific init methods of a generic UIView):
#import "SectionContainer.h"
#implementation SectionContainer
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"SectionContainer" owner:self options:nil];
self = [nibArray objectAtIndex:0];
/*NSArray *nibRoot = [[UINib nibWithNibName:#"SectionContainer" bundle:nil] instantiateWithOwner:self options:nil];
[self addSubview:[nibRoot objectAtIndex:0]];*/
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// Initialization code
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"SectionContainer" owner:self options:nil];
self = [nibArray objectAtIndex:0];
/*NSArray *nibRoot = [[UINib nibWithNibName:#"SectionContainer" bundle:nil] instantiateWithOwner:self options:nil];
[self addSubview:[nibRoot objectAtIndex:0]];*/
}
return self;
}
The matching XIB has its root view set to this custom class (just like I do in the custom cells)
THE PROBLEM
This custom class causes a EXC_BAD_ACCESS code=2 and from what I can tell by stepping through it, it's as if the class is being called recursively. Call after call after call to initWithDecoder is being made until the EXC_BAD_ACCESS error happens
WHAT I'VE TRIED
Given the seeming recursive calls I tried another approach I saw that set the XIB's file owner to the Custom Class instead of the XIB's root View. This caused the following error:
'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key sectionContainerView
Tried a slightly different method (commented out in the code above) where the XIB's root is added to the custom class (addSubView) instead of being set to it. This didn't change anything, same recursive calls (or error above if that is set up)
I would REALLY appreciate some guidance on this. Thank you.
You need to use a component called Custom Container View in storyboard. I can't just post code here because it involves some configuration in your storyboard and the code would depend on how you plumb your views / VCs, but you can read the relevant guide here:
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
First, the recursive call is on initWithCoder:, loading a nib means instatiating its views through initWithCoder:.
That's why you can't use your UIView subclass you've designed on a nib this way (by setting a view's class on a storyboard or even on another nib actually).
The only way to use it is to instantiate it through the nib, in code.
Section *sectionView = [[[NSBundle mainBundle] loadNibNamed:nibName owner:owner options:options] objectAtIndex:index];
Now, with wiring up things from the nib you've made:
You can make connections from the objects on your nib to another object which is not found on the nib. That is what File Owner is for. You have to set its(File Owner's) class, and make the connections to it, and use an instance of its class to which you want the connections to be realized, as the owner parameter when loading the nib.
But I guess this is not what you wanted. I think you wanted to make the sub views on the nib accessible through "Section" which I assume is the root view on the nib. You create IBOutlet (or, IBAction, IBOutletCollection) properties on the Section class. To wire these up with the rest of the objects on your nib, control click on the "Section" view on your nib, and you'll see what to do from there.

Changing IBOutlet from seperate class not working

-(void)buttonTapped {
ExpandableCell * mvc= [[ExpandableCell alloc] init];
if (mvc == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ExpandingCell" owner:self options:nil];
mvc = [nib objectAtIndex:0];
}
mvc.interactLabel.text = #"Second";
}
Isn't working for me. The interactLabel text doesn't change when compiled. This method is called when the user taps on a button in a custom cell. I've been able to change other labels in the cellForRowAtIndexPath method but for some reason it won't change here. I know this method gets called because I've logged it. Could someone please help? Am I creating the ExpandableCell instance incorrectly?
Remove the code:
ExpandableCell * mvc= [[ExpandableCell alloc] init];
if (mvc == nil) {
Because it creates an empty view not associated with any XIB (so no sub views exist). Just keep the NIB loading code and the configuration.
After that, your code doesn't do anything with mvc (I assume this is just for the question and you are adding it as a subview somewhere in your real code).

Subclassing a UIView subclass loaded from a nib

I have a class, FooView, that is a subclass of UIView, and whose view is loaded from a nib, something like:
+ (instancetype)viewFromNib
{
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:#"FooView" owner:self options:nil];
return [xib objectAtIndex:0];
}
The nib itself has its Custom Class set to FooView in the Identity Inspector.
This is instantiated as:
FooView *view = [FooView viewFromNib];
This behaves as you'd expect. However, when FooView is itself subclassed as FooSubclassView, and instantiated as:
FooSubclassView *view = [FooSubclassView viewFromNib];
view is still of type FooView, not FooSubclassView.
Swizzling the class with object_setClass doesn't fix the fact that the underlying object is an instance of FooView, and thus methods called on the subclass instance will be those of the superclass (FooView), not FooSubclassView.
How can I correct this so that subclasses are of the correct type, without having to create a new nib for every subclass, or having to reimplement viewFromNib in every subclass?
Swizzling is not (ever) the answer.
The problem is in your NIB; it is archived with object[0] being an instance of FooView, not FooSubclassView. If you want to load the same NIB with a different view subclass as object[0], you need to move the instance out of the NIB's archive.
Probably the easiest thing to do, since your class is already loading the NIB, is make an instance of FooView or FooSubclassView the File's Owner.
This question has a decent explanation of the File's Owner pattern. Note that you are pretty much already there in that your class is what is loading the XIB/NIB anyway.
And here is the official docs on File's Owner.
I'm not sure you are onto the best solution, but I think this is what you are looking for.
+ (instancetype)viewFromNib
{
NSString *className = NSStringFromClass([self class]);
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
return [xib objectAtIndex:0];
}
That is as long as you can be sure that the NIB has the same name as the class.
After realizing that I mistook one of the requirements, I say I'll have to agree with #bbum.
- (id)init
{
// NOTE: If you don't know the size, you can work this out after you load the nib.
self = [super initWithFrame:GCRectMake(0, 0, 320, 480)];
if (self) {
// Load the nib using the instance as the owner.
[[NSBundle mainBundle] loadNibNamed:#"FooView" owner:self options:nil];
}
return self;
}
+ (instancetype)viewFromNib
{
return [[self alloc] init];
}

UIView, struggling with some basics ( xcode, iOS )

I'm struggling with some basics in my storyboard-based iOS Application.
I like to load some UIView's which are builded up in NIB-Files (xib). If I load the View, the initWithCoder-method will be called.
Now I like to load "green" Views; I read, that I'm able to put some other initializations in the initWithCoder - but only "new" objects and nothing, that regarding the View itself, like self.backgroundColor in cause of memory (maybe the object isn't complete initialized at that point).
Where is the best place to add some stuff, like setBackgroundColor, setCornerRadius in the view-Class itself?
I'm doing it like this:
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:nil options:nil];
self = [nibViews objectAtIndex: 0]; //here it would be best practice to loop through nibViews and get the first view that is member of your class.
Also , the view , in the nib file is set as a MyCustomView instead of UIView.
After these 2 lines you can set any values that you want. Be aware though , if you set the frame for super for example , it will be overwritten by the values in the xib file. So better set everything AFTER loading the nib , not before.
As far as I know , there are no callbacks for UIView loading as viewDidLoad is for UIViewController.
Hope this helps.
Cheers!
EDIT:
You can do something like this:
- (id)initWithInfo:(MyInfoClass *) selectedInfo Body:(NSString *) _body
{
if ((self = [super init]))
{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"MyCustomClass" owner:nil options:nil];
self = [nibViews objectAtIndex: 0];
[self setInfo:selectedInfo];
[self setBody:_body];
}
}
And then just use that initWithInfo: Body: method.

Resources