Azu Azu - 5 months ago 16
iOS Question

UIButton in a UIView does not respond to user tapping when using Auto Layout (iOS)

I am trying to create a grid like menu that can be shown if rightBarButton is being tapped. It seems that the layout is correct (see pic below), but the cell (each of which is a UIButton) doesn't responds to user tapping.

screenshot of the dropdown

The dropdown (the part that is marked with red frame) is composed of two views. One is GridCell and the other GridCollection. GridCell is simple a view containing a UIButton that occupies the whole of its container. GridCollection init GridCells and positions them in order.

The following is the code extracted from GridCell

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

- (id)initWithColor:(UIColor *)color image:(UIImage *) cellImage title:(NSString *) title {
self = [self init];
if(self) {
self.backgroundColor = color;
self.image = cellImage;
self.title = title;
if(title == nil) self.title = @"default";
}
return self;
}

- (void)updateConstraints {
[super updateConstraints];
[self setTranslatesAutoresizingMaskIntoConstraints:NO];

//button should fill container
UIButton *button = [[[UIButton alloc] init] autorelease];
self.cellButton = button;
[self.cellButton addTarget:self action:@selector(cellTapped) forControlEvents:UIControlEventTouchUpInside];
[self.cellButton setTitle:self.title forState:UIControlStateNormal];
[self.cellButton setTitle:@"pressed" forState:UIControlStateHighlighted];
[self.cellButton setTitleColor:[UIColor blueColor] forState:UIControlStateHighlighted];
[self.cellButton setBackgroundImage:self.image forState:UIControlStateNormal];
[self.cellButton setUserInteractionEnabled:YES];
[self.cellButton setTranslatesAutoresizingMaskIntoConstraints:NO];

[self addSubview:self.cellButton];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[_cellButton]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_cellButton)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[_cellButton]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_cellButton)]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:60]];
}

- (void)cellTapped {
NSLog(@"cell %d pressed ", self.tag);
}

- (void)layoutSubviews {
[super layoutSubviews];
NSLog(@"Printing button's frame: %@", NSStringFromCGRect(self.cellButton.frame));
}


The following is the code extracted from GridCollection

- (void)updateConstraints {
[super updateConstraints];
GridCell *previousCell = nil;
GridCell *firstCell = nil;
for (int i = 1; i <= self.numberOfCells; ++i) {
GridCell *cell = [[[GridCell alloc] initWithColor:i % 2 == 0 ? [UIColor brownColor] : [UIColor grayColor] image:nil title:[NSString stringWithFormat:@"Cell %d", i]] autorelease];
cell.tag = i;
[self addSubview:cell];
[cell setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addConstraint:[NSLayoutConstraint constraintWithItem:cell attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeWidth multiplier:1 constant:self.superview.frame.size.width/4]];
if (!previousCell) { //pin to top left
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[cell]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(cell)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[cell]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(cell)]];
firstCell = cell;
} else { //pin to previous
if(i % 4 == 1 && i != 1) {
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[firstCell][cell]" options:0 metrics:nil views:@{@"cell" : cell, @"firstCell" : firstCell}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[cell]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(cell)]];
} else {
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[previousCell]-0-[cell]" options:NSLayoutFormatAlignAllTop metrics:nil views:NSDictionaryOfVariableBindings(previousCell, cell)]];
}
}
previousCell = cell;
}
[self layoutIfNeeded];
}


For debugging purpose, I've put
NSLog(@"Printing button's frame: %@", NSStringFromCGRect(self.cellButton.frame))
in GridCell's layoutSubview method. And I found that they all return 0,0 for button's origin. And I think perhaps this is the reason why my UIButton in the cell did not respond, is that right?

