iOS - increase partial curl tap area to dismiss modal - objective-c

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]];
}

Related

Objective c stop animations of UIImageView on UINavigationController segue

I have some strange animations when transitioning between views with UINavigationController. The images start with different frame properties and animate to the ones that i have set each time i segue with UINavigationController. I can't find anything to make that animation go away. Does it have something to do with ViewWillAppear implementation?
- (void)viewDidLoad
{
LoadingView* loadingView;
// adding a loading view
loadingView = [self setViewLoading];
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithTitle:#"≡"
style:UIBarButtonSystemItemDone
target:self.revealViewController
action:#selector(rightRevealToggle:)];
// pan gesture for side menu (Facebook style)
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
self.navigationItem.title = #"Brands";
[self.navigationController setNavigationBarHidden:NO];
self.menusDataArray = self.menusData[TAG_MENUS];
// removing loading view.
[self setViewNotLoading:loadingView];
}
- (void)viewWillAppear:(BOOL)animated
{
// if I don't do this, the swipe gesture for the Facebook stile side menu won't be recognised after pressing back on navigation controller bar
[super viewWillAppear:animated];
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}

Pop controller after back bar button is pressed

I have a UINavigationController ans a chain of 3 simple controllers. Each one has a button. When press a button a next controller is Pushed. ViewController1 -> ViewController2 -> ViewController3. When I push a back button on the 3rd view i want to move to the first view. Using of backBarButtonItem is obligatory. Here is the code for second controller:
#import "ViewController2.h"
static BOOL isBackButtonPressed;
#implementation ViewController2
- (void)viewDidLoad {
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"back from 3" style:UIBarButtonItemStyleBordered target:nil action:nil];
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
if (isBackButtonPressed) {
[self.navigationController popViewControllerAnimated:YES];
} else {
isBackButtonPressed = YES;
}
[super viewWillAppear:animated];
}
#end
But when I press back button on the third view I return to the second view instead of the first view. Could you help me to return to the first view pressing back button on the third view.
I tried suggestions from answers but they don't help.
Adding a selector to backBarButtonItem doesn't help because it is never called.
Adding a [self.navigationController popToRootViewControllerAnimated:YES] in viewWillDisappear methos also doesn't work. I don't know why. I think that the actual problem is how backBarButtonItem works.
Any other suggestions?
The behaviour I try to achieve exists in the calendar on iPhone. When you rotate iPhone to landscape you get to the weeek view. Then go to the event details, and rotate to the portrait. When you press back button you will get to a day view not to a week view, so a controller with weekview is skipped.
After countless number of tries my solution was simply not use backBarButtonItem! As whatever i do it always goes to previous viewController instead of calling its selector
Instead I use only leftBarButtonItem for navigation, as it guarantees calling my action.
Here an example
UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 27, 22)];
[backButton setImage:[UIImage imageNamed:#"backbutton"] forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
This certainly calls backButtonPressed action.. This works both for IOS 6 and 7
No need to register a new selector for the back button, just do:
-(void)viewWillDisappear{
if ( [self.navigationController.viewControllers containsObject:self] )
//It means that the view controller was popped (back button pressed or whatever)
//so we'll just pop one more view controller
[self.navigationController popViewControllerAnimated:NO];
}
in your ViewController3 viewWillDisappear method
Try using this in your third view controller, this way you check if you have pressed the back button and directly pop to the root view controller which as you stated is your first view controller.
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
[super viewWillDisappear:animated];
}
I had the same problem as you beofre and fixed it like this:
You can capture the back button on the ViewController3 and before poping the view, remove ViewController2 from the navigation stack like this:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"back from 3" style:UIBarButtonItemStyleBordered target:self action:#selector(customBackPressed:)];
}
-(void)customBackPressed:(id)sender {
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray: navigationController.viewControllers];
for (UIViewController *vc in viewControllers)
{
// If vc is ViewController2 type, remove it
}
navigationController.viewControllers = allViewControllers;
[self.navigationController popViewControllerAnimated:YES];
}
Because ViewController2 is not in the stack anymore, it will jump to ViewController1.
Also, if ViewController1 is the root view controller, you can just do:
[self.navigationController popToRootViewControllerAnimated:YES];

Reading touch events in a QLPreviewController

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.

addSubview outside the view

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];

Overriding -handlePan: in UIScrollView

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];