Pass an event on the whole view hierarchy - ios

I have created a "classic" category on ui controller to present in my way an other controller.
#interface UIViewController (QZPopup)
- (void)presentPopupViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion;
- (void)dismissPopupViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion;
#end
The category adds:
a blurred view on top of the calling controller view
an invisible view on top of all the screen. this invisible view must catch touch event to dismiss the popup and after having dismissed the popup, I also want to transmit this event to do anything required.
For my usage, I have a UIController embedded in a UINavigationBarController. I want to have the part below the navigation bar blurred and the navigation bar visible. A touch on the blurred view just dismiss and a touch on the navigation bar must dismiss AND do anything required by touched items in the navigationbar.
Visual is ok on my side, but I have trouble to pass the event and reach the navigationbar. For the moment I have subclassed UIView in QZPopupDismiss like below:
#implementation QZPopupDismiss {
touchCallback _touchCompletion;
}
- (instancetype)initWithFrame:(CGRect)frame andTouchCompletion:(touchCallback)completion {
if (self = [self initWithFrame:frame]) {
_touchCompletion = completion;
self.userInteractionEnabled = YES;
}
return self;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// first do the touch completion
_touchCompletion();
// pass the event to the view hierarchy
[super touchesEnded:touches withEvent:event];
}
#end
But the fact is that, in touchsEnded, super does not reach the navigation bar. How can I pass my event to the whole view hierarchy to reach the navigation bar items ?

Related

How to dismiss a whole stack of view controllers in navigation view controller?

I have two navigation view controllers.When I click on a button in a ViewController that belongs to the second navigation controller i want to dismiss the complete view controller stack of that navigation controller and want to go to a view controller in the first navigation controller.How can I do this? I tried [self.navigationController dismissViewControllerAnimated:YES completion:nil]; and nothing seems to happen.How to do this ?
The bug must be somewhere else. The code you described you are using does indeed work. I created a new project and made an extremely simple example:
#import "ViewController.h"
#interface MyViewController : UIViewController
- (instancetype)initWithColor:(UIColor *)color;
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self presentViewController:[[UINavigationController alloc] initWithRootViewController:[[MyViewController alloc] initWithColor:[UIColor redColor]]] animated:YES completion:nil];
}
#end
#implementation MyViewController
- (instancetype)initWithColor:(UIColor *)color {
if((self = [super init])) {
self.view.backgroundColor = color;
}
return self;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
CGPoint point = [touches.anyObject locationInView:nil];
if(point.x < self.view.frame.size.width*0.5 && point.y < self.view.frame.size.width*0.5) {
[self.navigationController pushViewController:[[MyViewController alloc] initWithColor:self.view.backgroundColor] animated:YES];
} else if(point.x > self.view.frame.size.width*0.5 && point.y < self.view.frame.size.width*0.5) {
[self.navigationController presentViewController:[[UINavigationController alloc] initWithRootViewController:[[MyViewController alloc] initWithColor:[UIColor greenColor]]] animated:YES completion:nil];
} else if(point.x < self.view.frame.size.width*0.5 && point.y > self.view.frame.size.width*0.5) {
[self.navigationController popViewControllerAnimated:true];
} else if(point.x > self.view.frame.size.width*0.5 && point.y > self.view.frame.size.width*0.5) {
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
}
#end
If you copy this into a new project where ViewController is your main view controller a navigation controller will be created and presented on view did appear. The background will be red.
By pressing the top-left part of the screen a new controller of the same color will be pushed to the current top navigation controller.
By pressing top right a new navigation controller will be presented with a green view controller.
By pressing bottom left you may pop the current view controller if any.
And by pressing bottom right you will dismiss the top navigation controller.
So your case is pressing top-left a few times to generate a stack of view controllers on a single navigation controller. Then press top right to present yet another navigation controller (green one). Press a few times on top left to create a stack of few view controllers on a green navigation controller. Now press the bottom right to dismiss the whole green stack and be back to the red navigation controller stack.
Check your code a bit more to see what is going on in your case, why you are experiencing issues in your case. First check if self.navigationController is nil.
try this i think it works for you
self.navigationController?.popToRootViewController(animated: true)

Automatically passing events from ChildViewController to parentViewController

I can't figure out how enable iOS to pass events from a childViewController down to its parentviewcontroller.
My childviewcontroller is like an overlay it's root view is transparent and it's opaque views cover a dynamic area - potentially the the entire parentviewcontroller (thus the root view of the childviewcontroller has the same frame as the parent).
Though only the opaque part of the childViewController's views should receive events. All other events I would like to get forwarded in the responderChain down to the parentviewcontrollers views. Just as if all views were sharing one viewcontroller.
I have made an instance of this class:
#interface SilentView : UIView
#end
#implementation SilentView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self) {
return nil;
}
return hitView;
}
#end
the rootview of my childViewController. That works for me.

ignore touch over a barButton

I made a custom split view controller, and I have tried to mimic the standard one as much as possible.
One feature I have is if the device is in portrait and the master view is displayed, if you tap on the details view, the master view will hide.
I use this code in the details view to accomplish this.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return [super hitTest:point withEvent:event];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
// Detect touches on the details View and notify the Split View controller
if(point.y != self.touchLocation.y)
{
if (_delegate && [_delegate respondsToSelector:#selector(detailsViewDidTapDetails:)])
[_delegate detailsViewDidTapDetails:self];
}
return [super pointInside:point withEvent:event];
}
The problem I am having, is if the detailsView is filled with a view that has its own navigation bar, then any buttons pressed in the navigation bar trigger the 'details view tapped' methods displayed above. This conflicts with adding the 'Show Details' button that you place inside a nav bar. Essentially what happens, is the methods to hide/show the master View is called twice back to back.
I need a way to ignore any taps on top of my 'hide/show details' button. I have a reference to the button, I just need to ignore taps on it within my hitTest / pointsInside methods.

How to stop gesture swiping back to previous view controller?

In one of my view controllers I have a carousel that the user can swiper through.
But if the user swipes up in the top left hand corner of the screen they can drag back the previous view controller.
How can I stop this? It could be something to do with this?
- (void) viewWillDisappear:(BOOL)animated
{
[self.carousel setHidden:YES];
}
- (void) viewWillAppear:(BOOL)animated
{
[self.carousel setHidden:NO];
}
https://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html#//apple_ref/occ/instp/UINavigationController/interactivePopGestureRecognizer
Set the interactivePopGestureRecognizer's enabled property to NO on your UINavigationController

Alter view when detail view pushed iOS

I have a view with a custom bar at the bottom of the screen. When a collection view cell is pressed, it loads a detail view, and I can go back.
This all works great; however, I have a plus button displayed on the custom bar, and I would like for the plus button to disappear only when the detail view shows up, and then come back when you hit the back button.
So far I have used the delegate method:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// Your code to update the parent view
}
The problem is this fires when the detail view is loaded and popped as well. Any idea on how to accomplish this? Thanks!
I assume mainView and detailView are viewControllers. In mainView's viewWillAppear method
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//get reference of plus button here
btnPlus.hidden = NO;
}
In detailView's viewWillAppear method
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//get reference of plus button here
btnPlus.hidden = YES;
}

Resources