iOS App crashes with message "-[UITapGestureRecognizer setFrame:]" when adding Gesture in XIB - ios

My app crashes when adding Gesture to Custom View (XIB). I am using Xcode version 6.4.
Below are the steps I followed to add a custom subview with tap gesture:
Added an XIB and a UIView subclass (MyView) to my project. And set the XIB class to MyView.
Added a TapGesture to MyView using Interface Builder
Created MyView object (myView) and added it as a sub view using [addSubview:myView].
When I run the app, it crashes
Removed the TapGesture in XIB and run again with no issues.
Code:
[[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil] firstObject];
Log:
-[UITapGestureRecognizer setFrame:]: unrecognized selector sent to instance 0x7f924a4ce910
Sometimes like this,
-[UITapGestureRecognizer superview]: unrecognized selector sent to instance 0x7f924a4ce910
Please advice.

I solved the issue by replacing the code,
[[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil] firstObject];
with
MyView *myView = nil;
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil];
for (id object in objects)
{
if ([object isKindOfClass:[MyView class]])
{
myView = object;
}
}
Note:
If the XIB have a single UIView object only, then the single line code mentioned above is enough. But if the XIB have multiple UIView objects or any Gestures added in XIB, then we need to find out the MyView object from the array returned by the loadNibNamed:owner:options: method.

Related

iOS: Passing string value to the UILabel of other XIB

