I have a viewcontroller that via "[self.view addSubview: secondView.view]," adds a second view. The problem is that the second view is added outside half.
secondView = [[SecondView alloc] initWithFrame: CGRectMake (-160, 0, 320, 460)];
[self.view addSubview: secondView.view]; "
I have noticed, however, that the part before the 0 (-160) is not interagibile. Is this normal? is there a way to solve?
Thank you!
You can allow subviews to receive touches outside of the parent's bounds by overriding pointInside:withEvent: for the parent view.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL pointInside = NO;
// step through our subviews' frames that exist out of our bounds
for (UIView *subview in self.subviews)
{
if(!CGRectContainsRect(self.bounds, subview.frame) && [subview pointInside:[self convertPoint:point toView:subview] withEvent:event])
{
pointInside = YES;
break;
}
}
// now check inside the bounds
if(!pointInside)
{
pointInside = [super pointInside:point withEvent:event];
}
return pointInside;
}
I fear that given the way the UIResponder chain works, what you want is not directly possible (the superview will only pass to its subviews the events that it recognizes as affecting itself).
On the other hand, if you really need to have this view outside of its parent's frame, you could associate a gesture recognizer (reference) to the subview. Indeed, gesture recognizers are handled outside the normal touch event dispatching and it should work.
Try this for a tap:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[secondView addGestureRecognizer:singleTap];
Related
What I want to do is put a UIView on top of another UIView, and both of them are screen size. The top UIView includes lots cocos nodes and will respond when I touch them. But when I touch a place that has no cocos node, the bottom UIView should respond.
I don't know how to do this. My imagine is check if top uiView is handled touch, do nothing. other wise let the bottom UIView start respond. But I don't know how to check that. I only know how to check touch but it seems the UIView will also be touched when I touch some place it can't handle.
I think you not requires 2 views.
Just take one view only and add all your node to that view.
in .h
IBOutlet UIView *bgView;//your view
UITapGestureRecognizer *viewTapRecognizer;// view tap recognizer
in .m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
viewTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handelGesture:)];
[bgView addGestureRecognizer:viewTapRecognizer];
for (UIView *subView in [bgView subviews]) {
UITapGestureRecognizer *nodeTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handelGesture:)];
[subView addGestureRecognizer:nodeTapRecognizer];
}
}
- (void)handelGesture:(UITapGestureRecognizer*)sender {
if (sender == viewTapRecognizer) {
// your view is tapped
NSLog(#"........Tapped view..........");
}
else {
// your node is tapped
NSLog(#"........Tapped node..........");
}
}
Try this. it might work for you.
You can achieve it by 2 ways: using pointInside... method or hitTest... method.
If you will use pointInside... you can use only your views (the top view and the bottom view). You will subclass UIView with the top view and override pointInside... method. You will return YES if user's tapped a cocoa node else returns NO.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL inside = [self didUserTapCocoaNode:point];
return inside;
}
If you want more complex logic, you should use third view, subclass of UIView (the container view), that will be contain the top view and the bottom view. Override method hitTest..., and return the bottom or the top view.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView* result = nil;
if ([self touchedCocoaNode:point])
{
result = self.topView;
}
else
{
result = self.bottomView;
}
return result;
}
UPD
Example implementation of didUserTapCocoaNode with pseudo code:
- (BOOL)didUserTapCocoaNode:(CGPoint)pointInSelf
{
__block BOOL tappedSomeNode = NO;
[self.nodes enumerateObjectsUsingBlock:^(NodeType* obj, NSInteger idx, BOOL* stop){
CGPoint pointInNode = [obj convertPoint:pointInSelf fromView:self];
tappedSomeNode = [obj pointInside:pointInNode withEvent:nil];
*stop = tappedSomeNode;
}]
return tappedSomeNode;
}
Imagine a custom segue ...
-(void)perform
{
UIView *sv = ((UIViewController *)self.sourceViewController).view;
UIView *dv = ((UIViewController *)self.destinationViewController).view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
[window insertSubview:dv aboveSubview:sv];
[dv coverFromRight:0 then:^
{
[self.sourceViewController
presentViewController:self.destinationViewController
animated:NO completion:nil];
}];
}
Which in fact, only PARTIALLY (!) covers the "underneath, previous" scene,
and in fact DOES NOT call "presentViewController", so, the "underneath, previous" scene in fact keeps operating normally.
-(void)perform
{
UIView *sv = ((UIViewController *)self.sourceViewController).view;
UIView *dv = ((UIViewController *)self.destinationViewController).view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
[window insertSubview:dv aboveSubview:sv];
[dv coverButOnlyHalfWay:0 then:^
{
}];
}
Essentially, is this possible?
In fact, I've found from experiment the above works (!!). BUT when you come to the custom unwind segue, it does not work: everything crashes. (Perhaps as you'd expect.)
What's the situation? is there a way to make a custom segue, which, covers only say half the "original, underneath" scene and leaves that scene running?
(I appreciate you can just implement this using a container view, but it's not as clean as a whole segue scene.)
Why use a segue? You can just add your view as a subview and position it correct using CGRectMake, this would be much easier.
// Size Your View with X, Y coordinates
[viewController.view setFrame:CGRectMake(0, 0, 320, 192)];
[self.view addSubview:viewController.view];
[viewController didMoveToParentViewController:self];
[self addChildViewController:viewController];
Is there a way to do this? Like tapping on any part of the screen dismisses the partial modal curl.
I thought about an invisible button, but that still doesn't cover the whole curl area.
Add gesture recognizers to your main view in viewDidLoad
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(getDismissed)];
UISwipeGestureRecognizer *swipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(getDismissed)];
swipeRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
tapRecognizer.numberOfTapsRequired = 1;
tapRecognizer.numberOfTouchesRequired = 1;
tapRecognizer.delegate = self;
[self.view addGestureRecognizer:tapRecognizer];
[self.view addGestureRecognizer:swipeRecognizer];
-(void)getDismissed
{
// call dismissViewControllerAnimated:completion: by the presenting view controller
// you can use delegation or direct call using presentingViewController property
}
Then exclude in view that you don't want to trigger the dismissal
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
// touching objects of type UIControl will not dismiss the view controller
return ![touch.view isKindOfClass:[UIControl class]];
}
I've got a QuickLook view that I view some of my app's documents in. It works fine, but I'm having my share of trouble closing the view again. How do I create a touch event / gesture recognizer for which I can detect when the user wants to close the view?
I tried the following, but no events seem to trigger when I test it.
/------------------------ [ TouchPreviewController.h ]---------------------------
#import <Quicklook/Quicklook.h>
#interface TouchPreviewController : QLPreviewController
#end
//------------------------ [ TouchPreviewController.m ]---------------------------
#import "TouchPreviewController.h"
#implementation TouchPreviewController
- (id)init:(CGRect)aRect {
if (self = [super init]) {
// We set it here directly for convenience
// As by default for a UIImageView it is set to NO
UITapGestureRecognizer *singleFingerDTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSingleDoubleTap:)];
singleFingerDTap.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:singleFingerDTap];
[self.view setUserInteractionEnabled:YES];
[self.view setMultipleTouchEnabled:YES];
//[singleFingerDTap release];
}
return self;
}
- (IBAction)handleSingleDoubleTap:(UIGestureRecognizer *) sender {
CGPoint tapPoint = [sender locationInView:sender.view.superview];
[UIView beginAnimations:nil context:NULL];
sender.view.center = tapPoint;
[UIView commitAnimations];
NSLog(#"TouchPreviewController tap!" ) ;
}
// I also tried adding this
- (BOOL)gestureRecognizer:(UIGestureRecognizer *) gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*) otherGestureRecognizer {
return YES;
}
#end
Edit: For clarification, this is how I instantiate the controller:
documents = [[NSArray alloc] initWithObjects: filename , nil ] ;
preview = [[TouchPreviewController alloc] init];
preview.dataSource = self;
preview.delegate = self;
//set the frame from the parent view
CGFloat w= backgroundViewHolder.frame.size.width;
CGFloat h= backgroundViewHolder.frame.size.height;
preview.view.frame = CGRectMake(0, 0,w, h);
//refresh the preview controller
[preview reloadData];
[[preview view] setNeedsLayout];
[[preview view] setNeedsDisplay];
[preview refreshCurrentPreviewItem];
//add it
[quickLookView addSubview:preview.view];
Also, I've defined the callback methods as this:
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller
{
return [documents count];
}
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index
{
return [NSURL fileURLWithPath:[documents objectAtIndex:index]];
}
Edit2: One thing i noticed. If I try making swiping gestures, I get the following message. This could shed some light on what is wrong/missing?
Ignoring call to [UIPanGestureRecognizer setTranslation:inView:] since
gesture recognizer is not active.
I think your example code is incomplete. It isn't clear how you are instantiating the TouchPreviewController (storyboard, nib file or loadView.)
I have never used the class so I could be way out in left field.
If you've already instantiated a UITapGestureRecognizer in the parent viewController, it is absorbing the tap events and they aren't passed on to your TouchPreviewController.
I would implement the view hierarchy differently by attaching the UITapGestureRecognizer to the parent viewController and handle presentation and unloading of the QLPreviewController there.
I think you might not have to subclass QLPreviewController by instantiating the viewController from a nib file.
When your parent viewController's UITapGestureRecognizer got an event you would either push the QLPreviewController on the navigation stack or pop it off the navigation stack when done.
Hope this is of some help.
Is it ok to override -handlePan: in a UIScrollView subclass?
i.e. my app won't get rejected from the app store?
Thanks for sharing your views.
Edit: what about calling -handlePan: in another method of my subclass?
In case anyone is interested, what I did instead of overriding was disabling the default UIPanGestureRecognizer and adding another instance of UIPanGestureRecognizer which is mapped to my custom handler.
Edit for twerdster:
I did it like this
//disables the built-in pan gesture
for (UIGestureRecognizer *gesture in scrollView.gestureRecognizers){
if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]){
gesture.enabled = NO;
}
}
//add your own
UIPanGestureRecognizer *myPan = [[UIPanGestureRecognizer alloc] init...];
//customize myPan here
[scrollView addGestureRecognizer:myPan];
[myPan release];
You can make the code even shorter.
//disables the built-in pan gesture
scrollView.panGestureRecognizer.enabled = NO;
//add your own
UIPanGestureRecognizer *myPan = [[UIPanGestureRecognizer alloc] init...];
[scrollView addGestureRecognizer:myPan];
[myPan release];