custom UIView in UITableViewCell blocks uitableviewcell from reciving an event - ios

I have a custom UITableViewCell i.e. I subclassed UITableViewCell class
when populating each cell (or row) of the uitableview, i am creating my custom UITableViewCell, which composes of an image and some labels.
now when I override the following method in my custom 'UITableViewCell' class,
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"CLICKED CLICKED CELL CLICKED CLICKED");
[super touchesBegan:touches withEvent:event];
}
it never gets called if i click on the custom views i added to the my custom uitableviecell, but it is fine (event triggered) when i click on the region where there is no custom view on top of it.
my goal is simple, i want to detect any touches in each row (uitableviewcell) in the uitableview.

I have experienced a similar problem today, I was unable to select the cell, since it had a custom UIView with a scrollView on it. Try setting userInteractionIsEnabled = false on your subviews of your UIView.

plz try to override this method
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event {}

Related

Pass touches on some part of UICollectionView to underlying views

First take a look at the attacthed image:
My hiearchy looks as following:
UIView
UIButton
UICollectionView
UICollectionViewCell
UICollectionViewCell
UICollectionViewCell
UICollectionViewCell
UICollectionView is added to UIVew as H:[collectionView(==270)]| and V:|-(70)-[collectionView]-(70)-|.
I have two requirements:
1) only the blue part of the UICollectionViewCell should trigger the collectionView::didSelectItemAtIndexPath: method. Which I have sucesfully implemented with
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self.customView) {
return self;
}
return nil;
}
on the custom class UIColectionViewCell. The customView property is the blue/red UIView of the cell.
This works as expected.
2) I want any gestures (Tap/LongTap/Pan/...) that are done in the green part of the UICollectionViewCell or in the UICollectionVIew itself (the background that is here white with 0.5 opacity) to be passed down to the superview. For Example the turquoise UIButtion below.
The green part should also not scroll the CollectionView .. it just has to be completly transparent to any gestures.
How can I do that? Tried a lot of different approaches but none of them worked. I know how to do it with a regular UIView, but cannot get it to work on a UICollectionView and its cells.
Keep in mind that there will be other UIViews or UIButtions that will be interactable and placed under the green part of collection view. The green color will later just be UIClearColor.
Suggestion to just have the UICollectionView smaller width (width of the blue part) is not an option since the Blue/Red part of UICell has to strecth out to full width of the cell in some cases.
Here is the final solution for above example:
1) Require only red/blue part to be tappable.
This stays basically the same. I have a reference to the blue/red UIView names customView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self.customView) {
return self;
}
return nil;
}
2) Any interactions inside the UICollectionView that are not done on blue/red UIViews should be ignored.
Subclassing UICollectionView and adding overwriting this metod:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
NSIndexPath *indexPath = [self indexPathForItemAtPoint:point];
LCollectionViewCell *cell = (LCollectionViewCell*)[self cellForItemAtIndexPath:indexPath];
if (cell && [self convertPoint:point toView:cell.customView].x >= 0) {
return YES;
}
return NO;
}
This will check if the CGPoint is done on the customView. With this we are also supporting variable widths:
Your answer helped me a ton, thanks!
Here's the solution in Swift 3.0:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if hitView == self.customView {
return self
}
return nil
}

tableView cells can't be selected with hitTest: WithEvent:

I have a map and above it UIScrollView with a subview of an UITableView (there is a map and you can scroll the tableView up and down on the screen using the scrollView).
I'm using -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event to detect rather the user touches the tableView or the map in the background (if he's scrolling the tableView it will go up and down and if he's scrolling the map the map will change the position (like any regular map)).
The code:
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGPoint comparePoint=CGPointMake(point.x, point.y-[[UIScreen mainScreen] bounds].size.height+self.allCellsHeightDividedBy2);
if (comparePoint.y<self.contentOffsetRecivied.y) {
return nil;
}
return self;
}
It's all working fine but the problem is that you can't select a cell in the tableView for some reason, it just don't select it.
Does anybody have an idea? Thanks!
hitTest:withEvent: is supposed to return the deepest subview which the point intersects with. Since you're only returning self, subviews can't be touched.
You should probably replace return self; with return [super hitTest:point withEvent:event].

UITableView (inside UIScrollView) didSelectRowAtIndexPath: not being called on first tap

