ios - How can I dismiss(close) the load UIViewController on the window - ios

I have a question, I want to build popup view like the UIAlertView,
I create two UIViewController in the storyboard(note: there are not segue),and Root UIViewController will load the UIViewController view popup.
I create two UIViewController to edit the UIView, because I want to use the storybaord to edit my complex UIView in the feature.
Then I put the container view on the RootViewController and there are two button in the container view.(my structure as below:)
When I click the pop up green button(with button name popupGreenBtn), It can correct addSubview on the UIWindow.
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"ContainerViewController view didload");
appDelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
secondGreenVC = (SecondGreenViewController*)[mainStoryboard instantiateViewControllerWithIdentifier:#"SecondGreenViewController"];
thirdRedVC = (ThirdRedViewController*)[mainStoryboard instantiateViewControllerWithIdentifier:#"ThirdRedViewController"];
}
- (IBAction)popupGreenBtnAction:(id)sender {
[appDelegate.window addSubview: secondGreenVC.view];
[appDelegate.window bringSubviewToFront:secondGreenVC.view];
}
- (IBAction)popupRedBtnAction:(id)sender {
[appDelegate.window addSubview: thirdRedVC.view];
[appDelegate.window bringSubviewToFront:thirdRedVC.view];
}
When I click the button , it can correct popup on the window:
But now , When I click the SecondGreenViewController and ThirdRedViewcontroller background view(just transparent dark black), I want to close the popon the view(secondGreenVC.view or thirdRedVC.view).
I had try to add the code in the SecondGreenViewController class below:
#implementation SecondGreenViewController
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapAction:)];
[_secondBaseView addGestureRecognizer:tap];
}
-(void) tapAction:(UITapGestureRecognizer*) recognizer
{
[self removeFromParentViewController];
// [self dismissViewControllerAnimated:NO completion:nil];
NSLog(#"tap in SecondGreenViewController");
}
#end
There are not effect to close the UIViewController.
How can I close the Popup window(the UIViewController) when I click the dark black part?
(If you want to more info, please tell me or you can see my this simple project from github https://github.com/dickfalaDeveloper/PopUpViewDemo )
Thank you very much.

-(void) tapAction:(UITapGestureRecognizer*) recognizer
{
thirdViewcontroller.hidden=YES;
}

I resolve the problem. But I don't know have any best answer.
My method is in the popupGreenBtnAction actin.
change the code to:
appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
secondGreenVC.modalPresentationStyle = UIModalPresentationCustom;
[appDelegate.window.rootViewController presentViewController:secondGreenVC animated:NO completion:nil];
Then in the SecondGreenViewController storyboard,
Drag an other UIView do base UIView and set the UIView IBOutlet(now I name with"secondBaseView").
at SecondGreenViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapAction:)];
[self.secondBaseView addGestureRecognizer:tap];
}
-(void) tapAction:(UITapGestureRecognizer*) recognizer
{
[self dismissViewControllerAnimated:NO completion:nil];
}
That is Ok to show the modal and dismiss the modal UIViewController.

Related

UINavigationController hidesBarOnSwipe memory leak issue

I've got a problem with hidesBarOnSwipe property of UINavigationController.
Overview :
Link to project file
I have one controller named FirstViewController which is root view of UINavigationController.
Everything is in Main.storyboard.
FirstViewController contains UIButton action. Inside that action I instantiate a SecondViewController and push it on a navigation stack.
- (IBAction)button:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[self.navigationController pushViewController:vc animated:YES];
}
Inside SecondViewController there is only an hidesBarsOnSwipe property set to YES on viewDidLoad :
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.hidesBarsOnSwipe = YES;
}
and dealloc gets NSLogged :
- (void)dealloc {
NSLog(#"Dealloc");
}
Problem :
When we swipe up to hide navigationBar, dealloc is never get called. Instruments shows a SecondViewController memory leak here.
When we are on SecondViewController and we just press back button - everything is fine. Dealloc gets called.
There is definitly some kind of retain cycle but i have no idea why and how to avoid this kind of situation.
Some updates and temporary solution :
There is another method to perform navigationBar hiding.
What worked for me is to use :
[self.navigationController setNavigationBarHidden:hidden animated:YES];
To achieve good results add a property in your class to keep track status of navigationBar animation :
#property (assign, nonatomic) BOOL statusBarAnimationInProgress;
Implement UIScrollViewDelegate like this :
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat yVelocity = [scrollView.panGestureRecognizer velocityInView:scrollView].y;
if (yVelocity > 0 && !self.statusBarAnimationInProgress) {
[self setNavigationBarHidden:NO];
} else if (yVelocity < 0 && !self.statusBarAnimationInProgress) {
[self setNavigationBarHidden:YES];
}
}
Set navigation bar hidden should look like :
- (void)setNavigationBarHidden:(BOOL)hidden {
[CATransaction begin];
self.statusBarAnimationInProgress = YES;
[CATransaction setCompletionBlock:^{
self.statusBarAnimationInProgress = NO;
}];
[self.navigationController setNavigationBarHidden:hidden animated:YES];
[CATransaction commit];
}
I use CATransaction to check if animation of navigation bar is completed. Any way that works. Not so easy solution but at least there is no leak :)

