Subclass UIView from NIB file not entered into subclass

I have a NIB file and some class (SomeClassView: UIView) is set as a custom top-level NIB class. SomeClass has IBOutlets, which I use to connect SomeClass routines, which I post in Interface Builder.

I create an instance of SomeClass as follows:

- (id)initWithFrame:(CGRect)frame { self = [[[[NSBundle mainBundle] loadNibNamed:@"SomeClassView" owner:nil options:nil] objectAtIndex:0] retain]; // "SomeClassView" is also the name of the nib if (self != nil) { self.frame = frame; } return self; } 

Now say I'm subclassing SomeClass with SubClassView. I add a method for SubClassView called - (void) foo:

 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self != nil) { [self foo]; } return self; } 

At this point, I get a runtime error: * The application terminated due to the unselected exception "NSInvalidArgumentException", reason: '- [SomeClassView foo:]: unrecognized selector sent to instance 0xAAAAAA'

It seems that the "self" inside the initWithFrame from the SubClassView is still set to super, SomeClassView. A quick hack fix to get around this is to change the isa pointer in the SubClassView initWithFrame:

 - (id)initWithFrame:(CGRect)frame { Class prevClass = [self class]; self = [super initWithFrame:frame]; if (self != nil) { isa = prevClass; [self foo]; // works now } } 

This is not an ideal workaround, since I have to update isa every time I do a subclass, or there may be different init methods that I will also have to update.

1) Ideally, is there an easy way to fix this, purely in code? Maybe with the way I set myself = loaded tip?

2) Is there an alternative architecture that works better for what I'm trying to do? One example is to set the owner yourself, but then you have to manually set all property / output mappings.

+4
source share
2 answers

Moving isa pointers is a problem if your subclasses have instance variables different from those specified in SomeClassView . Please note that your nib file has an object of type SomeClassView , which means that when you load the nib file, the nib loader will select an object of this type and cancel it from the nib file. Changing the isa pointer to [SubViewClass class] temporarily does not make it an object of type SubViewClass per se, since what the nib loader selects is a SomeClassView object.

However, I do not think that this is a reliable and automatic way to use nib files containing objects whose types must be changed when loading nib.

What you can do is have your SomeClassView objects SomeClassView delegate that conforms to some protocol. This protocol will define behaviors in SomeClassView that can potentially be extended. For instance,

 @protocol SomeClassViewDelegate @optional - (void)someClassViewDidAwakeFromNib:(SomeClassView *)someClassView; @end 

Instead of subclassing SomeClassView , you have arbitrary objects that execute any custom behavior that you currently have in SubClassView . For instance,

 @interface SubClassViewBehaviour : NSObject <SomeClassViewDelegate> … @end @implementation SubClassViewBehaviour - (void)someClassViewDidAwakeFromNib:(SomeClassView *)someClassView { // whatever behaviour is currently in -[SubClassView foo] } @end 

A SubClassViewBehaviour object A will be created in the code and set as the owner of the nib files when loading the nib file or any other IB proxy object, for that matter. SomeClassView will have a delegate output connected to the owner / proxy object file, and itd will call the delegate methods in the appropriate places. For instance,

 @implementation SomeClassView - (void)awakeFromNib { SEL didAwakeFromNib = @selector(someClassViewDidAwakeFromNib:); if ([[self delegate] respondsToSelector:didAwakeFromNib]) { [[self delegate] performSelector:didAwakeFromNib withObject:self]; } } @end 

One more note: your code is currently losing an object of the view, because two objects are created: one through +alloc in your code, and the other through loading nib. You assign the last self , therefore, created with +alloc . In addition, I believe that you missed the super call in your third piece of code.

+3
source

Instead of doing this from the subclass itself, why not make sure that it is the correct class when you first create an instance from outside the class:

 SubClass *instance = [[SubClass alloc] initWithNibName:@"SomeClassView" bundle:nil]; 
0
source

Source: https://habr.com/ru/post/1346666/


All Articles