简体   繁体   中英

UIButton target action inside custom class

I've been struggling with a bug, and I found a work-around, but I'd like to understand what is exactly going on. it has something to do with UIButton target actions misfiring depending on different subview hierarchies, inside a subclass.

Brief summary: I have a subclass of NSObject with a UIView property object, a UIButton attached to it, and a target added to the button calling a function inside the subclass. Inside the main ViewController, I init the subclass and add its view to the view stack, click the button, and it throws me to main.mm with the error - EXC_BAD_ACCESS, gives me little feedback. so the hierarchy looks like this:

-CustomClass
--UIView           <-this is added as a subview to the View Controller
---UIButton (onRelease calling a function)

so I fixed it by changing the custom class to be a subclass of UIView instead of NSObject, then add its @property UIView to be a subview of the custom class (and the button is still attached to the subview), and then in the main View Controller, I add the custom class itself as a subview, not the class's subview property object. then the button successfully calls the function. so the new arrangement looks like this:

-CustomClass (now UIView)     <-this is added as a subview to the View Controller
--UIView                      <-this is added as a subview to CustomClass
---UIButton (onRelease calling a function)

then, i realized i can just keep the CustomClass a subclass of UIView for both instances, the problem persists with the original setup if everything else is unchanged.

okay, more detail, here's code:

CustomClass: .h

@interface Temp : UIView
@property UIView *subview;
@property UIButton *but;
@end

.m

-(id) init{
    self = [super initWithFrame:[[UIScreen mainScreen] bounds]];
    if(self){
        _subview = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        //[self addSubview:_subview];  // FOR THE FIX
        _but = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        [_but setTitle:@"OKAY" forState:UIControlStateNormal];
        [_but setBackgroundColor:[UIColor blackColor]];
        [_but setFrame:CGRectMake(50, 50, 200, 200)];
        [_subview addSubview:_but];
        [_but addTarget:self action:@selector(pageTurn) forControlEvents:UIControlEventTouchUpInside];
    }
    return self;
}

-(void) pageTurn{
    NSLog(@"WORKS");
}

inside view controller:

Temp *temp = [[Temp alloc] init];
[self.view addSubview:temp.subview];
//[self.view addSubview:temp];  // FOR THE FIX, instead of above line

You're messing with all kinds of view hierarchy stuff you don't need to, which is likely the cause of the problem. I created a Test UIView subclass that had a UIButton instance variable that I added as a subview in the Test object, there's no need to add another view as a subview and then add the button to the subview, and then in your view controller add the button's subview to the view - it's WAY more complicated than it needs to be.

In a nutshell - Create the Temp UIView, add the button as a subview, then in your view controller class add the Temp UIView as a subview. Simple as that, here is the code:

- (id)init {
    self = [super initWithFrame:[[UIScreen mainScreen] bounds]];
    if(self){
        _but = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        [_but setTitle:@"OKAY" forState:UIControlStateNormal];
        [_but setBackgroundColor:[UIColor blackColor]];
        [_but setFrame:CGRectMake(100, 100, 200, 200)];
        [_but addTarget:self action:@selector(pageTurn) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:_but];
    }
    return self;
}

- (void)pageTurn {
    NSLog(@"WORKS");
}

Then added an instance to my view controller:

Test *temp = [[Test alloc] init];
temp.backgroundColor = [UIColor redColor];
[self.view addSubview:temp];

This was the result:

在此处输入图片说明

Who's holding onto temp ?

If temp isn't referenced by anyone then it's released. At that point the target is zombie and of course you will crash. temp.subview is being held by self.view .

In the second setup, adding temp as a subview of self.view keeps a reference to it.


You can fix this by adding a Temp * property in the view controller.

self.temp = [[Temp alloc] init];
[self.view addSubview:self.temp.subview];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM