iPad: how to close UIPopoverController from within the "popped" ViewController - ipad

I'm converting and iphone project to ipad. On iphone I have a mainViewController that opens a loginViewController using addSubView.
On the iPad I would like to display that loginViewController in a popover. so I did something like:
UIPopoverController *loginPop = [[UIPopoverController alloc] initWithContentViewController:loginViewController];
[loginPop presentPopoverFromRect:CGRectMake(150, 150, 90, 90) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:true];
This works fine. The problem is when the loginViewController finishes its "business". On the iPhone I just call a simple [self.view removeFromSuperview]; But on the ipad this causes the view to be removed from the PopoverController but the frame of the popup stays.
So my question is: is there any simple way from within the loginViewController to remove its PopoverController container (without using delegate or notifications)?

Yes, your loginViewController should keep a reference to the popover. Then you can use the dismissPopoverAnimated: method of your popover itself to remove it.

Actually, I want to implement that, but I remembered that we can access application delegate, which in turn will have access to main view of it, In there, you can store property of the popover, and you can call the dismissPopoverAnimated.
Like this :
MyAppDelegate *app = (MyAppDelegate *) [[UIApplication sharedApplication] delegate];
MyViewController * myView =[app viewController];
[myView.popover dismissPopoverAnimated:YES];

Related

Presenting a view controller triggers UIApplicationDidChangeStatusBarOrientationNotification

I've got a universal app that supports both iPad and iPhone, and on the iPhone it supports UIInterfaceOrientationPortraitUpsideDown.
When I run the app on an iPhone, then rotate the device to UIInterfaceOrientationPortraitUpsideDown, then modally present a view controller like this:
UIViewController* vc = [[UIViewController alloc] initWithNibName:nil bundle:nil];
UINavigationController* navigationController = [[UINavigationController alloc]
initWithRootViewController:vc];
navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:navigationController animated:YES completion:nil];
Very unexpectedly, this triggers UIApplicationDidChangeStatusBarOrientationNotification.
OK, so maybe this is because the modal view controller does not support the upside-down portrait orientation. Let's make a custom subclass of UIViewController that overrides supportedInterfaceOrientations:
- (NSUInteger) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
But no difference, presenting the custom VC still triggers the notification. I also noticed that the notifications is not triggered on iPad, only on iPhone. Finally, the behaviour is the same in iOS 8 and iOS 7 (specifically I am testing on iOS 8.1 and iOS 7.1, but I doubt that this makes any difference).
Question: Is it normal that this notification is sent when a modal VC is presented? Why does it happen only for UIInterfaceOrientationPortraitUpsideDown, but not for the regular portrait or any of the two landscape orientations? And why does it happen only on iPhone, not on iPad?
Note: I can provide a more complete, minimal running example on request.
The problem was, indeed, that the modally presented view controller did not support UIInterfaceOrientationPortraitUpsideDown. My idea to add an override of supportedInterfaceOrientations was correct, but where I went wrong was that I added the override to the custom subclass of UIViewController - but this is not the presented view controller!
The presented view controller, as my code snippet shows, is the UINavigationController instance. One correct solution to my problem therefore is to create a subclass of UINavigationController and add supportedInterfaceOrientations to that subclass.
Another correct, but in my case more elegant solution than subclassing is to make use of the UINavigationControllerDelegate protocol.
Step 1: Modify the code snippet from my question:
UIViewController* vc = [[UIViewController alloc] initWithNibName:nil bundle:nil];
UINavigationController* navigationController = [[UINavigationController alloc]
initWithRootViewController:vc];
navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
// --- THE NEXT LINE IS NEW ---
navigationController.delegate = self;
[self presentViewController:navigationController animated:YES completion:nil];
Step 2: Make sure that the class that contains the presenting code (= the above code snippet) adopts the UINavigationControllerDelegate protocol.
Step 3 (yes, there is a step 3): Add this protocol method to the newly designated delegate class:
- (NSUInteger) navigationControllerSupportedInterfaceOrientations:(UINavigationController*)navigationController
{
return UIInterfaceOrientationMaskAll;
}
If you have many places where you present view controllers you will probably want to have some shared object that adopts UINavigationControllerDelegate, but that's part of your application design.

UIPopover with UINavigationController and UITableView: tableView delegates not called

