8

I have a problem with a UIButton that works perfectly well in iOS6, but fails to respond to touch events in iOS7 up to a certain point. To clarify please see below image:

ios7_button_issue

The button that fails is the "Discard All" button that is in the UIView. (Please note, this button is only disabled temporarily and that is NOT the issue. I just don't have a screenshot of the newest test where the button is enabled")

This button ignores all touches, unless one first presses the "Discard" or "Retry" buttons in the UITableViewCell. (This does cause a reload of the view controller, which triggers the lifecycle methods like ViewDidLoad to be called again.) After either the "Discard" or "Retry" buttons in the table view cell have been pressed, the "Discard All" button starts functioning correctly.

The view and the "Discard All" button are build on the Controller's XIB file and not in code. This only fails on iOS7, and starts working as soon as the taleview cell buttons are touched.

Anyone have any ideas?

Thanks!

Daniel Retief Fourie
  • 626
  • 2
  • 10
  • 20

10 Answers10

17

I found the solution last night. Okay, so what happens is that I put the above table view and UIView elements onto a target frame.

I'm not 100% sure, but it seems that in iOS6 the buttons respond to events irrespective of where they are placed. For some reason in iOS7 when the button sits outside of the frame it is supposed to be in, it ignores touch events, even though it does get displayed.

I solved the problem by positioning the view's frame in the correct place so it overlays the button.

If I can find any documentation around this, I will post here.

Thanks!

Daniel Retief Fourie
  • 626
  • 2
  • 10
  • 20
  • 2
    Please elaborate ur answer. I am facing the same issue. – Sahil Mahajan Oct 01 '13 at 07:51
  • 4
    What I did to find the problem is I set each UIView's background colour to something different and obviously visible. Basically, the UIViewController, had another UIViewController embedded within it. This embedded view controller's frame was set so that the button was not inside the frame. That is why it didn't work. I increased the size of the frame so that the UIView with the button in it would be inside this frame. If you would like to you can send me your code / xib's so I can have a look if that isn't clear. – Daniel Retief Fourie Oct 01 '13 at 10:46
  • 5
    I solved my Issue by setting userInteractionEnabled = NO of cell.contentView. :) Thanx for replying :) Happy coding – Sahil Mahajan Oct 01 '13 at 12:11
  • Ah yes, that is a good solution. Except in my case the broken button was not a tableView cell, and hence it had no contentView property. And Happy coding to you too :D – Daniel Retief Fourie Oct 02 '13 at 08:46
  • 1
    When I removed `[tableHeaderView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];`from my iOS7 specific code, my UIButton started working again. Otherwise the tableHeaderView did not have a correct frame to work with. – Webdevotion Oct 03 '13 at 19:07
  • Neither setting userInteractionEnabled to NO (or to YES) nor making sure the button's rect is within the contentView rect nor removing my autoresizing mask solved this for me. Changing to a regular text button. Custom button not responsive. – Alyoshak Oct 10 '13 at 20:33
7

I have just faced to this problem. According to @Guntis Treulands advice, I decided to check what happens if I override hitTest:withEvent: method in my custom header view. This method ignores view objects that are hidden, that have disabled user interactions, or have an alpha level less than 0.01. This method does not take the view’s content into account when determining a hit. Thus, a view can still be returned even if the specified point is in a transparent portion of that view’s content and now, after it has been overridden, receives touches outside the bounds. It did the trick for me. Hope it helps you, guys.

swift

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    guard !isHidden, alpha > 0 else {
      return super.hitTest(point, with: event)
    }
    
    for subview in subviews {
      let subpoint = subview.convert(point, from: self)
      let result = subview.hitTest(subpoint, with: event)
      if result != nil {
        return result
      }
    }
    return super.hitTest(point, with: event)
}