I have a non scrollable UITableView inside an UIScrollView. And I'm having the problem that when I touch a row, the callback didSelectRowAtIndexPath: is not being called on the first tap, but after the first tap everithing works.
A few considerations:
After the first tap, the table view works normally, every tap works in every cell.
This happens just after I scroll the UIScrollView. If I don't scroll the UIScrollView, this never happens.
I have overriden the UIScrollView's touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view and the event does pass throw here, the view is a UITableViewCellContentView effectively.
I just don't know why the event is not been sent to the UITableView on the first time, and on the following ones it does.
If you have UIScrollView that contains vertical content and UITableView as part of this content, you must at least disable scrolling on UITableView - otherwise it's confusing for the user when he will scroll your mainView and when tableView, and also confusing for the framework because it's not clear where to send panning gestures.
As a rule of thumb - you should avoid putting table views inside scrollViews, unless you really know what you're doing.
Please disable scrolling of your table view and check the datasource and delegate are connected to your table view. If not follow the bellow code
#interface myClass ()<UITableViewDataSource, UITableViewDelegate>
- (void)viewDidLoad {
[super viewDidLoad];
self.myTableView.scrollEnabled = NO;
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
}

Forward tap-gesture to superview of UICollectionViewCell

I have a UITableView where each UITableViewCell contains a UICollectionView. The UICollectionView has UICollectionViewCells.
My problem is: Some of these UICollectionViewCells should react to tap-gestures, others should forward the event to the UITableViewCell (so it triggers displaying of the detail-view for that UITableViewCell).
I've been studying the apple-docs and several questions here but I can't get it to work.
(I think it should be possible to solve this generally, but just in case: Each UICollectionViewCell contains an UIImageView.)
Suggestions are very appreciated.
If I understand you correctly, you can do it like this:
Set cell.userInteractionEnabled = NO on the cells you don't want to handle events.
Override hitTest in your UICollectionView with this method:
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitTest = [super hitTest:point withEvent:event];
return hitTest == self ? nil : hitTest;
}
So tapping anywhere outside of a cell with user interaction enabled, super returns the collection view and we return nil, causing the table view cell to handle the event.
Why don't you let only the uicollectionview cell's handle the taps and then in the callback determine what action to take. For some you could do what you do now when tapping the cell, for others whatever you want.

I want a UItextField that is in UITableViewCell to become first responder only when the cell touched

This is what I'm trying to achieve:
I want a UITextField that is in UITableViewCell to become first responder only when the cell touched. I would like the text field to become the first responder only when I set it to first responder in the method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath`
Any idea how I can achieve this? When the textfield is touched directly, tableView:didSelectRowAtIndexPath: is never called.
Thanks in advance.
I guess, u have a custom UITableViewCell. In that u could have a UITextField member. Right?
In the custom cell class, override the method,
- (void)setSelected:(BOOL)selected animated:(BOOL)animated:
In that , if selected == YES, then make the text field as first responder. Else , resign the first responder.
Let an object be the delegate of the UITextField and implement textFieldShouldBeginEditing:; return NO if the cell hasn't been selected. (You might be able to always return NO from this method, if calling becomeFirstResponder directly bypasses the check.)
For having the textField become the first responder when a uitableviewcell is selected, use your textField as your property, and call [self.textField1 becomeFirstResponder];
Note: You will need as many properties as your the number of UITableViewCells as each cell has a textField.
When a textField is touched, the compiler will not know that the corresponding row got selected.
For this you will need to use tags, such as textField.tag == 0 for the first textField, textField.tag ==1 for the second TextField and so on. And in the textFieldDidBeginEditing you should check for the tag, and then link that value with the corresponding row selected.
Did any of these make sense ? :)
First, you need a custom table cell, your own subclass of UITableViewCell. In that implementation, you need to implement hitTest: to determine where the touch occurred. In that method you can determine if the touch was in fact inside the rect of your UITextField, and if it was, make it the first responder. Here's an example from some code I wrote for a project:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (self.editing) {
if ([nickname pointInside:[self convertPoint:point toView:nickname] withEvent:nil])
return [nickname hitTest:[self convertPoint:point toView:nickname] withEvent:event];
return [super hitTest:point withEvent:event];
}
return [self contentView];
}
The attribute nickname, in this case, was a UITextField inside the custom UITableViewCell.
The condition around self.editing may or may not be relevant to your application. The idea here is show you how hitTest: might be used, in general.

Resources