I am trying to display a simple iPad popover which contains a navigationController with a tableView.
popover -> navigation controller -> view controller -> table view.
I do need the navigationController because on touching the cells I want to push another viewController (within that same popover).
Without the navigation controller, everything is fine.
But as soon as I put the viewController inside a navigationController, the tableView stops responding (didSelectRow doesn't get called). I suppose something is wrong with my delegates but I just can't work it out.
. The navigationController responds fine (I can hit a button I have in the top bar)
. The buttons that are IN the cells respond fine.
. If I touch down and hold on a cell it gets highlighted, but not selected.
UPDATE: I just found out that if I hold the cell down for at least a second, the delegate is called when I release it. any less than that and it is never called...???
Here is the code use:
ModalViewController* controllerWithTable = [[self storyboard] instantiateViewControllerWithIdentifier:identifier];
UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:controllerWithTable];
UIPopoverController* popover = [[UIPopoverController alloc] initWithContentViewController:navigationController];
popover.delegate = self;
[popover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
If you are using a popoverController to show a table view, you don't need to embed it in a navigation Controller, Instead make a different Viewcontroller with the table view and just shoe that inside a popover controller .I used it somewhat like this..
Make a global reference for UIPopoverController in your app delegate,
//in your Appdelegate.h file declare this
#property(strong, nonatomic) UIPopoverController *popOverForTableView;
Now In the controller which you want to show the PopOver view use this,
TableViewCOntroller* popoverContent = [[TableViewCOntroller alloc] init];
NSString *identifier=#"tableVC"
popoverContent =[[UIStoryboard storyboardWithName:#"Main"
bundle:nil]
instantiateViewControllerWithIdentifier:identifier];
popoverContent.preferredContentSize = CGSizeMake(330, 280);
AppDelegate *appDelegate=(AppDelegate *)[[UIApplication sharedApplication]delegate];
appDelegate. popOverForTableView = [[UIPopoverController alloc]initWithContentViewController:popoverContent];
appDelegate.popOverForTableView.delegate = self;
appDelegate.popOverForTableView.backgroundColor=[UIColor blackColor];
[appDelegate.popOverForTableView presentPopoverFromRect:self.date.frame inView:self.view permittedArrowDirections:(UIPopoverArrowDirectionUp) animated:YES];
You can dismiss it accordingly…,…see if this Helps..
Ok I found the answer there: https://stackoverflow.com/a/18159463/3562952
The tableView was not irresponsive, it was deceptively responding only after a 1-3 seconds hold down.
I had a tap responder on the parent view that was capturing the tap. I am now removing it when displaying the popover and putting back in on dismissal.
I was googling for the wrong symptoms :)

ios 5 opening a specific viewcontroller with the correct TabBar and NavController wrapped

I will try to explain this as best as I can.
My application has a TabBarController which functions as the main navigation
I have a modal view that I segue to to add a list. that screen can be reached from 2 different viewcontrollers.
From the main route I simple just close the modal and all is fine. However from the second route I need to be able to open up an entirely new ViewController.
The issue that I am having is that I can not seem to open that ViewController with the TabBar and NavBar included.
This is the code I am currently playing with to try to get it to work.
UITabBarController *tabController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainInterface"];
tabController.selectedIndex = 1;
//_window.rootViewController = tabController;
UINavigationController *groceryNavController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainNavController"];
UIViewController *groceryViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"GroceryViewController"];
UIViewController *currentVC = self;
[currentVC.navigationController pushViewController:groceryViewController animated:YES];
One way to to do it is through the delegate. If in the delegate, the relevant navigation controller is called:
self.navigationController
Then you would have to do:
YourAppDelegate *delegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.navigationController pushViewController:groceryViewController animated:YES];
(replace "YourAppDelegate" with the actual name of your app delegate)

Orientation Landscape -> Portrait isn't working

