1

I've managed to setup a custom UIView class with a nib.

My .h looks like

@interface MyView : UIView <UITextFieldDelegate>

@property (nonatomic, weak) IBOutlet UITextField *textField;
@property (nonatomic, strong) MyView *topView;

And .m

 @implementation MyView

NSString *_detail;

    -(id)initWithCoder:(NSCoder *)aDecoder{
            if ((self = [super initWithCoder:aDecoder])&&self.subviews.count==0){

                MyView *v = [[[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil] objectAtIndex:0];
                self.textField = v.textField;
                if (self.topView == nil)self.topView = self;
                v.topView = self.topView;
                [self addSubview:v];
            }
            return self;
        }


    -(NSString *)topDetail{
        return _detail;
    }
    -(NSString *)detail{
        return [self.topView topDetail];
    }
    -(void)setTopDetail:(NSString *)detail{
        _detail = detail;
    }
    -(void)setDetail:(NSString *)detail{
        [self.topView setTopDetail:detail];
    }
    - (BOOL)textFieldShouldReturn{
        //here I show an UIAlertView using self.detail for the message
    }

Note: The setup I have works exactly how I want it to.

The problem
What I would like to do is remove my manual detail methods and turn NSString *_detail into @property (...)NSString *detail

When I try it with the @property, then within my ViewController if i call
myView.detail = someString, myView will be referring to the top most view. Then if textFieldShouldReturn gets called because of user interaction, then it calls the nested MyViews _detail which has not been set.

What I want: To not have to write extra code for access to _detail regardless of where I'm accessing it from. I want to merely declare the property and go on with my usual coding.

Reed
  • 14,703
  • 8
  • 66
  • 110
  • Is your example correct? The class you've define is called `MyView`, but it also has a `@property` called `topView` of class `MyView`. Nevertheless, you should just remove the setters and getters out of your .m file. You are setting it on the `@property` `TopView` rather than on yourself. – Will Mar 22 '14 at 01:06
  • Do assignment once in your init and make weak properties? Like the first commenter I'm confused about what's being subclassed or not here. – stevesliva Mar 22 '14 at 04:28
  • `topView` is `strong` (sorry, edited my question) and is not on the nib. I keep `topView` assigned to the initial `MyView` which is initialized by the storyboard with `initWithCoder`. I have that because it maintains a reference to the top level object, which is the only object my `ViewController` will have access to. If I use `@property NSString *detail` without the custom getters/setters, then when my `ViewController` does `myView.detail = someString` it sets the top level view. Then when `textFieldShouldReturn` gets called, it's calling the bottom level view. – Reed Mar 22 '14 at 16:26
  • Why do you hold a strong reference to self in self.topView? First I don't see a reason for that and second I fear that this could cause a memory leak when objects have strong references to themselfs. – Hermann Klecker Mar 22 '14 at 17:03
  • And, as others said, the reference textField should be strong as you are allocating the object yourself. – Hermann Klecker Mar 22 '14 at 17:04
  • I thought `topView` would need to be strong, but have now switched it to weak and it's working fine. I am new to iOS/Objective C, but as I understand it, `textField` can be `weak` because it is part of the view hierarchy once I call `[self addSubview:v]` – Reed Mar 22 '14 at 18:17

2 Answers2

1

Your problem is that you're trying to keep the a class reference, topView, with an object property.

In other words every objects' topView is the object itself, which makes no sense.

Your definition should be:

@interface MyView : UIView <UITextFieldDelegate>

// Class "properties"
+ (instancetype)topview;
+ (void)setTopView:(UIView *)topView;

// Object properties
@property (nonatomic, weak) IBOutlet UITextField *textField;
@property (nonatomic, strong) NSString *detail;

Now you can keep track of the topView:

 static MyView * _topView;

 @implementation MyView

+ (instancetype)topView {return _topView}; // You could also create one here lazily
+ (void)setTopView:(UIView *)topView { _topView = topView };

-(id)initWithCoder:(NSCoder *)aDecoder{
            if ((self = [super initWithCoder:aDecoder])&&self.subviews.count==0){

                JUITextFieldHint *v = [[[NSBundle mainBundle] loadNibNamed:@"JUITextFieldHint" owner:self options:nil] objectAtIndex:0];
                self.textField = v.textField;
                if ([MyView topView] == nil)[MyView setTopView:self];
                v.topView = self.topView;
                [self addSubview:v];
            }
            return self;
        }

No more need for manual setters and getters. Now you can use your detail property, either with anyInstance.detail or [MyView topView].detail, or even MyView.topView.detail if you like dots like me ;)

You're init method still looks weird but should work. Check Apples init template.

Lastly, textField can be weak as long as it has a superview, otherwise make it strong.

Rivera
  • 10,792
  • 3
  • 58
  • 102
0

My xib contained one UIView (no controller). I had the UIView set to MyView for the class.

I changed the UIView back to just UIView then set File's Owner to MyView. This solved issues of recursion (which is why I had such a weird setup in the first place) and caused my variables and IBOutlets to be linked up properly.

Credit goes to How do I create a custom iOS view class and instantiate multiple copies of it (in IB)? and some of the comments which I missed the first couple times I read through it.

Community
  • 1
  • 1
Reed
  • 14,703
  • 8
  • 66
  • 110