dzenbot / DZNEmptyDataSet

A drop-in UITableView/UICollectionView superclass category for showing empty datasets whenever the view has no content to display
https://www.cocoacontrols.com/controls/dznemptydataset
MIT License
12.09k stars 1.73k forks source link

Button tap on custom view #263

Open whitefoxy opened 8 years ago

whitefoxy commented 8 years ago

I have created custom view for Empty tableview and there is a button which can be tapped, but problem is that button not recognize any touch it is like a static element.

Button have:

button.addTarget(self, action: #selector(TestController.buttonTap(_:)), forControlEvents: UIControlEvents.TouchUpInside)

Also I have added:

func emptyDataSetShouldAllowTouch(scrollView: UIScrollView!) -> Bool {
    return true
}

And the method emptyDataSetDidTapView also is not called, when I am touching the view.

How can I make a button touchable?

ghost commented 8 years ago

I have the same problem. I searched in the closed issues and found out that the author had claimed fixing this but I can't make it work. I'm using the latest version from Cocoapods

sasojadrovski commented 8 years ago

I have the same problem.

martinciu commented 8 years ago

I have the same issue. It works alright in version 1.7.3

xumoyan commented 8 years ago

i submit an issues #265 ,you will know why button can't tap on customView.

sasojadrovski commented 8 years ago

Hey @dzenbot,

Could you please take a look at this? For some reason if a custom view is used, and a button is present inside the custom view, the tap event is not transferred.

For some reason the UITableViewWrapperView is above the custom view and therefore the tap event is not triggered. The hitTest is invoked, but it cannot find the UIControl i.e. the UIButton that is tapped.

Removing this line

[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[customView]|" options:0 metrics:nil views:@{@"customView":_customView}]];

puts the custom view to the bottom of the UIScrollView, and "solves" the problem, but of course, this behaviour is not something that we all want.

Thanks in advance for looking into this. Any feedback is appreciated!

Cheers, Sasho

coderxdotwang commented 8 years ago

Running into the same issue. " UIView *hitView = [super hitTest:point withEvent:event]; " hitView is DZNEmptyDataSetView itself.

DaiYue commented 8 years ago

I had run into the same problem, and I used the author's fix and solved the problem. Thanks dzenbot!

jcbernabe commented 8 years ago

As per checking based on @FunnyCoderWang 's comment. I noticed that when using a customView the height of contentView is zero. I think that's why the hitTest is giving UIView *hitView = DZNEmptyDataSetView

jeffreylyg commented 8 years ago

Hi, @dzenbot , Could you fix the problem? The custom view's height is zero so the button on it can not be clicked.

dzenbot commented 8 years ago

This has been a long standing bug, and haven't yet figure out a good way of fixing this from within the library. Instead, your custom view should override intrinsicContentSize if using auto-layout, and make sure that the view's height isn't zero, just like @jcbernabe stated.

thanhcao commented 8 years ago

Change hitTest function like below code:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if(_customView != nil){
        CGRect newFrame = _customView.frame;
        newFrame.size.height = self.frame.size.height;
        _customView.frame = newFrame;
        UIView *hitView = [_customView hitTest:point withEvent:event];
        return hitView;
    }else{
        UIView *hitView = [super hitTest:point withEvent:event];

        // Return any UIControl instance such as buttons, segmented controls, switches, etc.
        if ([hitView isKindOfClass:[UIControl class]]) {
            return hitView;
        }

        // Return either the contentView or customView
        if ([hitView isEqual:_contentView] || [hitView isEqual:_customView]) {
            return hitView;
        }
        return nil;
    }

}
backmeupplz commented 8 years ago

In case anybody wonders: the latest version of pods didn't fix the issue but code by @thanhcao did :)

jpm commented 8 years ago

The last comment from @dzenbot is the real fix for this issue, given a custom view that uses auto-layout.

My case was a custom UIView subclass that was laid out as a NIB, and then loaded into -[CustomClass initWithFrame:] with -[NSBundle loadNibNamed:owner:options:].

My custom view had two embedded UIButton widgets, and none of them were responding to touches. I implemented intrinsicContentSize and that fixed the event handling issue:

- (CGSize)intrinsicContentSize
{
    return CGSizeMake(272.0f, 172.0f);
}
ad0ma commented 8 years ago

2016.10.27 update Step1: Method -> setupConstraints add ->

NSLayoutConstraint *heightConstraint = [self equallyRelatedConstraintWithView:self.contentView attribute:NSLayoutAttributeHeight]; 

if (_customView) {
        [self addConstraint:heightConstraint];
    }

Step2: Method-> didMoveToSuperview add ->

if (_customView) {
        [self.superview bringSubviewToFront:self];
    }

Can fix this!

rajderks commented 8 years ago

For me the above suggestions did not suffice. The point's origin.y interfered with the hitTest on _customView. In order to fix this I altered the code a bit. Validated in two projects when setting a customView from code with no constraints. (_customView constraints unaltered.)

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if(_customView != nil){
        _customView.frame = self.frame;
        point = [super convertPoint:point toView:_customView];
        UIView *hitView = [_customView hitTest:point withEvent:event];
        return hitView;
    }else{
        UIView *hitView = [super hitTest:point withEvent:event];

        // Return any UIControl instance such as buttons, segmented controls, switches, etc.
        if ([hitView isKindOfClass:[UIControl class]]) {
            return hitView;
        }

        // Return either the contentView or customView
        if ([hitView isEqual:_contentView] || [hitView isEqual:_customView]) {
            return hitView;
        }
        return nil;
    }

}
okankocyigit commented 7 years ago

