21

My structure of views:

UITableView
  UITableViewCell
    UIScrollView
      CustomView
        UIButton

The problem is UIButton doesn't work when I touch it. I create it with code:

btn = [[UIButton alloc] init];
[btn setImage:image forState:UIControlStateNormal];
[btn addTarget:self action:@selector(tileTouchUpInside) forControlEvents:UIControlEventTouchUpInside];
btn.layer.zPosition = 1;
[self addSubview:btn];

I've also add this:

scroll.delaysContentTouches = NO;
scroll.canCancelContentTouches = NO;

But my button doesn't work anyway. I can't cancelContentTouches or set delay for UITableView because if I set it, UITableView stop scrolling verticaly.

Thanks for any help!

George
  • 756
  • 1
  • 9
  • 16
  • have you implemented `tileTouchUpInside` method? – Ahmed Z. May 20 '13 at 12:48
  • 2
    Is userInteractionEnabled YES for CustomView? – Max Huk May 20 '13 at 13:01
  • @Capt.Hook - thanks, I've tried, didn't help. Also, want to add - when I just add CustomView to ViewController, withour scroll, it works perfectly. It stops work if placed in scrollview. – George May 20 '13 at 13:09
  • 1
    Check this question: [Allow UIScrollView and its subviews to both respond to a touch](http://stackoverflow.com/questions/11485100/allow-uiscrollview-and-its-subviews-to-both-respond-to-a-touch) – Max Huk May 20 '13 at 13:17
  • @Capt.Hook thanks, your link helps me. – George May 21 '13 at 09:04

9 Answers9

22

Create a subclass of UIScrollview with

- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
  return NO;
}

If you are not getting that message, set:

scrollView.delaysContentTouches = NO;

The problem is because of the way touchesShouldCancelInContentView behaves by default, it returns NO only if the subview is an UIControl, but your button is hidden inside the CustomView which isn't.

class
  • 8,621
  • 29
  • 30
Max Huk
  • 498
  • 3
  • 11
4

Came across the same scenario. UIScrollView in separate class and adding button through a custom view. 'NSNotificationCenter' helped me solving the issue. Sample code:

-(void)tileTouchUpInside:(id)sender
{    
    NSDictionary *dict=[NSDictionary dictionaryWithObject:@"index" forKey:@"index"];

    [[NSNotificationCenter defaultCenter]postNotificationName:@"Action" object:nil userInfo:dict];
}

In the class containing Scroll View:

- (void)viewDidLoad
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doAction:) name:@"Action" object:nil];
}
-(void)doAction:(NSNotification *) notification
{
    NSString* value =[notification.userInfo valueForKey:@"index"];
    // .......
}

Enable userInteraction for scroll view, custom view and button.

Siva
  • 51
  • 4
3

I guess you need to set the button frame. By default if you use btn = [[UIButton alloc] init]; method to initialize button it will be UIButtonTypeCustom. Also set a background color to it for debugging purpose to note down where the button is actually placed. Now for your case you need to set the frame for that button like btn.frame = CGRectMake(0,0,100,100);

For the inner UIScrollView implement this delegate method.

- (BOOL)touchesShouldCancelInContentView:(UIView *)view
  {
       return ![view isKindOfClass:[UIButton class]];
  }

This may work.

IronMan
  • 1,505
  • 1
  • 12
  • 17
3

None of the answers worked for me, because my problem was that the button was not visible at layout time or something...

FIX:

1) move the button out of the scrollView completely (to be subview of VC's main view).

main view's hierarchy

2) select the button and the element from your contentV you want your button located (in my case: after the last element in conventV)

button and last element selection

3) add the necessary constraints (in my case: to be bottom aligned)

enter image description here

4) Add the rest of the constraints for the button.

5) Enjoy.

Nikola Markovic
  • 301
  • 1
  • 13
  • thanks a lot! my solution is similar, i just had to add the autolayout constraints accordingly. tried many ways, using tap gestures, bringing view to front. nothing works. – Rejinderi Apr 01 '19 at 02:30
2

Your CustomView's frame could be interfering with your UIButton. Are they overlapping? Add your button to the CustomView just to test by [CustomView addSubview:UIButton]; (using your values of course). If it works, then your issue is most likely an overlap.

sangony
  • 11,636
  • 4
  • 39
  • 55
  • Thanks for idea. But when I just add my CustomView to ViewController, withour scroll, it works perfectly. It stops work if placed in scrollview. – George May 20 '13 at 13:12
  • If you are looking to just add a button for a tableview, take a look at this previous post... http://stackoverflow.com/questions/10229597/adding-uibutton-to-uitableview-cells – sangony May 20 '13 at 14:32
1

Add all subviews from CustomView to ScrollView. And hide the CustomView. Make sure CustomView(ContainerView of scrollView) should also be a subView of scrollView.

Jeet
  • 59
  • 1
  • 4
1

Button does not work in scroll view. I did spend lot of time struggling with this and looking for help. Finally I found this answer: Add buttons to UIScrollView Swift 5 programmatically where UIStackView made things easy.

My adaptation of the solution did go something like this:

override func viewDidLoad() {
    super.viewDidLoad()
    
    let subView = UIView()
    view.addSubview(subView)
    subView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        subView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0),
        subView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0),
        subView.widthAnchor.constraint(equalToConstant: 400),
        subView.heightAnchor.constraint(equalToConstant: 250),
    ])
    
    let scrollView = UIScrollView()
    subView.addSubview(scrollView)
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        scrollView.topAnchor.constraint(equalTo: subView.topAnchor, constant: 0),
        scrollView.bottomAnchor.constraint(equalTo: subView.bottomAnchor, constant: 0),
        scrollView.leadingAnchor.constraint(equalTo: subView.leadingAnchor, constant: 0),
        scrollView.trailingAnchor.constraint(equalTo: subView.trailingAnchor, constant: 0),
    ])
    
    let stackView = UIStackView()
    scrollView.addSubview(stackView)
    stackView.axis = .vertical
    stackView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        stackView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor, constant: 8.0),
        stackView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor, constant: 8.0),
        stackView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor, constant: -8.0),
        stackView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor, constant: -8.0),
    ])
    
    // Create what ever content you need to be shown in the scroll view.
    // In this example long view with a button somewhere in the middle.
    let content = UIView()
    stackView.addArrangedSubview(content)
    content.backgroundColor = .gray
    content.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        content.widthAnchor.constraint(equalTo: subView.widthAnchor, constant: -10),
        content.heightAnchor.constraint(equalToConstant: 1000.0),
    ])
    
    let button = UIButton(type: .system)
    button.frame = CGRect(x: 0, y: 0, width: 100, height: 50)
    button.backgroundColor = .blue
    button.setTitle("Button", for: .normal)
    button.center = CGPoint(x: 100, y: 400)
    content.addSubview(button)
    button.addTarget(self, action: #selector(click), for: .touchDown)
}

@objc func click(_ sender : UIButton) {
    print("Click")
}
jnswb
  • 31
  • 3
0

Link from Capt.Hook helped me. I used UIGestureRecognizer instead of UIButtons.

Allow UIScrollView and its subviews to both respond to a touch Use it, if you have the similar problem.

Community
  • 1
  • 1
George
  • 756
  • 1
  • 9
  • 16
-1

I had same issue & same hierarchy of the views, With latest sdk , just use it :

Setting delaysContentTouches to NO for UIButton in the same UITableViewCell.

self.tableView.delaysContentTouches = NO
Ash
  • 5,525
  • 1
  • 40
  • 34