//Update provided
I have a storyboarded UIViewController application.
I have other XIB (inherited from UIView having only a label on it) like below
The following are contents Main.storyboard (MyController is an interface extending UIViewController)
> ViewController (UIViewController)
> MyHolderView (UIView)
The following are contents of MySquare.xib (MySquare is an interface extending UIView)
> MySquare (UIView)
> MyLabel (UILabel) (Having Default value LABEL, entered in attribute inspector)
Now I have to make 3 UIViews of instances MySquare and add it to MyHolderView
I tried to assign new label to these 3 UIView's labels' text.
But I am not able to see the new labels but only the default label LABEL is coming.
MySquare *square=[[MySquare alloc]init];
//square.myLabel.text = #"TRY";
[square.myLabel setText:[[NSString alloc]initWithFormat:#"%d",(myVar)]];
Please help.
Update
I have overrode my MySquare's init method like this. Still no luck.
I am calling the below method from UIViewController where I init my MySquare views.
Calling from UIViewController:
MySquare *square=[[MySquare alloc]initWithFrame:CGRectMake(20,20,50,50) string:[[NSString alloc] initWithFormat:#"%d",myVar]];
Implementation of the overridden init function
- (id)initWithFrame:(CGRect)frame string:(NSString *)str;
{
self = [super initWithFrame:frame];
if (self) {
self.myLabel.text=#"A";
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil] objectAtIndex:0]];
[self.myLabel setText:[[NSString alloc]initWithString:str]];
}
return self;
}
You need to understand the difference between a class and an instance. MySquare is a class, but you need a reference to the actual instance of MySquare in your interface. Thus this code is pointless:
MySquare *square=[[MySquare alloc]init];
[square.myLabel setText:[[NSString alloc]initWithFormat:#"%d",(myVar)]];
It is working perfectly, but the problem is that this MySquare instance is not the MySquare instance that is in your interface. (It is just a separate MySquare instance that you created, floating loose in your code.) Thus you cannot see anything happening.
Now let's consider this code:
[self addSubview:[[[NSBundle mainBundle]
loadNibNamed:#"View" owner:self options:nil] objectAtIndex:0]];
[self.myLabel setText:[[NSString alloc]initWithString:str]];
Here, you did fetch a MySquare instance from the nib and put it in your interface. Good. But you did not keep any reference to it, so you have no (easy) way to talk to it! In particular, self.myLabel is not the same as the MySquare instance's myLabel.
You left out a step! You need a reference to the MySquare instance, like this:
MySquare* square = [[[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil] objectAtIndex:0]];
[self addSubview:square];
[square.myLabel setText:#"Look, it is working!"];
Even that is not enough, if you want to talk to square.myLabel in the future. You will need to keep a reference to square (or to square.myLabel) as an instance variable.

How to load a XIB with a top-level ViewController?

I've got a nib with a view controller root element like this:
so I can position elements relative to the top and bottom layout guides using auto-layout.
When I first tried to load this nib using
SearchViewControllerPro* searchViewController = [[SearchViewControllerPro alloc]initWithNibName:#"SearchViewControllerPro" bundle:[NSBundle mainBundle]];
I got the following run-time exception:
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: '-[UIViewController
_loadViewFromNibNamed:bundle:] loaded the "SearchViewControllerPro" nib but the view outlet was not set.'
Googling the error it was pointed out to me, that the file owner of the xib needed to be set to the class of my view controller and the view outlet had to be set to the view object in the xib. If I do so, then I get the following run-time error:
Terminating app due to uncaught exception
'UIViewControllerHierarchyInconsistency', reason: 'A view can only be
associated with at most one view controller at a time! View > is associated with . Clear this association before associating this view with
.'
Does not come as a surprise since the view is associated to both the file owner and the top-level view controller of the nib. But how can I tell the run-time that they are both in fact the very same thing instead of two separate entities?
Edit:
When I try to unpck the ViewController from the nib like so,
NSArray* xibContents = [[NSBundle mainBundle] loadNibNamed:#"SearchViewControllerPro" owner:nil options:nil];
SearchViewControllerPro* mapSearchViewController = [xibContents lastObject];
, it does no good either:
Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[ setValue:forUndefinedKey:]: this class
is not key value coding-compliant for the key view.
Temporary solution:
I found a solution, but it is not pretty. Despite the structure as shown in IB, the view controller is not the last object in the xib. So I have:
__block SearchViewControllerPro* mapSearchViewController = nil;
[xibContents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[SearchViewControllerPro class]]) {
mapSearchViewController = obj;
}
}];
and this seems to work without run-time crashes. However, it's everything but clean code.
how can I tell the run-time that they are both in fact the very same
thing instead of two separate entities?
You can't because they are not the same thing. You have created two SearchViewControllerPro intstances.
You need to either alloc-init a SearchViewControllerPro instance or unarchive one from a nib.
If you decide to create the ViewController in the nib the usual way to access it is the same way you would access any other item (view, button, textfield) that you create in a nib.
Add an outlet to the FilesOwner object, hook up the connection in interface builder and be sure to pass the object when you unarchive the nib.
eg If you want the Object that unarchives the nib to be FilesOwner:
[[NSBundle mainBundle] loadNibNamed:#"SearchViewControllerPro" owner:self options:nil];
The following works as well and (at least to me) is a little more explicit that creating an outlet for the ViewController:
NSArray* xibContents = [[NSBundle mainBundle] loadNibNamed:#"SearchViewControllerPro" owner:nil options:nil];
SearchViewControllerPro* mapSearchViewController = [xibContents objectAtIndex:0];
if (isIPad)
{
[[NSBundle mainBundle] loadNibNamed:#"ABC_iPad" owner:self
options:nil];
}
else{
[[NSBundle mainBundle] loadNibNamed:#"ABC_iPhone" owner:self options:nil];
}

iOS SDK using loadNibNamed, code within 'init" not working as expected

FluidInfo *fluidInfo = [[FluidInfo alloc]init];
UIView *info = [[[NSBundle mainBundle] loadNibNamed:#"FluidInfoSheet" owner:fluidInfo options:nil] objectAtIndex:0];
[self createFormulaPopup:info];
I have a nib file with a UIView. and I have a subclass of UIView called 'FluidInfo.' I make this UiView appear as a popup inside my viewController using my function 'createFormulaPopup'
I have made my UIView a subclass of FluidInfo. When I create outlets and actions they are all working correctly.
The problem is that my init function within my UIView is working unexpectedly. If I log something then it appears at the same time as my popup.. but if i set something like background color of the view it seems to disappear. If I set it in interface builder it will stick. If I attach the change of the color to an action within UIView then that will work as well. But when the view is initialized it seems to undo all the stuff I've done programmatically.
okay so i fixed this. i loaded the nib within the uiview itself instead of within my viewcontroller
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self = [[[NSBundle mainBundle] loadNibNamed:#"FluidInfoSheet" owner:self options:nil]objectAtIndex:0];
// Initialization code
}
return self;
}
in your code you pass FluidInfo *fluidInfo instated of your view Controller
[[[NSBundle mainBundle] loadNibNamed:#"FluidInfoSheet" owner:fluidInfo options:nil]objectAtIndex:0];
refer this link
https://developer.apple.com/library/ios/documentation/uikit/reference/NSBundle_UIKitAdditions/Introduction/Introduction.html
in that given you have to pass owner that would be your file owner here you pass owner as view subclass that's why it may not work pass self (your view controller)so it would find file owner in application.
[[[NSBundle mainBundle] loadNibNamed:#"FluidInfoSheet" owner:self options:nil]objectAtIndex:0];
refer this link of Stack over flow give you very good description of file owner What describes the "File's Owner" best in objective-c / cocoa Nib?

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];
}

nib class is different in different target?

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.

Resources