Uiview. Why Does a Subviews Outside Its Parent's Extent Not Receive Touches

UIView. Why Does A Subviews Outside its Parent's Extent Not Receive Touches?

The problem is the responder chain. When you touch the display it will go down from the parents to the childen.

So .. when you touch the screen the parent will see that the touch is outside of it's own bounds and so the children will not even asked.

The function that does that is the hitTest. If you have your own UIView class you can overwrite it and return the button by yourself.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

UIView Partially Outside superView Not Receiving Touches

In order for a view to receive a touch, the view and all its ancestors must return true from pointInside:withEvent:.

Normally, pointInside:withEvent: returns false if the point is outside the view's bounds. Since a touch in the green area is outside the container view's bounds, the container view returns false, so the touch won't hit the gesture view.

To fix this, you need to create a subclass for the container view and override its pointInside:withEvent:. In your override, return true if the point is in the container view's bounds or in the gesture view's bounds. Perhaps you can be lazy (especially if your container view doesn't have many subviews) and just return true if the point is in any subview's bounds.

class ContainerView: UIView {

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if super.point(inside: point, with: event) { return true }
for subview in subviews {
let subviewPoint = subview.convert(point, from: self)
if subview.point(inside: subviewPoint, with: event) { return true }
}
return false
}

}

Touch event does not register outside of a child UIView that partially extends outside of parent UIView

As DonMag wrote:

You cannot interact with an element that extends beyond the bounds of
its parent (superview).

At least not without overriding some methods, views have their own implementations to detect touches. Most important methods are:

func hitTest(_ point: CGPoint, 
with event: UIEvent?) -> UIView?

func point(inside point: CGPoint,
with event: UIEvent?) -> Bool

Those methods must be overriden, I've pasted some code on mine, is pretty old thus probably will require swift migration from 3.x to 4.x. Everything that you will add as a subview will handle touches even if is outside the bounds:

class GreedyTouchView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !self.clipsToBounds && !self.isHidden && self.alpha > 0.0 {
let subviews = self.subviews.reversed()
for member in subviews {
let subPoint = member.convert(point, from: self)
if let result: UIView = member.hitTest(subPoint, with:event) {
return result
}
}
}
return super.hitTest(point, with: event)
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return super.point(inside: point, with: event)
}
}


But I'd like to point out that drop down menus are more Web UI related and not iOS UI related, you should use a picker instead.

UIView subview not receiving touches

containerView is a subview of view, but containerView's origin is right of the right edge of its parent view (view), because of the line:

containerView.frame = CGRectMake(self.view.frame.width, originalY, 150, self.view.frame.height)

Any portion of subview that is outside of its superview's bounds won't receive touch events. Touch events will only be passed along and recognized by portions of the subview that are within the bounds of its superview This is why your cyan button won't "fire" when you tap it.

One way to solve it is instead of manipulating the view's frame, use a secondary subview (call it overlayView) to overlay and cover the containerView, and manipulate its frame. Make sure at least some portion both views are within the bounds of the main view so they may receive touch events.

UIView clipToBounds is not stopping a subView receiving touches outside the parent view

Using clipToBounds only affects the visual layout of a subView, not the logical layout. This means that whilst my subView isn't visible to the eye, it's visible to touch.

I've worked around this issue by animating the size of the subView rather than its position. In my code below, the stationProximityView is the subView. I animate its size by 40 pixels to bring the black title back into view.

[UIView beginAnimations:@"stationProximityBar" context:NULL];
self.stationProximityView.view.frame = CGRectOffset(self.stationProximityView.view.frame, 0, -40);
[UIView commitAnimations];

When I no longer need it, I animate it out of view.

[UIView beginAnimations:@"stationProximityBar" context:NULL];
self.stationProximityView.view.frame = CGRectMake(0 ,0, 320, 500);
[UIView commitAnimations];

If the user taps the view button, the entire subView is shown:

 [UIView beginAnimations:@"stationProximityBar" context:NULL];
self.stationProximityView.view.frame = CGRectMake(0,460,320,40);
[UIView commitAnimations];

Dismissal causes the view to be hidden in the same way as the small bar.

 [UIView beginAnimations:@"hideStationProximityBar" context:NULL];
self.stationProximityView.view.frame = CGRectMake(0,0,320,500);
[UIView commitAnimations];

At the moment, this code is only being tested on the iPhone 5, so the hard-coded height of 500 would causes issues on previous iPhone models.

Subview Blocking Parent View TouchesBegan?

The touchesBegan method has to do with the frame of the parent view. If the frame isn't visible for the parent view and is totally enclosed by the subviews, the parent view won't receive touches.

Subview displaying outside the bounds of the parent UIView

If you're setting up your views in code, set the clipsToBounds property of the superview to YES.

If you're setting up your views in a nib, turn on the “Clip Subviews” checkbox on the superview. It's off by default, as in this screenshot:

Clip Subviews checkbox

Interaction beyond bounds of UIView

Yes. You can override the hitTest:withEvent: method to return a view for a larger set of points than that view contains. See the UIView Class Reference.

Edit: Example:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGFloat radius = 100.0;
CGRect frame = CGRectMake(-radius, -radius,
self.frame.size.width + radius,
self.frame.size.height + radius);

if (CGRectContainsPoint(frame, point)) {
return self;
}
return nil;
}

Edit 2: (After clarification:) In order to ensure that the button is treated as being within the parent's bounds, you need to override pointInside:withEvent: in the parent to include the button's frame.

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (CGRectContainsPoint(self.view.bounds, point) ||
CGRectContainsPoint(button.view.frame, point))
{
return YES;
}
return NO;
}

Note the code just there for overriding pointInside is not quite correct. As Summon explains below, do this:

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if ( CGRectContainsPoint(self.oversizeButton.frame, point) )
return YES;

return [super pointInside:point withEvent:event];
}

Note that you'd very likely do it with self.oversizeButton as an IBOutlet in this UIView subclass; then you can just drag the "oversize button" in question, to, the special view in question. (Or, if for some reason you were doing this a lot in a project, you'd have a special UIButton subclass, and you could look through your subview list for those classes.) Hope it helps.

UIView: Let subviews receive touches, but not main view?

I found an answer here: http://vectorvector.tumblr.com/post/2130331861/ignore-touches-to-uiview-subclass-but-not-to-its

Basically, I'm making the main view a subclass of UIView, and overriding hitTest with this:

-(id)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
id hitView = [super hitTest:point withEvent:event];
if (hitView == self) return nil;
else return hitView;
}

(Note that confusingly you must set UserInteractionEnabled to true, ticked, yes for the UIView in question!)



Related Topics



Leave a reply



Submit