iOS How to make UIButton below UICollectionView receive touch? - ios

I have next structure
UView -
- UIView2 with Button
- CollectionView - which overlap UIView2 and has contentInset
I can't make to Button receive touch events.
I try pointInside with check clear color (collectionView has backgroundColor is set to clear color)
But i need to scroll work event on blue part (UIView2)
So, i need then i touch collectionView touch also receive a UIButton
Is it possible?

Why does your collection view need to overlap the button? Anyway, you can try overriding HitTest in UIView like this:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGPoint pointInUIView2 = [UIView2 convertPoint:point fromView:self];
if ([UIView2 pointInside:pointInUIView2 withEvent:event])
{
return UIView2;
}
else
{
return [super hitTest:point withEvent:event];
}
}

Related

forwarding UISwipeGestureRecognizer to underlying UIScrollView

in my viewController I have an imageSlider which is above a UITableView.
I want to forward vertical swipeGesture from imageSlider to the underlying tableview. so when I swipe vertically on imageSlider, underlying tableView begins to scrolling. (tableview can scroll below imageSlider)
what I did:
I have created a custom view for my viewController View and have override hitTest:WithEvent: method:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *targetView = self.flexibleHeader;
// Convert the point to the target view's coordinate system.
// The target view isn't necessarily the immediate subview
CGPoint pointForTargetView = [targetView convertPoint:point fromView:self];
if (CGRectContainsPoint(targetView.bounds, pointForTargetView)) {
// The target view may have its view hierarchy,
// so call its hitTest method to return the right hit-test view
UIView *finalView = [targetView hitTest:pointForTargetView withEvent:event];
//final view should be UIButton or other UIControl element in RestaurantFlexibleHeader view
if (![finalView isKindOfClass:[targetView class]]) {
return finalView;
}
}
return [super hitTest:point withEvent:event];
}
but the problem is that I can't detect swipe gesture from UIEvent. because allTouches property is empty!
I can put imageSlider below tableView, so When I start to scroll vertically on imageSlider tableView begins to start scrolling. but how can I forward horizontal swipe and tap event to imageSlider?
so whats your idea? how do you handle this dilemma?

Hit Testing a UIButton located outside bounds of SuperView

I am trying to animate this view ControlsView up by touchUpInside in the UIButton which is the carrot character inside the white square in the image attached. When the button is hit, a delegate method is fired an the controlsView is animated up. The problem is that because the UIButton is outside of the bounds of controlsView it does not receive touch info.
I have thought about this a lot and read up on some potential candidate solutions. Such as detecting the touch event on a super view by overriding hitTest:withEvent:. However, the UIButton is actually a subview of CockPitView which is a subview of ControlsView which is a subview of MainView. MainView, it seems, is the rectangle whose coordinates the UIButton would truly lie in. So would I override hitTest there and pass the touch info to CockPitView, where I could then have my Button trigger its action callback?
Has anyone encountered this or a similar problem and have any advice on how to proceed?
You should override hitTest in your custom view CockPitView
- (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;
}
}
}
return nil;
}
CockPitView has to be a subclass of UIView
You may want to use -pointInside:withEvent:. In your button's superview, i.e., ControlsView, override the method like this:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
// self.button is the button outside the bounds of your ControlsView
if (CGRectContainsPoint(self.button.bounds, [self convertPoint:point toView:self.button])) {
return YES;
}
return [super pointInside:point withEvent:event];
}
By doing this, your ControlsView claims that the points inside the bounds of your button should be treated like the points inside your ControlsView.

Ignoring touch events on UIView except for the Buttons on them.

I have a MainViewController(A) and another DetailedViewController(B) as its subview. In "B" I have a UIView containing some UIControls like a UIButton, UILabel and some UIViews.
I need to ignore all the touch events within this UIView except for Button clicks and I have to remove the subView if clicked outside the UIView.
How can I accomplish this ?
A option might be to set a custom view in the detail view controller.
In that view you can override the hittest function.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *resultView = [super hitTest:point withEvent:event];
if (![resultView isKindClass:[UIButton class]])
{
resultView = nil;
}
return resultView;
}
Touches not on the buttons will than be ignored. But if you have a scroll view under it and you will start dragging on the buttons the scrollview will be ignored.

Embed UITableView and other UIViews inside a restricted UIScrollView

I have a bit of a complex situation involving multiple gestures. Basically, I want to have one container UIScrollView that only scrolls left-to-right if the touches are within a specific area. If they are not within the area, the UIScrollView passes those touches on to child UIViews that exist side-by-side inside the UIScrollView (think of it like a panel navigation).
I have the UIScrollView containing UIViews working fine. I subclassed UIScrollView and added the panning restriction via TouchesBegan/TouchesMoved/TouchesEnded/TouchesCancelled. Everything works except when the UIView is a UITableView. It seems at that point my parent UIScrollView never gets these events and so can never restrict the panning properly.
Anyone have any ideas for how to accomplish this?
Thanks!
The way to do this is to subclass the subviews that are eating the touch events and not allowing the UIScrollView to get them. Then, override the pointInside: method (with the appropriate exception for UI that you want to still work). For example:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
// Confine the offending control to a certain area
CGRect frame = CGRectMake(0, 0,
self.frame.size.width,
self.frame.size.height - 100.00);
// Except for subview buttons (or some other UI element)
if([self depthFirstButtonTest:self pointInside:point withEvent:event])
{
return YES;
}
return (CGRectContainsPoint(frame, point));
}
- (BOOL)depthFirstButtonTest:(UIView*)view pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView * subview in view.subviews)
{
if([self depthFirstButtonTest:subview pointInside:point withEvent:event])
{
return YES;
}
}
// Is it a button? If so, perform normal testing on it
if ([view isKindOfClass:[UIButton class]]) {
CGPoint pointInButton = [view convertPoint:point fromView:self];
if ([view pointInside:pointInButton withEvent:event]) {
return YES;
}
}
return NO;
}

iOS define touchable areas of an object, confine touches to subviews of self

I have a subclassed UIView I'll call customView. I would like enable touches so users can manipulate subviews, which have gesture recognizers and other controls, but the view itself I would like to not be touchable so that views drawn below the view will still be touchable. In other words customView will be drawn on top of other views in the app, but I still want the views below to be touchable, while allowing touches on subviews of customView.
I've tried to use touchesBegan like so but this does not work. Any ideas? Thanks for reading!
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
//I've tagged the views that I want to be touchable.
if ([touch view].tag == 1000 || [touch view].tag == 2000 || [touch view].tag == 3000) {
self.userInteractionEnabled = YES;
} else {
self.userInteractionEnabled = NO;
}
}
What you need to do is implement the following method in your customView:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
//check if one of the subviews was hit, if so forward the touch event to it
for (UIView *view in self.subviews){
if (CGRectContainsPoint(view.frame, point))
return view;
}
// use this to pass the 'touch' upward in case no subviews trigger the touch
return [super hitTest:point withEvent:event];
}
Then add those subview upper to your customview.other vice your customview should not let them touche.and put custom view to below those subview so none other area then your selected subview will be touchable.

Resources