Daniel Retief Fourie
Daniel Retief Fourie

Reputation: 626

UIButton not working correctly in iOS 7

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!

Upvotes: 8

Views: 14694

Answers (10)

Neil Galiaskarov
Neil Galiaskarov

Reputation: 5073

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];
}

Upvotes: 7

Anjaneyulu Battula
Anjaneyulu Battula

Reputation: 1960

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

Upvotes: 1

david72
david72

Reputation: 7307

Check that the button in inside parent frame

Upvotes: 0

user3579410
user3579410

Reputation: 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.

Upvotes: -1

Michal Gumny
Michal Gumny

Reputation: 1770

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]];

Upvotes: 1

Nianliang
Nianliang

Reputation: 3076

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.

Upvotes: 0

user920877
user920877

Reputation: 33

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

Upvotes: 1

Guntis Treulands
Guntis Treulands

Reputation: 4762

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 .

Upvotes: 2

Marcel Wolterbeek
Marcel Wolterbeek

Reputation: 3702

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.

Upvotes: 1

Daniel Retief Fourie
Daniel Retief Fourie

Reputation: 626

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!

Upvotes: 17

Related Questions