2014-08-04 21:36:42.837 projectName[38661:60b] Printing button's frame: {{0, 0}, {80, 60}}
2014-08-04 21:36:42.838 projectName[38661:60b] Printing button's frame: {{0, 0}, {80, 60}}
2014-08-04 21:36:42.839 projectName[38661:60b] Printing button's frame: {{0, 0}, {80, 60}}
2014-08-04 21:36:42.840 projectName[38661:60b] Printing button's frame: {{0, 0}, {80, 60}}
2014-08-04 21:36:42.840 projectName[38661:60b] Printing button's frame: {{0, 0}, {80, 60}}
2014-08-04 21:36:42.841 projectName[38661:60b] Printing button's frame: {{0, 0}, {80, 60}}
2014-08-04 21:36:42.842 projectName[38661:60b] Printing button's frame: {{0, 0}, {80, 60}}
2014-08-04 21:36:42.843 projectName[38661:60b] Printing button's frame: {{0, 0}, {80, 60}}


I am wondering how can I position cells' origin correctly in this occasion.

Thanks in advance.

Azu Azu
Answer

After printing my view hierarchies, I found that it's because I did not assign height to my parent view gridview. Giving a height to that view solved this problem.

<GridView: 0x9b625a0; frame = (0 64; 320 0); tag = 100; layer = <CALayer: 0x8e4e720>>
   |    <GridCell: 0x8e56db0; frame = (0 0; 80 60); tag = 1; layer = <CALayer: 0x8c680c0>>
   |       | <UIButton: 0x8ca6170; frame = (0 0; 80 60); opaque = NO; gestureRecognizers = <NSArray: 0x8caea80>; layer = <CALayer: 0x8ca6260>>
   |    <GridCell: 0x8ca3ae0; frame = (80 0; 80 60); tag = 2; layer = <CALayer: 0x8ca47b0>>
   |       | <UIButton: 0x8e57990; frame = (0 0; 80 60); opaque = NO; gestureRecognizers = <NSArray: 0x8e57a80>; layer = <CALayer: 0x8e57680>>
   |    <GridCell: 0x8cae620; frame = (160 0; 80 60); tag = 3; layer = <CALayer: 0x8ca7450>>
   |       | <UIButton: 0x8e58490; frame = (0 0; 80 60); opaque = NO; gestureRecognizers = <NSArray: 0x8e585d0>; layer = <CALayer: 0x8e58450>>
   |    <GridCell: 0x8caf4d0; frame = (240 0; 80 60); tag = 4; layer = <CALayer: 0x8c657d0>>
   |       | <UIButton: 0x8e58fc0; frame = (0 0; 80 60); opaque = NO; gestureRecognizers = <NSArray: 0x8e59100>; layer = <CALayer: 0x8e58f80>>
   |    <GridCell: 0x8ca82e0; frame = (0 60; 80 60); tag = 5; layer = <CALayer: 0x8cb5450>>
   |       | <UIButton: 0x8e59af0; frame = (0 0; 80 60); opaque = NO; gestureRecognizers = <NSArray: 0x8e59c30>; layer = <CALayer: 0x8e59ab0>>
   |    <GridCell: 0x8caed70; frame = (80 60; 80 60); tag = 6; layer = <CALayer: 0x8caedf0>>
   |       | <UIButton: 0x8e5a620; frame = (0 0; 80 60); opaque = NO; gestureRecognizers = <NSArray: 0x8e5a760>; layer = <CALayer: 0x8e5a5e0>>
   |    <GridCell: 0x8caf260; frame = (160 60; 80 60); tag = 7; layer = <CALayer: 0x8caf300>>
   |       | <UIButton: 0x8e5b170; frame = (0 0; 80 60); opaque = NO; gestureRecognizers = <NSArray: 0x8e5b2b0>; layer = <CALayer: 0x8e5b130>>
   |    <GridCell: 0x8cb0cc0; frame = (240 60; 80 60); tag = 8; layer = <CALayer: 0x8cb0d60>>
   |       | <UIButton: 0x8e5bca0; frame = (0 0; 80 60); opaque = NO; gestureRecognizers = <NSArray: 0x8e5bde0>; layer = <CALayer: 0x8e5bc60>>