SWRevealViewController No StoryBoard and Toggle with UIButton

I am trying to use the SWRevealViewController without a storyboard and with a UIButton. My project has two UIViewControllers with nib's. The main one is called ViewController and the other is called MenuViewController.
In my ViewController I have the following code:
- (void)viewDidLoad {
[super viewDidLoad];
MenuViewController *menuView;
SWRevealViewController *revealViewController = [[SWRevealViewController alloc] initWithRearViewController:menuView frontViewController:self];
if ( revealViewController )
{
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}
}
- (IBAction)toggleMenu:(id)sender {
NSLog(#"Toggling menu");
SWRevealViewController *revealController = self.revealViewController;
[revealController rightRevealToggleAnimated:YES];
}
The toggle menu button prints "Toggling Menu" as expected but doesn't reveal the menu controller. I know this isn't the conventional way of using the SWRevealViewController but does someone have any ideas how to get this to work ?
Add below line in viewDidLoad()
self.revealViewController.delegate = self;
Here are two methods I use to set up the two (left and right) view controllers for SWRevealViewController. Just call them in viewDidLoad.
-(void) initSlideMenu
{
SWRevealViewController * menuViewController = (SWRevealViewController *)self.revealViewController;
if (menuViewController)
{
[_menuButton setTarget:self.revealViewController];
[_menuButton setAction:#selector(revealToggle:)];
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}
}
-(void) initRightMenu
{
SWRevealViewController * rightViewController = (SWRevealViewController *)self.revealViewController;
if (rightViewController)
{
rightViewController.rightViewRevealOverdraw =0.0f;
[_notificationButton setTarget:self.revealViewController];
[_notificationButton setAction:#selector(rightRevealToggle:)];
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}
}
You can try this one with some modifications. It helped in my case.
- (IBAction)toggleMenu:(id)sender{
[self.btn addTarget:self.revealViewController action:#selector(revealToggle:) forControlEvents:UIControlEventTouchUpInside];
}

UIViewController removeFromSuperview error

I have a UIViewController then when I longpress to self.view it will push a popup (MenuViewController). But when I try to remove popup by removeFromSuperview it still appears
You can see more detail of my problem with this http://www.youtube.com/watch?v=nVVgmeJEnnY
ViewController.m
#import "MenuViewController.h"
#interface ViewController () {
MenuViewController *menu;
}
....
- (void)viewDidLoad
{
....
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(albumButtonPressed : ) name:#"albumButtonPressed" object:nil];
....
}
....
-(void)albumButtonPressed : (NSNotification*) notification {
UIImagePickerController *photoPicker = [[UIImagePickerController alloc] init];
photoPicker.delegate = self;
photoPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentModalViewController:photoPicker animated:YES];
}
...
-(void)handleLongPress:(UILongPressGestureRecognizer*)recognizer {
menu = [[MenuViewController alloc] initWithNibName:#"MenuViewController" bundle:nil];
if (self.imageView.image != nil) {
menu.imageAdded = YES;
}
[self.view addSubview:menu.view];
}
MenuViewController.m
-(IBAction)albumButtonPressed:(id)sender {
[self.view removeFromSuperview];
[[NSNotificationCenter defaultCenter] postNotificationName:#"albumButtonPressed" object:nil];
}
Setting aside my reservations about not applying proper view controller containment, the problem is that your handleLongPress will be called multiple times with different recognizer.state values, once as UIGestureRecognizerStateBegan and again as UIGestureRecognizerStateEnded. You should be checking the state of the gesture, e.g.:
-(void)handleLongPress:(UILongPressGestureRecognizer*)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
menu = [[MenuViewController alloc] initWithNibName:#"MenuViewController" bundle:nil];
if (self.imageView.image != nil) {
menu.imageAdded = YES;
}
[self.view addSubview:menu.view];
}
}
Original Answer:
I'd suggest putting a NSLog or breakpoint at your code with the removeFromSuperview and see if you're even getting to that piece of code.
There are some clear problems here. Specifically, you're not adding added the view associated MenuViewController in handleLongPress properly. If you want a subview with it's own controller, you have to use containment (and that only works with iOS 5 and later). And in containment, you have critical methods like addChildViewController, etc. See Creating Custom Container View Controllers in the View Controller Programming Guide or see WWDC 2011 - Implementing UIViewController Containment. And, as an aside, you're also maintaining a strong reference to MenuViewController, so even if you succeeded in removing it's view, you'd leak the controller.
Spend a little time going through the containment documentation/video, and I think you'll want to revisit how you're presenting your menu. This is dense reading, but worth really understanding. Containment is powerful, but has to be done right.
[self.view removeFromSuperview];
what do you mean by this?????? removing the main view!!!!
Instead of directly using
[self.view removeFromSuperview];
use
[[self.view.superview subviews] makeObjectsPerformSelector:#selector(removeFromSuperview) withObject:self.view];

ios presentModalViewController with two views

I'm confused. I have a navigation controller with a BarItem which opens a first view. After some work is done, I want this view to disappear and I want a second view to open.
root view: navigation controller
first view: activity indicator, where some data is put together
second view: MFMailComposeViewController
In the root view, the BarItem runs these lines to open the first view:
IndicatorViewController *indicator = [[IndicatorViewController alloc] initWithNibName:#"IndicatorViewController" bundle:nil];
indicator.view.backgroundColor = [UIColor clearColor];
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:indicator animated:YES];
The first view (IndicatorViewController) does some work and finally runs
[self dismissModalViewControllerAnimated:YES];
This works fine. But - how do I open the second view?
I tried this:
I open the second view. After closing the second view, my first view pops up again (since it is still there) and get's dismissed at this point. This code is placed in the first view:
- (void) viewDidAppear:(BOOL)animated {
static BOOL firstTime = YES;
if (firstTime) {
//do stuff that takes some time (that's why I show the indicator)
MailViewController *controller = [[MailViewController alloc] init];
if (controller)
[self presentModalViewController:controller animated:YES];
firstTime = NO;
} else {
[self dismissModalViewControllerAnimated:YES];
}
}
Since the first view pops up again, the user can see the indicator one more time, after the second view is closed - and that is not what I want.
What am I missing here? What would be the better way to do this?
I would do something like this. Make a navigationController, and make the first view as the root controller. Then do something like this:
FirstView.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES];
}
- (void) nextView { // however you get to your next view, button/action/etc.
UIViewController *screen = [self.storyboard instantiateViewControllerWithIdentifier:#"yourIdentifier"];
[self.navigationController pushViewController:screen animated:YES];
}
Then in the second view:
SecondView.m
- (void) nextView { // however you get to your next view, button/action/etc.
UIViewController *screen = [self.storyboard instantiateViewControllerWithIdentifier:#"yourIdentifier"];
[self.navigationController pushViewController:screen animated:YES];
}
And finally in the rootview:
RootView.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *navStack = [NSArray arrayWithObject:self];
self.navigationController.viewControllers = navStack;
[self.navigationController setNavigationBarHidden:NO];
}
This will make your RootView the new rootview of the NavigationController.
self.navigationController.viewControllers
is the array with all the ViewControllers that are on the navcontrollers stack. The first object is the rootcontroller. If we replace the whole array with our own array, it knows only one item. You CAN go back by dismissing if that's what you want though. This isn't the prettiest way of doing it, but it's not the worst either.

Back button disappears after pushing two views using UINavigationController

I'm currently developing an app using a UINavigationController. I set the root view controller to ViewController1 and then push ViewController2 and then ViewController3 in response to button click events.
If I then click the back button from view 3, I'm returned to view 2 but this view has no back button. Interestingly as well, having set titles for each of these views ('View 1', 'View 2' and 'View 3' respectively), if I navigate from view 3 back to view 2 using the back button, the title changes to 'View 1' i.e. the title for the initial view (view 1) - not the title for view 2.
If anyone has any idea what might be going on here, your suggestions are very much appreciated.
Many thanks in advance!
Edit: I use the following code to init the UINavigationController in the app delegate:
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen] bounds]];
self.viewController1 = [[ViewController1 alloc] init];
self.viewController2 = [[ViewController2 alloc] init];
self.viewController3 = [[ViewController3 alloc] init];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:viewController1];
self.window.rootViewController = self.navigationController;
I later push view controllers to the UINavigationController on button clicks as follows:
MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate];
[self.navigationController pushViewController:appDelegate.viewController2 animated:YES];
I found the solution - in viewController2 and viewController3 I had the following code in order to hide the navigation bar (I wanted the navigation bar hidden on view1 and then visible on views 2 and 3).
- (void) viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:NO animated:animated];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:animated];
[super viewWillDisappear:animated];
}
I realised it makes far more sense to do the reverse in viewController1 i.e.
- (void) viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:animated];
[super viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:NO animated:animated];
[super viewWillDisappear:animated];
}
and then removing the previous code from view controllers 2 and 3. This solved the issue.

Resources