I have another orientation problem. But this one is very tricky.
My RootViewController is a normal NavigationController.
self.window.rootViewController = _naviController;
which has another ViewController inside, lets call it VC1.
VC1 has some buttons and labels. Its like an overview with folders.
If I press a button I come to the next ViewController with 3 ViewController (Page) and another bunch of buttons (like inside a folder looking at the pictures/thumbnails inside):
Archiv *archiv = [[Archiv alloc] init];
[self.navigationController pushViewController:archiv animated:YES];
[archiv release];
in loadView:
firstPage = [[Page alloc] initViewWithFrame:CGRectMake(0, 0, 768, 960)];
[firstPage setRootViewController:self];
secondPage = [[Page alloc] initViewWithFrame:CGRectMake(0, -960, 768, 960)];
[secondPage setRootViewController:self];
thirdPage = [[Page alloc] initViewWithFrame:CGRectMake(0, 960, 768, 960)];
[thirdPage setRootViewController:self];
If I now click again on a button the active Page push my third ViewController (image with resizing, dragging...):
Picture *pic = [[Picture alloc] initWithPicURLString:urlString];
[rootViewController.navigationController pushViewController:pic animated:YES];
[pic release];
With the BackButton of the NavigationController I can always come back to the previous view.
Some more informations:
Every ViewController supports all orientations
Every ViewController implements - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation with return YES;
Every ViewControler calls the [super init] in their init-methode
I already read Apple's Q&A: Why won't my UIViewController rotate with the device
Now the tricky problem:
If I switch from 2nd VC to the 3rd VC, change the orientation there from portrait to landscape and press the BackButton everything is working (shouldAutorotateToInterfaceOrientation is calling, frame size and origins changing ...).
BUT if I do it the other way around, I am in landscape mode, switch from 2nd VC to 3rd VC, rotate to portrait and come back to 2nd VC with BackButton, the status- and controllerBar are at the top but the shouldAutorotateToInterfaceOrientation wasn't called.
Please help me. $h#rky
Try this, it works for me:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self shouldAutorotateToInterfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation] ];
}
Today I got the idea that solved the problem without knowing the cause.
In my third VC I just created a pointer to the 2nd View and called the shouldAutorotateToInterfaceOrientation myself.
But the point is still the same: Why isn't shouldAutorotateToInterfaceOrientation not calling in the described situation?
Kind regards. $h#rky
shouldAutorotateToInterfaceOrientation only called when user rotate, so when you from landscape to portrait or otherwise then view controller still landscape, so this solve problem, you have to hack code, it's mean when you push to view controller from landscape to portrait presentViewController example:
ListCurrentViewController *list = [self.storyboard
instantiateViewControllerWithIdentifier:#"ListCurrentViewController"];
[self.navigationController presentViewController:list animated:NO completion:Nil];
[list dismissViewControllerAnimated:NO completion:Nil];
[self.navigationController pushViewController:list animated:YES];
in ListViewController function called:
(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation // iOS 6 autorotation fix { return UIInterfaceOrientationPortrait; }
and you have to create category for UINavigationController
(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [self.visibleViewController preferredInterfaceOrientationForPresentation];
}
I hope this solve will help you.

iPad's UISplitViewController did not rotate well when modalView is rotated

I have rotation problem on UISplitViewController. In my case, after calling present modal on splitViewController (this splitViewController is the root view of window) to show another view. When that modalView is rotated, the splitView rotate incorrectly.
The splitView appears like it rotated, but the DetailViewController stays like it appear on portrait, and the RootViewController did not show at all. It still behave like on portrait mode. It seems that the splitView is rotated, but not the underlying views (DetailViewController and RootViewController).
I try to implement willRotateToInterfaceOrientation on the modalView, and call the splitView's willRotateToInterfaceOrientation method using delegate.
This is what I implement in modalView:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
MacProjectAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.splitViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
The result is, the DetailViewController behave correctly, but not with RootViewController. It stay hidden. It will re-appear after the user manually rotate to portrait and back to landscape. But obviously user did not want that.
This is what I did when presenting modalView:
ModalViewController *modalView = [[ModalViewController alloc] init];
MacProjectAppDelegate *delegate = (MacProjectAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.splitViewController presentModalViewController:modalView animated:YES];
[modalView release];
Is there a way to show the RootViewController in this case? Or is my doing things is wrong?
Edit:
It seems all views did not rotate following the modalView. neither the RootViewController nor DetailViewController did rotate (the didRotateFromOrientation on both ViewController did not called at all). So now I assume it didn't rotate following the device/modalView.
OK. My problem has been solved.
What I did is as I wrote as comment on Shurakov's comment.
I acquire appDelegate and get the splitViewController to call willRotateToInterfaceOrientation.
MacProjectAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.splitViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
I did it twice since only one of them seems did not work (I don't know why). One at modalViewController's willRotateToInterfaceOrientation method, one at RootViewController's willAppear method.
To remove the navigator/popover button on DetailViewController, I simply invalidate that button by calling invalidateRootPopoverButtonItem at RootViewController's willAppear method. This is what it's looks like on this particular method (for the first one at modalViewController, I just pass the willRotateToOrientation command to splitViewController):
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (UIInterfaceOrientationIsLandscape(orientation)) {
MacProjectAppDelegate *delegate = (MacProjectAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.splitViewController willRotateToInterfaceOrientation:orientation duration:1];
[[[delegate.splitViewController viewControllers] objectAtIndex:1] invalidateRootPopoverButtonItem:self.rootPopoverButtonItem];
}
Hope this can explain how I overcome my problem and help anyone who had the same problem as me.

Resources