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
}
Related
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].
I have a UITableView which is embedded in the root view of the ViewController. This UITableView contains multiple columns. And these columns can not be shown in one screen, so I make the UITableView can scroll horizontally. Here is the code which make the tableview scrolling horizontally:
- (void)viewDidAppear:(BOOL)animated
{
CGSize size = self.tableView.contentSize;
size.width = 450;
self.tableView.contentSize = size;
[self.tableView layoutIfNeeded];
}
This works fine and the table can scroll horizontally and vertically. And in the original screen(where no scroll happening), I can select one row and the selected row can be highlighted.
What my problem is: If I scroll the UITableView a little bit left, and in the area where is not shown before scrolling, the tap event cannot be responded by the UITableView. And thus, the row under my finger will not be selected and highlighted. But I can scroll if I swipe in the same area.
Anyone knows what I am doing wrong? Appreciated for your comments and thanks in advance.
I understand your problem and there is always a simple way to do a complex problem.Your required functionality can be achieved by using UICollectionView.
In your UITableViewCell add UICollectionView.In this approach your code will get cleaner and you can achieve your functionality without any headache.
Here is very simple tutorial about UICollectionView.
http://www.appcoda.com/ios-programming-uicollectionview-tutorial/
At last, I think this is a bug of UITableView. I get a workaround for the problem by subclass UITableView. Here it is:
// in my own UITableView subclass .m file
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(#"MonthDetailTableView: touchesEnded");
UITouch * touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
if (point.x < self.frame.size.width)
{
[super touchesEnded:touches withEvent:event];
}
else
{
// if not adjust by mod, the NSIndexPath returned by indexPathForRowAtPoint
// will be nil. I think this is a bug of UITableView
point.x = (int)point.x % (int)self.frame.size.width;
NSIndexPath *path = [self indexPathForRowAtPoint:point];
[self selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
if (self.delegate != nil &&
[self.delegate respondsToSelector:#selector(tableView:didSelectRowAtIndexPath:)])
{
[self.delegate tableView:self didSelectRowAtIndexPath:path];
}
}
}
I overrode hitTest, and that works just fine. I want it to behave as if I hadn't overridden this method under certain conditions, and that's where the problem lies.
I'm using a subclassed UICollectionView to render cells over a MKMapView using a custom UICollectionViewLayout implementation. I needed to override hitTest in the UICollectionView subclass so that touch events can be passed to the mapView and it can be scrolled. That all works fine.
I have a toggle mechanism which animates between my UICollectionViewLayout (map) to a UICollectionViewFlowLayout (animate items on a map to a grid format). This works good too, but when I'm showing the flow layout, I want the user to be able to scroll the UICollectionView like a normal one (act as though hitTest isn't overridden). I can't figure out what to return in hitTest to have it's default behavior.
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
if(self.tapThrough == YES){
NSArray *indexPaths = [self indexPathsForVisibleItems];
for(NSIndexPath *indexPath in indexPaths){
UICollectionViewCell *cell = [self cellForItemAtIndexPath:indexPath];
if(CGRectContainsPoint(cell.frame, point)){
return cell;
}
}
return nil;
} else {
return ???
}
}
I've tried returning a number of things. self, self.superview, etc... Nothing get it to behave normally (I cannot scroll the cells up and down).
If you want the normal behaviour of your hit test:
return [super hitTest:point withEvent:event];
This will return what the hit test usually returns when it is not overridden.
Basically what i want to do is to have a cell in a UICollectionView, which part of it will respond to tap, and part of it won't.
This cell will be a normal cell that is expanded when tapped, showing a subview on the expanded area that doesn't respond to tap.
I considered expanding the cell and adding a subview above it, that wouldn't be clickable. But i think that is kind of a "bad" way of accomplishing it.
Is there a best way of doing this?
This is what i want to do:
Any help is appreciated, thanks!
You need subclass UICollectionViewCell, then override hitTest:withEvent rather than pointInside:withEvent, inside hitTest:withEvent: you should define the targetRect, then check if the point user clicked is inside of targetRect.
Here is the code which works on my tableViewCell (similar for collectionViewCell).
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
CGRect targetRect = CGRectMake(0, 0, CGRectGetWidth(self.bounds), 20);
if (CGRectContainsPoint(targetRect, point)) {
return nil;
} else {
return [super hitTest:point withEvent:event];
}
}
With in the selector
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
you must return a cell, and in this function you should determine whether or not the cell is clickable and then simply do
cell.userInteractionEnabled = shouldAllowTouch;
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 {}