Objective-C

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (!self.clipsToBounds && !self.hidden && self.alpha > 0) {
        for (UIView *subview in self.subviews.reverseObjectEnumerator) {
            CGPoint subPoint = [subview convertPoint:point fromView:self];
            UIView *result = [subview hitTest:subPoint withEvent:event];
            if (result != nil) {
                return result;
            }
        }
    }
    // use this to pass the 'touch' onward in case no subviews trigger the touch
    return [super hitTest:point withEvent:event];
}
Neil Galiaskarov
  • 5,015
  • 2
  • 27
  • 46
2

Just as an alternative answer - Actually for me, this has never worked (if uibutton is outside it's parent view, then it will not receive touch.) - But in some cases, it is required to have the button outside it's boundaries.

For that reason, there is a hittest function, which can be overridden - to simply check parent view all subviews to find uibutton that is placed outside parent views boundaries. (You would then check each subview, if it is uibutton type, and if touched coordinates are inside uibuttons frame.

By default hittest function skips checking views, that are outside boundaries.

Hittest example: https://stackoverflow.com/questions/17246488/best-way-to-perform-the-hit-test-objective-c .

Community
  • 1
  • 1
Guntis Treulands
  • 4,764
  • 2
  • 50
  • 72
1

Not sure if this is the same case, but it might be related:

When the UIButton is in a view with a UIGestureRecognizer which did NOT set delaysTouchesEnded to NO (default is YES), the button won't get TouchUpInside events anymore in iOS 7. It DID get the TouchUpInside events in iOS 6.

If possible, set the delaysTouchesEnded property of the UIGestureRecognizer to NO to solve the problem.

Marcel Wolterbeek
  • 3,367
  • 36
  • 48
1

This is weird but I was developing a new app for iOS 7 and setting [cell.contentView setUserInteractionEnabled: YES]; actually worked for me.

krsteeve
  • 1,794
  • 4
  • 19
  • 29
user920877
  • 33
  • 3
1

I had this same issue - buttons did work on iOS6 and didn't on iOS7, problem was with Container UIView to which one I was adding UIButtons, I add constrains to container UIView and it started working. Code that I had to add in my case:

[superView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:    [_controllersView]-|" options:0 metrics:nil views:views]];
[superView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_controllersView(70)]" options:0 metrics:nil views:views]];
[superView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_controllersView]|" options:0 metrics:nil views:views]];
Unheilig
  • 16,196
  • 193
  • 68
  • 98
Michal Gumny
  • 1,770
  • 1
  • 16
  • 24
1

I also got same problem but after adding below code button actions working properly cell.contentView.userInteractionEnabled = NO;

Anjaneyulu Battula
  • 1,910
  • 16
  • 33
0

If it's using auto layout, then try adding a log statement in -viewDidAppear:

NSLog(@"Height of UIView: %f",  self.<name of UIView>.frame.size.height);

if the result is 0 (or not large enough), then set the height of UIView large than the height of 'Discard All' button.

Nianliang
  • 2,926
  • 3
  • 29
  • 22
0

Check that the button in inside parent frame

david72
  • 7,151
  • 3
  • 37
  • 59
-1

Okay, so seems like buttons programatically created in iOS7 won't call their target. I don't know why, but I've found the following solution:

  1. In the InterFace Builder (or nib) add a button and customize and HIDE it. ( theButtonInTheNib);

  2. Make it IBOutlet property: @property (strong) IBOutlet UIButton *theButtonInTheNib

  3. Make the target IBAction connection: (IBAction)theTargetAction:(id)sender;

Here comes the trick. We will make a copy/or copies of this button:

NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:theButtonInTheNib];
UIButton *theCopyButton = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
[theCopyButton setHidden:NO];
[theCopyButton setFrame:CGRectMake(x, y, width, height)];
[theCopyButton addTarget:self action:@selector(theTargetAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:theCopyButton];

I have added again the target action, but any other action will work.

Alexey Malev
  • 6,408
  • 4
  • 34
  • 52