views:

537

answers:

2

Hi,

I'm creating and adding an UIButton dynamically to UIView, like:

UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
btn.frame = CGRectMake(100, 10, btn.bounds.size.width, btn.bounds.size.height);
[btn addTarget:self action:@selector(myAction:) forControlEvents:UIControlEventTouchUpInside];
[myView addSubview:btn];

This works as expected, myAction gets called. The problem begins as I add this to the outside of the visible myView, like:

btn.frame = CGRectMake(-100, 10, btn.bounds.size.width, btn.bounds.size.height);

later on, I animate myView out of the screen to make btn visible:

[UIView beginAnimations:nil context:NULL];
[myView layer].frame = CGRectMake(320, 0, 320, 48);
[UIView commitAnimations];

But the thing is, myAction does not get called anymore. Why? And how to fix it?

+1  A: 

Two things I noticed in your code:

First, since frame is relative to a view's superview, not to the the main screen btn.frame should not be at the offscreen location, but at the relative location in myView. Assuming myView is already at some negative x location, the button will be there, too.

Second, when you use Core Animation, you should set the UIView property you want to animate, not the UIView's layer's property.

I tested this to make sure, here's my code:

[super viewDidLoad];
offscreenView = [[UIView alloc] initWithFrame:CGRectMake(-100, 0, 200, 200)];
offscreenView.backgroundColor = [UIColor blueColor];
UIButton * btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
//note we put this at 10, 10 *relative to the button's superview*
btn.frame = CGRectMake(10.0, 10.0, 50.0, 25.0);
[btn addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[offscreenView addSubview:btn];
[self.view addSubview:offscreenView];
[UIView beginAnimations:@"animateVIew" context:self];
offscreenView.frame = CGRectMake(0.0, 0.0, 200, 200);
[UIView setAnimationDuration:2.0];
[UIView commitAnimations];

Hope that helps!

Art Gillespie
thanks for code, but this is not exactly my problem. Here's the flow:1. I create myView on screen: CGRectMake(0, 0, 320, 48)2. I'm adding btn *to the left* of myView, like CGRectMake(-100, 0, 10, 10), so that it becomes visible when I....3. ...move myView to the right (out of screen): CGRectMake(320, 0, 320, 48)The problems seem to be by adding btn to the outside of myView.Anyway thanks for tip with animating frame and not layer (but that didn't help though)
Konstantin
Art, just change your initial btn.frame = CGRectMake(10.0, 10.0, 50.0, 25.0) to btn.frame = CGRectMake(200.0, 10.0, 50.0, 25.0) and you'll see my problem.
Konstantin
Oh, I see. Here's my understanding of the problem: You won't get events when a `UIView` is outside of its superview's bounds. Events are passed down the view hierarchy based on the geometry of containing views. So imagine `myView`'s parent getting the touch event—it asks all of its subviews "is this event inside your geometry?" Since in the case of `btn` it's not, `myView` doesn't get the event and it's never passed along to `btn.`
Art Gillespie
OK, good to know! And is there any solution to this?
Konstantin
Not sure... you could try implementing `hitTest:withEvent` in `myView`'s superview and handle the hit testing yourself, but I think the simplest solution is to have another, transparent view that is the superview of both `myView` and `btn`.
Art Gillespie
+1  A: 

Your comment to Art's answer pretty much explains the problem. You have a button which is outside the visible area of its parent; it's irrelevant if the parent is positioned such that the button would be visible higher up on the stack. The button is getting clipped and not painted.

You have two choices.

1) Make myView's frame bigger and adjust all your coordinates appropriately. myView will always have some of its content offscreen and some onscreen and you can simply position it where you want. When the button is onscreen it will be visible. (E.g., in your code above the initial frame for myView would start at some negative value for x and extend to much wider than the iPhone screen, whereas your btn would be positioned at some positive x,y position relative to the top left of myView. Then you move myView so that, for example, it moves to 0,0, pushing some of its content offscreen while moving btn onscreen.)

2) Don't put button on myView; put it on a new view that you animate in while you are animating myView out.

Simple code for #1:

// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
    UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(-100, 0, 420, 480)]; // start 100 pixels to the left
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
    btn.frame = CGRectMake(0, 10, btn.bounds.size.width, btn.bounds.size.height);
    [myView addSubview:btn];

    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [btn1 setTitle:@"DoIt" forState:UIControlStateNormal];
    btn1.frame = CGRectMake(100, 100, 50, 50);
    [btn1 addTarget:self action:@selector(doit:) forControlEvents:UIControlEventTouchUpInside];
    [myView addSubview:btn1];
    self.view = myView;
}

- (void) doit: (id) bt {
    [UIView beginAnimations:nil context:NULL];
    self.view.frame = CGRectMake(100, 0, 420, 480);
    [UIView commitAnimations];
}
ddoughty
thanks, I think I'll go with #2. BTW, button is not clipped and always visible. It just doesn't get clicked.
Konstantin
Whoops, yes, sorry. It's the event handling rather than the painting that's getting stopped, but the workaround is the same in any event. If you use the code above (edited for typo) you can see the button responds to the press after it's scrolled onscreen.
ddoughty