I'm trying to make a modal popup appear when scanning QR codes.
I have a .xib file that has the necessary views and is linked to my custom view controller. The usual stuff, except that I have a navigation controller so that I can add a close button at the top later and/or in case I need to push another screen.
To make things a little more complicated, I do not have direct access to the view controller (code is in a manager file). This is the code to create and launch the popup:
self.m_scannerVC = [[BarcodeScannerVC alloc] initWithNibName:NSStringFromClass([BarcodeScannerVC class]) bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.m_scannerVC];
// Set the navigation controller to be a modal popup.
navigationController.modalTransitionStyle = UIModalPresentationFormSheet;
// Set the size of the popup here.
navigationController.preferredContentSize = CGSizeMake(100, 200);
UIWindow *win = [[UIApplication sharedApplication].delegate window];
UINavigationController * winNav = (UINavigationController *)win.rootViewController;
winNav.providesPresentationContextTransitionStyle = YES;
winNav.definesPresentationContext = YES;
winNav.topViewController.providesPresentationContextTransitionStyle = YES;
winNav.topViewController.definesPresentationContext = YES;
winNav.modalPresentationStyle = UIModalPresentationOverCurrentContext;
winNav.topViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
// And now present the view in a modal fashion.
[winNav.topViewController presentViewController:navigationController animated:YES completion:^{
// Once presented, set the capture layer to fix inside our camera preview box.
[self.captureLayer setFrame:self.m_scannerVC.m_viewCameraPreview.layer.bounds];
// Adding the camera AVCaptureVideoPreviewLayer to our view's layer.
[self.m_scannerVC.m_viewCameraPreview.layer addSublayer:self.captureLayer];
// Start the camera capture session as soon as the view appears completely.
[self.captureSession startRunning];
}];
Any tips on what I am doing wrong will be greatly appreciated!
I found out why, I'm so stupid!
navigationController.modalTransitionStyle = UIModalPresentationFormSheet;
should be
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
Related
I am updating an old app to the new adaptive size way of doing things and having difficulty getting a popover with a navigation controller to work.
My goal: I want to be able to open a popover from a button when the app is either compact and regular horizontal. The popover has a tableview and uses a navigation controller to push view controllers when the user touches a row on the table. I can get the popover to open correctly, but I can’t figure out who to make the pushes work.
Here’s the code that opens the popover:
OptionsController *vc = [[OptionsController alloc] initWithNibName:#"OptionsView" bundle:nil];
vc.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController *popover = [vc popoverPresentationController];
popover.delegate = self;
[self presentViewController:vc animated: YES completion: nil];
popover.permittedArrowDirections = UIPopoverArrowDirectionUp; // change as necessary
popover.sourceView = self.view;
CGRect popoverRect = [self.view convertRect:[sender frame] fromView:[sender superview]];
popover.sourceRect = popoverRect;
This code correctly opens a popover in either compact or regular size.
In the OptionsController’s didSelectRowAtIndexPath method, I have this(controllersArray is an array of UIViewControllers, each of which corresponds to a row in the table):
UIViewController *nextController = [self.controllersArray objectAtIndex: [indexPath row]];
[self.navigationController pushViewController:nextController animated:YES];
All this executes, but no push occurs, so the next view never appears.
I clearly am not understanding something about using the UIViewController’s navigationController, or how to install a navigationController to make this work. After three or four days of digging around to try to understand how to make this work, I'd appreciate any insights, or links to documentation about how to do this. Thanks in advance.
Crud - this has a very easy answer. Just took me thinking a different way and digging through Larcerax's comments. Here's how to make this work:
OptionsController *vc = [[OptionsController alloc] initWithStyle:UITableViewStylePlain];
vc.title = #"Options";
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController: vc];
nc.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController *popover = [nc popoverPresentationController];
popover.delegate = self;
[self presentViewController:nc animated: YES completion: nil];
popover.permittedArrowDirections = UIPopoverArrowDirectionUp; // change as necessary
popover.sourceView = self.view;
CGRect popoverRect = [self.view convertRect:[sender frame] fromView:[sender superview]];
popover.sourceRect = popoverRect;
The difference is that I create the UINavigationController in the usual manner...
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController: vc];
...set the navigationController's presentation style to popover, then get the popoverPresentationController from the navigationController - before I was doing those two methods on the UIViewController.
nc.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController *popover = [nc popoverPresentationController];
Finally, I present the navigationController:
nc.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController *popover = [nc popoverPresentationController];
This approach presents a popover, in compact and regular horizontal sizes, that contains navigation controller functionality: just what I wanted.
Thanks again to Larcerax whose answer wasn't what I needed, but made me re-think what I was doing in a different way. As usual, StackOverflow comes through.
Here's what you should try. I use about 10-40 navigation controllers per app for the apps I work on and I've had this same sort of issue, I've not used popovers, but I've subclassed the crap out of navigation controllers and view controllers to have encountered your same problem. The thing is that if you do this:
UIViewController * ff = [UIViewController new]
[self presnetViewController:ff ... blah blah blah
There is apparently NO navigation system attached to the modal view controller and therefore you can't navigate to anything else, you can only close the modal and move on. So, this is what I do to resolve this and it works everytime, well it works everytime for UIViewControllers, give it a shot
see the following, it's not for popovers, but the principle is the same:
NSHTermsOfServiceViewController * pvc = [NSHTermsOfServiceViewController new];
UIBarButtonItem * backBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"exit-button"] style:UIBarButtonItemStylePlain target:self action:#selector(backerPressed)];
NSHNavigationController * ssf = [[NSHNavigationController alloc] initWithRootViewController:pvc];
[[pvc navigationItem] setLeftBarButtonItem:backBarButtonItem];
[[self navigationController] presentViewController:ssf animated:true completion:nil];
NSHTermsOfServiceViewController <== is a subclass of another subclass of a UIViewcontroller, it's basically a UIViewController on steroids, that's all
NSHNavigationController is a UINavigationController that is subclassed and pumped up on steroids for animations
The flow is this:
create a viewController
create a new UINavigationController
Set the view controller you created in step 1 as the root view controller of the navigation controller created in step 2
present the NAVIGATION CONTROLLER, not the UIViewController, you can present this navigationController from a view controller like so ..
v
[self presentViewController:ssf animated:true completion:nil];
or you can present it from the current view controller's navigation controller which is what I prefer, like so:
[[self navigationController] presentViewController:ssf animated:true completion:nil];
Your code, modified, the only problem is that I don't know if you can present a UIPopOverViewController by rooting it inside a navigation controller
OptionsController *vc = [[OptionsController alloc] initWithNibName:#"OptionsView" bundle:nil];
UIPopoverPresentationController *popover = [vc popoverPresentationController];
UINavigationController * stuff = [[NSHNavigationController alloc] initWithRootViewController:popover];
stuff.modalPresentationStyle = UIModalPresentationPopover;
stuff.delegate = self;
[self.navigationController presentViewController:stuff animated: YES completion: nil];
popover.permittedArrowDirections = UIPopoverArrowDirectionUp; // change as necessary
popover.sourceView = self.view;
CGRect popoverRect = [self.view convertRect:[sender frame] fromView:[sender superview]];
popover.sourceRect = popoverRect;
Yep, my bad, doesn't work for popover, I just tried it
So, with that said, is it absolutely necessary to use a popover? Why are you using this and now just a UIViewcontroller that you reconfigure to look like a popover and then you have what you need?
Here's this, I just tried it with the Ipad simulator, and it allowed a push, just as it should have.
NSHLoginViewController * pvc = [NSHLoginViewController new];
UINavigationController *navController = [[UINavigationController alloc]initWithRootViewController:pvc];
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:navController];
UIView * stuff = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 1000, 1000)];
[self.view addSubview:stuff];
[popover presentPopoverFromRect:[[self contentView] nameField].frame inView:stuff permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
this: [[self contentView] nameField].frame is just a uitextfield, nothing special, that's all, and the method above presented the login viewcontroller, when I put in my credentials, I pressed log in and it pusehed the next viewcontroller as it normally would, there's probalby something wrong with the touches being intercepted by your uitableview or whatever, perhaps not, but this method did work for me.
That simple example but that don't work;
I have ViewController where inside on NavigationConroller, then I want to add new ViewConroller with its self navigation controller.
In main viewController:
CustomViewController *vc = [[CustomViewController alloc] init];
NewNavigationVC *nav = [[NewNavigationVC alloc] initWithRootViewController:vc];
[self presentViewController:nav animated:NO completion:nil];
Two controllers has a background color clear, but still black color.
Navigation bar I can do clear, but not a view.
UPDATE:
if i change self.window.backroundColor to red for example, that work but not clear
UPDATE 2:
[self addChildViewController:vc];
[self.view addSubview:vc.view];
[vc didMoveToParentViewController:self];
and when I want to dealloc vc
[vc willMoveToParentViewController:nil];
[vc.view removeFromSuperview];
[vc removeFromParentViewController];
All work ok without navigation controller
A viewController's view's backgroundColor can't be clear (as in showing the previous viewController's view on the stack). Pushing or presenting a viewController will put the new viewController on the stack and hide the previous viewController completely.
If you want a clear backgroundColor on the view, you will need to either:
1) set the viewController as a childViewController of the previous viewController - then animate the transition yourself.
Or
2) transplant the viewController logic into the previous viewController and have a new uiview act as that view (you also need to animated the transition yourself).
The solution is as follows. For clear example we use tableViewController:
UITableViewController *modalVC = [UITableViewController new];
UINavigationController *modalNVC = [[UINavigationController alloc] initWithRootViewController:modalVC];
UIViewController *mainVC = [UIViewController new];
UINavigationController *mainNVC = [[UINavigationController alloc] initWithRootViewController:mainVC];
modalVC.view.backgroundColor = UIColor.clearColor;
mainVC.view.backgroundColor = UIColor.redColor;
mainNVC.modalPresentationStyle = UIModalPresentationCurrentContext;
[mainNVC presentViewController:modalNVC animated:YES completion:NULL];
The key feature is that you have to set modalPresentationStyle of presentingViewController to UIModalPresentationCurrentContext.
It works fine BUT without slide animation. You will get result immediately.
But you can still use "blood hack" to retain visual animation by successive presenting, dismissing and presenting again:
modalVC.view.backgroundColor = UIColor.clearColor;
mainVC.view.backgroundColor = UIColor.redColor;
[mainNVC presentViewController:modalNVC animated:YES completion:^{
[modalNVC dismissViewControllerAnimated:NO completion:^{
mainNVC.modalPresentationStyle = UIModalPresentationCurrentContext;
[mainNVC presentViewController:modalNVC animated:NO completion:NULL];
}];
}];
You basically need to tell the navigation controller to:
navigation.modalPresentationStyle = .overCurrentContext
In other words:
A presentation style where the content is displayed over another view controller’s content.
and that's it.
You can also make sure that:
navigation.view.backgroundColor = .clear
I'm using an IIViewDeckController as my root view controller. It's a library that allows me to have a sliding menu view controller similar to the sliding menu in the facebook application. It works by initiating it with a center and left view controller and then setting the entire thing as the root view controller. Here's what that looks like in AppDelegate.m:
FeedViewController* mainFeed = [[FeedViewController alloc] init];
UINavigationController* mainStack = [[UINavigationController alloc] initWithRootViewController:mainFeed];
MenuViewController* sideMenu = [[MenuViewController alloc] init];
IIViewDeckController* viewDeck = [[IIViewDeckController alloc] initWithCenterViewController:mainStack leftViewController:sideMenu];
self.window.rootViewController = viewDeck;
When in the center view controller (navStack), I have a button, that when pressed, presents a modal view controller, login:
- (void)openLogin{
LoginViewController* login = [[LoginViewController alloc] init];
[self presentViewController:login animated:YES completion:nil];
}
In login, after the user enters their credentials and logs in, I try to have the navStack dismiss login, and then immediately push in a different controller creator:
- (void)dismissLoginController{
//get a reference to the mainFeed
IIViewDeckController* viewDeck = (IIViewDeckController*)self.presentingViewController;
UINavigationController* navStack = (UINavigationController*)viewDeck.centerController;
FeedViewController* mainFeed = (FeedViewController*)navStack.topViewController;
//have it dismiss login, then push creator
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
[mainFeed openListingCreator];
}
openListingCreator in mainFeed:
- (void)openListingCreator{
ListingCreatorViewController* creator = [[ListingCreatorViewController alloc] init];
UINavigationController* creationStack = [[UINavigationController alloc] initWithRootViewController:creator];
[self presentViewController:creationStack animated:YES completion:nil];
}
The error i'm getting is: Warning: Attempt to present <UINavigationController: 0xa40c790> on <IIViewDeckController: 0xa843000> whose view is not in the window hierarchy!
I'm assuming this is because IIViewDeckController isn't on the screen when I ask it to present a view controller. However openListingCreator is being called AFTER i dismiss the login view controller, so I'm not sure why it's doing this. I believe it has something to do with IIViewDeckController since I have done this same exact process before in other projects without it and had no problems.
Any ideas?
you get this error because the openListingCreator is being called while the transition effect of the LoginViewController is still active. You could put the [mainFeed openListingCreator]; in the completion block of [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; or present creationStack directly from the LoginViewController.
[self.presentingViewController dismissViewControllerAnimated:YES completion:^{
[mainFeed openListingCreator];
}];
How to I change the size of modal dialog? Why is this not working?
SlingDialogViewController *slingDialog = [[SlingDialogViewController alloc] init];
slingDialog.modalPresentationStyle = UIModalPresentationFormSheet;
slingDialog.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
slingDialog.view.superview.frame = CGRectMake(0, 0, 900, 700);
[self presentModalViewController:slingDialog animated:YES];
Do this:
SlingDialogViewController *slingDialog = [[SlingDialogViewController alloc] init];
slingDialog.modalPresentationStyle = UIModalPresentationFormSheet;
slingDialog.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:navController animated:YES completion:nil];
slingDialog.view.superview.frame = CGRectMake(0, 0, 900, 700);
slingDialog.view.superview.center = self.view.center;
It's important to call presentViewController:animated:completion first, from my experience at least.
The modal controllers in iOS have fixed sizes. I don't recommend trying to change them, it never works reliably.
A workaround is to implement it by yourself. A modal (presented) controller is a very simple functionality:
A view overlapping your presenting controller, so that it can't be clicked (and it is dimming the contents to make them look backgroundish)
A child view controller (and its view) added to your controller.
I am creating a viewcontroller in this way:
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"PhotoViewControlleriPhone" bundle:nil];
UIViewController *vc = [sb instantiateInitialViewController];
vc.view.backgroundColor = [UIColor clearColor];
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:vc animated:NO];
vc.view.frame = CGRectMake(imageView.frame.origin.x, imageView.frame.origin.y + 64, imageView.frame.size.width, 200.000000);
vc.view.layer.cornerRadius = 10; // this value vary as per your desire
vc.view.clipsToBounds = YES;
The viewcontroller is not full screen, so you can still see the previous one. I want to be able to see it, but lock it. Just like when you use ios facebook sharing, you see the background, but it becomes darker and you can't interact with it. How can I do this?
I believe the problem is that you’re displaying it using -presentModalViewController:animated:. Using that method carries with it some assumptions about the view controller you’re hosting; one of the assumptions it makes (on iPhone-type devices, at least) is that the view controller takes up the entire screen and is opaque.
To get around this, try adding the modal view controller’s view to the current view controller’s view manually. You’ll need to set the view controller hierarchy up to match the view hierarchy, so your code would look like this:
[self addChildViewController:vc];
[self.view addSubview:vc.view];
You’ll need to adjust the incoming view’s frame to position it within its new superview, but that should allow you more freedom.
My workaround is to take a screenshot with code, pass the UIImage as a parameter to the new UIViewController, and display it as a background image. In that way it appears transparent, and the you don't have to disable the underlying controls that might be reachable/accessible.
iOS8+
You can use below code snippet for iOS8+, its worked for me
SecondViewController *secondViewController = [[SecondViewController alloc] init];
secondViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
[self presentViewController:secondViewController animated:YES completion:nil];