@jcbernabe

As per checking based on @FunnyCoderWang 's comment. I noticed that when using a customView the height of contentView is zero. I think that's why the hitTest is giving UIView *hitView = DZNEmptyDataSetView

When I read this comment I found the problem.

It's actually a common problem if you are creating a view from xib file which uses auto-layout.

If you are using auto layout you don't actually need to set height for root view, be sure that you have top and bottom constraints for each elements.

In my case I forgot to add bottom constraint for most bottom element, when I set zero (or 5 for padding) hitTest returns my button, and it all works.

So you don't need to override intrinsicContentSize.

summertian4 commented 7 years ago

I have the same problem, and I watched layout of views, then I found the contentView has a error frame. In UIScrollView+EmptyDataSet.m contentView does not set up layout correctly. So, I change the code that starts at line 935 :

    // If applicable, set the custom view's constraints
    if (_customView) {
        // Complete setup the layout of contentView
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:nil views:@{@"contentView": self.contentView}]];
        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[customView]|" options:0 metrics:nil views:@{@"customView":_customView}]];
        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[customView]|" options:0 metrics:nil views:@{@"customView":_customView}]];
    }
pmlbrito commented 6 years ago

@martinciu was the fastest solution... downgraded the version to 1.7.3 and it started magically working

romeugodoi commented 6 years ago

@summertian4 give me a simple solution above. Now it works!!! Thanks!! Why has not this become a PR so far? Someone know?

akerdi commented 6 years ago

I implete with @jpm told, I have to set customView.userinteractionEnabled = NO; and delegate "emptyDataSet:didTapView:" will be resolve finally. If i add gesture or button, I test @rajderks 's advise ,then it success. current version: 1.8.1

Wilsonilo commented 5 years ago

I'm just trying to keep this alive because it is a weird bug. I had two views, one was presenting problems, the other did not, constraints are almost similar, but i had to apply a override just like @jpm mentioned to jump the problem.

override var intrinsicContentSize: CGSize { return CGSize(width:bounds.width, height: bounds.height) }

liubiaocong commented 5 years ago

@jcbernabe

As per checking based on @FunnyCoderWang 's comment. I noticed that when using a customView the height of contentView is zero. I think that's why the hitTest is giving UIView *hitView = DZNEmptyDataSetView

When I read this comment I found the problem.

It's actually a common problem if you are creating a view from xib file which uses auto-layout.

If you are using auto layout you don't actually need to set height for root view, be sure that you have top and bottom constraints for each elements.

In my case I forgot to add bottom constraint for most bottom element, when I set zero (or 5 for padding) hitTest returns my button, and it all works.

So you don't need to override intrinsicContentSize.

Very good!