I have a UIImagePickerController with a custom cameraOverlayView.
I create the imagePicker like this:
self.overlay = [self.storyboard instantiateViewControllerWithIdentifier:#"OverlayViewController"];
self.picker = [[UIImagePickerController alloc] init];
self.picker.sourceType = UIImagePickerControllerSourceTypeCamera;
self.picker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
self.picker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
self.picker.showsCameraControls = NO;
// Insert the overlay
self.picker.cameraOverlayView = self.overlay.view;
self.overlay.pickerReference = self.picker;
self.picker.edgesForExtendedLayout = UIRectEdgeAll;
self.picker.extendedLayoutIncludesOpaqueBars = YES;
self.picker.delegate = self.overlay;
[self presentViewController:self.picker animated:NO completion:^{
}];
For some reason, the OverlayViewController's view is misplaced. It seems as if the constraints haven't been calculated. However, if I explicitly call [self.overlay viewWillAppear:NO]; in the completion block, they layout seems to render correctly.
After some investigation it seems as if viewWillAppear and viewDidAppear is not called for the OverlayViewController.
However, these methods are called if I come back to the imagePicker from a modal 'custom gallery viewcontroller'.
I.e:
rootVC-> (No calls) -> imagePicker -> customGalleryVc
customGalleryVc (dismiss modal) -> (Calls to willAppear) -> imagePicker
What is this? Am I missing something with the fundamentals of the view-hierarchy?
Thank you!
View controller lifecycle methods are called only when the view controller is added into the view controller hierarchy.
The first line of the snippet only instantiated the VC. As you access the view property of the VC, its view (you defined in IB) is loaded. That's all, nothing else happened.
So, a suggestion would be that there is no need to use a view controller for your custom overlay. You just need a normal UIView. With IB, you may create the overlay in a xib, or in a storyboard drag a UIView object to the dock (the top bar of a scene where any gesture recogniser appear if you add any) of your current VC, then you can reference them by creating an IBOutlet.
Related
I'm currently trying to make a small demo app which is supposed to present 2 views at the same time :
- The first one, BaseViewController, is a classic controller with 2 buttons
- The second one, OverViewController, is launch on top of baseView and contains
a last button, and is supposed to be transparent and sending the touch controls
to the first one
Both viewController have their xib. To make it clear : rootView --> baseView + overView (transparent)
My problem is NOT to send the control events from the 2nd view to the first, but to make the 2nd view transparent and functionnal.
Here's what I've tried so far :
1) ------------ Presenting the two view controllers ------------
From the app rootViewController :
- (IBAction)buttonClicked:(id)sender
{
OverViewController *overVC = [[OverViewController alloc] init];
[overVC presentViewControllerFrom:self];
}
From OverViewController :
- (id)init
{
self = [super init];
if (self)
{
self.base = [[BaseViewController alloc] initWithNibName:#"BaseViewController" bundle:nil];
}
return self;
}
I made a custom method to make overView present the baseView before showing up.
- (void)presentViewControllerFrom:(RootViewController *)sender
{
[sender presentViewController:self.base animated:NO completion:nil];
self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.0];
self.view.opaque = NO;
[self.base presentViewController:self animated:YES completion:nil];
}
At this point, the baseView appears, followed by the overView. The button of overView work correctly. Great ! But then the background of overView doesn't show what's supposed to be behind and stays black. No alpha transparency.
2) ------------ Presenting the baseView and adding overView as a subview ------------
From the app rootViewController (same than the one before) :
- (IBAction)buttonClicked:(id)sender
{
OverViewController *overVC = [[OverViewController alloc] init];
[overVC presentViewControllerFrom:self];
}
From OverViewController (not presenting but adding subview) :
- (void)presentViewControllerFrom:(RootViewController *)sender
{
[sender presentViewController:self.base animated:NO completion:nil];
self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.0];
self.view.opaque = NO;
[self.base.view addSubview:self.view];
}
This time, the transparency is great but any touch on the overView button cause a EXC_BAD_ACCESS to show up.
There I am, wondering how to do this. Any help will be greatly appreciated ! :)
Don't use a combination of presenting a view controller and adding subviews. Choose 1. It should be subview that you choose. It can be owned by another controller and that controller should probably be added as a child view controller.
In your first situation you see black because iOS is removing the (now expected to not be visible) view from the view hierarchy. So your view is transparent but what you expect to be behind it is no longer there.
Your second situation is probably just resulting in an invalid view hierarchy somewhere.
The overview controller should not own the base controller. The base controller should be shown and then the overview controller view added as a subview.
As part of my updating my apps to replace the deprecated presentModalViewController with presentViewController, I did some testing.
What I found was disturbing. Whereas presentModalViewController always works and there is no question about it working, I have found the presentViewController method often will not display my VC at all. There is no animation and it never shows up.
My loadView are called without problems, but the actual view does not appear.
So here is what I am doing:
User taps a button in my main view controller.
In the callback for that tap, I create a new view controller and display it as shown above.
The VC never appears (it is an intermittent problem though) but because this VC begins playing some audio, I know that its loadView was called, which looks like as follows.
My button-pressed callback is as follows:
- (void) buttonTapped: (id) sender {
VC *vc = [[VC alloc] init];
[self presentViewController: vc animated:YES completion: nil];
[vc release];
}
Here is my loadview in the VC class:
- (void) loadView {
UIView *v = [UIView new];
self.view = v;
[v release];
... create and addsubview various buttons etc here ...
}
Thanks.
Make sure the controller that calls the function has its view currently displayed (or is a parent to the one currently displayed) and it should work.
I'm using a custom segue which looks like this:
#implementation ModalPushSegue
- (void)perform {
UIViewController *fromController = self.sourceViewController;
UIViewController *toController = self.destinationViewController;
UIView *fromView = fromController.view;
UIView *toView = toController.view;
CGPoint centerStage = toView.centerStage;
toView.center = toView.rightStage;
[fromView.window addSubview:toView];
[fromController addChildViewController:toController];
[UIView transitionWithView:toView
duration:0.5 options:0
animations:^{
toView.center = centerStage;
}
completion:nil];
}
This works well in that the view is slide on from the right as expected and the controller is added to the controller hierarchy.
But later in the added controller I do this:
[self presentViewController:anotherController animated:YES completion:nil];
I would expect this to slide the new controller's view up the screen ala modal style. But what happens instead is the the new view doesn't appear. And when I later remove this controller, it's view flashes up and slides off the screen, leaving a black background instead of the view that was originally there.
I've been playing around with this for a while and if I change the code to
//[self presentViewController:oauthController animated:YES completion:nil];
[self.view addSubview:oauthController.view];
[self addChildViewController:oauthController];
Then the view appears as expected, although not resized.
My problem appears to be with the way that the segues setup the hierarchy vs the way that presentViewController does things. I've done lots of reading and searching but so far have not been able to get a clear picture of exactly what is going on.
I've also played around with using presentViewController in the segue but instead of laying the new view over the old one, the screen goes black and the new view then slides on.
Any help appreciated.
Set a Storyboard ID on your destination view controller (in the storyboard), then try the following code:
AnotherController *viewC = [self.storyboard instantiateViewControllerWithIdentifier:#"AnotherController"];
ModalPushSegue *segue = [[ZHCustomSegue alloc] initWithIdentifier:#"coolSegueName" source:self destination:viewC];
[segue perform];
Got a strange problem. The cancel button that normally is shown at the navigation bar when you add a UIImagePickerController is missing when I add it to my UIPopoverController.
I have tried to subclass the UIImagePickerController and add a navigation button to the navigation bar myself, but it did not show up.
If I select my camera roll the back button gets automatically added, but the cancel button is still missing.
Anyone know what could cause this?
Here is the code i use. Note that _popOver is the instance of my UIPopoverController.
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[_popOver setContentViewController:picker animated:YES];
I have tried this code and now I think it's a default behavior. UIImagePickerController usually presented as modal view controller and the aim of Cancel button is to dismiss it. When you put UIImagePickerController into UIPopoverController then Cancel button became meaningless. For dismissing this you can just tap anywhere (except UIPopoverController view). If you want to access UIImagePickerController when UIPopoverController will/did dismissed use UIPopoverControllerDelegate method:
- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController{
UIImagePickerController *imagePicker = (UIImagePickerController*)popoverController.contentViewController;
return YES;
}
I'm pretty new to iOS development, and I'm trying to develop a simple app in which a button changes the subviews. I have a base RootViewController, which displays MiddleView correctly on init. MiddleView has a single button, labeled "First," which is connected (in Interface Builder) to RootViewController's -openFirstView.
Here's how MiddleView is displayed within RootViewController's -viewDidLoad
MiddleViewController *middleTemp = [[MiddleViewController alloc] initWithNibName:#"MiddleView" bundle:nil];
self.middle = middleTemp;
self.middle.rootViewController = self;
[self.view addSubview:middle.view];
[middleTemp release];
So I have the following ViewControllerss: MiddleViewController and FirstController which control MiddleView and FirstView respectively, and a RootViewController which switches between the two.
I've linked this by placing a RootViewController reference in MiddleViewController, and adding
self.middle.rootViewController = self;
to RootViewController's -viewDidLoad.
-(IBAction)openFirstView:(id)sender{
[middle.view removeFromSuperview];
[self.view addSubview:firstController.view];
}
Note: I've tried initializing firstController within -openFirstView, and when it initially didn't run, I moved the initialization to -viewDidLoad and have proven that it is initializing from the nib correctly by displaying FirstView directly in -viewDidLoad
Where firstController is loaded to a reference earlier in code. However, when I run the code and click the button, nothing in the view changes.
I've done some more diagnosing. I've found specifically that -ViewDidLoad in rootViewController is being called twice, once on the original load and once on the first click of the button, and I'm not sure exactly why.
It seems you haven't inialize your firstView in the below method
-(IBAction)openFirstView:(id)sender{
[middle.view removeFromSuperview];
//First initialize the firstController
[self.view addSubview:firstController.view];
}