I have a viewController called MainViewController, which has a UIButton. When tapped, this button loads another viewController, called ModalSelector like this:
- (IBAction)showModalSelector:(id)sender {
ModalSelector *modal = [[ModalSelector alloc]
initWithNibName:#"modalSelector" bundle:nil];
modal.modalPresentationStyle = UIModalPresentationFormSheet;
modal.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
modal.modalInPopover = YES;
modal.issues = issuesForModal;
modal.parent = self;
[self presentModalViewController:modal animated:YES];
[modal.view setFrame:CGRectMake(-80, 20, 700, 400)];
[issuesForModal release];
[modal release];
}
However, when I rotate the device in the simulator, neither view rotates properly, even though I have this in both viewControllers:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
How can I make it rotate properly?
Thanks
I've discovered there's not a problem with the code, but with the simulator itself. When I test it on the actual device, it works perfectly.
Related
//---------- viewController.h file has
IBOutlet UIView *viewCamera; // custom view for taking camera reason
//------------viewController.m :: inside viewDidLoad method
// -- camera start
[viewCamera setBackgroundColor:[UIColor clearColor]];
imgPicker = [UIImagePickerController new];
imgPicker.allowsEditing = YES;
imgPicker.delegate = self;
imgPicker.cameraOverlayView = viewCamera;
imgPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:imgPicker animated:NO completion:nil];
// ----- ob btn clicked
- (IBAction)btnTakePhotoPressed:(id)sender{
[imgPicker takePicture];
}
App is crashed due to this line, if I comment this line code is running but camera is open in full view, I want to add my camera access on my custom view.
imgPicker.cameraOverlayView = viewCamera;
Thanks in advance :)
When I try to dismiss my UIImagePickerController, it crashes the app. the error is: "Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'preferredInterfaceOrientationForPresentation must return a supported interface orientation!'"
I have the preferred interface orientation set in my view controller.
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
- (BOOL) shouldAutorotate {
return YES;
}
Here is the method I'm calling to bring up the camera, this works fine for adding the camera, but like I said, crashes when I try to remove the camera.
-(IBAction)addCamera:(id)sender
{
self.cameraController = [[UIImagePickerController alloc] init];
self.cameraController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.cameraController.cameraViewTransform = CGAffineTransformScale(self.cameraController.cameraViewTransform,
1.13f,
1.13f);
self.cameraController.showsCameraControls = NO;
self.cameraController.navigationBarHidden = YES;
self.wantsFullScreenLayout = YES;
ar_overlayView = [[UIView alloc] initWithFrame:CGRectZero];
self.view = ar_overlayView;
[self.cameraController setCameraOverlayView:ar_overlayView];
[self presentViewController:cameraController animated:NO completion:nil];
[ar_overlayView setFrame:self.cameraController.view.bounds];
}
-(IBAction)back:(id)sender
{
[ar_overlayView removeFromSuperview];
[cameraController dismissViewControllerAnimated:NO completion:nil];
}
Alright, found the solution, it was really simple, I just changed my back method to:
[self dismissModalViewControllerAnimated:YES];
Now my camera goes away and I can see my original view when I press the back button.
I still haven't figured out what was causing the original problem as I've gone through the info.plist and the methods for supported orientations, but this accomplishes what I wanted.
I'm still curious as to what was causing the error though if anyone has any ideas.
You can try remove this method:
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
It will fix it.but sometimes it will lead to reduce stateBar height.
You cannot remove a UIViewController's main view from its superview.
Instead of this:
self.view = ar_overlayView;
Try this:
[self.view addSubview:ar_overlayView];
Then you will be able to remove it from the superview correctly.
You should be using the didFinishPickingMedieWithInfo method similar to below and use [self dismissModalViewControllerAnimated:YES];
-(void) imagePickerController:(UIImagePickerController *) picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
NSLog(#"Camera");
}
else {
NSLog(#"Album");
}
[self dismissModalViewControllerAnimated:YES];
}
I don't think you need to add ar_overlayView to the current view yourself if it's a camera overlay.
Here's what your code is doing now:
Add ar_overlayView to the current view
Add ar_overlayView as the camera's overlay view
Show the camera view (modal)
At this point, ar_overlayView is being displayed twice. When you send it the removeFromSuperview message on dismissing the camera view, it might be getting confused since it's in two view hierarchies at the same time.
Skipping the self.view = ar_overlayView; or [self.view addSubview:ar_overlayView]; lines should fix the problem.
Also, dismissViewControllerAnimated:completion: should be sent to the same object that presentViewController:animated:completion was called on (self in this case):
-(IBAction)addCamera:(id)sender
{
// -- snip -- //
ar_overlayView = [[UIView alloc] initWithFrame:self.cameraController.view.bounds];
[self.cameraController setCameraOverlayView:ar_overlayView];
[self presentViewController:self.cameraController animated:NO completion:nil];
}
-(IBAction)back:(id)sender
{
[self dismissViewControllerAnimated:NO completion:nil];
}
I'm developing an Augmented Reality application, everything worked properly till now that I need two different kind of visualization (AR and Map) depending on the device orientation. In particular the application should use the landscapeViewController when the device is in landscape mode while it should use another controller (named faceUpViewController ) when the device's orientation is "face up". I tried doing it with two simple view controllers and it works fine. The problem happens when the landscapeViewController uses the AR controller. The view is completely white and I don't understand why. Both the two controllers are "contained" by a Root View Controller. I'm doing everything by coding so without nib files. Here is the code:
RootViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
- (void)deviceOrientationDidChange:(NSNotification *)notification{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationLandscapeLeft) {
if (self.landscapeViewController.view.superview == nil) {
if (self.landscapeViewController == nil) {
LandscapeViewController *lvc = [[LandscapeViewController alloc] init];
self.landscapeViewController = lvc;
[lvc release];
}
[self.faceUpViewController.view removeFromSuperview];
[self.view addSubview:self.landscapeViewController.view];
}
}
if (orientation == UIDeviceOrientationFaceUp) {
if (self.faceUpViewController.view.superview == nil) {
if (self.faceUpViewController == nil) {
FaceUpViewController *fvc = [[FaceUpViewController alloc] init];
self.faceUpViewController = fvc;
[fvc release];
}
[self.landscapeViewController.view removeFromSuperview];
[self.view addSubview:self.faceUpViewController.view];
}
}
}
#end
LandscapeViewController.m
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
UIView *landscapeView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
landscapeView.backgroundColor = [UIColor yellowColor];
self.view = landscapeView;
[landscapeView release];
ARController *arC = [[ARController alloc] initWithViewController:self];
arC.landscapeViewController = self;
self.arController = arC;
[arC release];
}
//When the view appear present the camera feed
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[_arController presentModalARControllerAnimated:NO];
}
FaceUpViewController.m
- (void)loadView
{
UIView *faceUpView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
faceUpView.backgroundColor = [UIColor blueColor];
self.view = faceUpView;
[faceUpView release];
}
ARController.m Very simple version
- (id) initWithViewController:(UIViewController *)theView{
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
self.rootController = theView;
//Retrieve screen bounds
CGRect screenBounds = [[UIScreen mainScreen] bounds];
UIView *overlaidView = [[UIView alloc] initWithFrame: screenBounds];
self.overlayView = overlaidView;
[overlaidView release];
self.rootController.view = overlayView;
// Initialise the UIImagePickerController
UIImagePickerController *picker= [[UIImagePickerController alloc] init];
self.pickerController = picker;
[picker release];
self.pickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.pickerController.cameraViewTransform = CGAffineTransformScale(
self.pickerController.cameraViewTransform, 1.0f, 1.12412f);
self.pickerController.showsCameraControls = NO;
self.pickerController.navigationBarHidden = YES;
self.pickerController.cameraOverlayView = _overlayView;
}
return self;
}
- (void)presentModalARControllerAnimated:(BOOL)animated{
[self.rootController presentModalViewController:[self pickerController] animated:animated];
self.overlayView.frame = self.pickerController.view.bounds;
}
#end
I say again that I'm doing everything by coding thereby without nib files.
I really appreciate any advice!
Thanks
The primary problem with adding and removing your "child" view controllers' views as you've done here is that the view controller life cycle methods (viewWillAppear:, viewDidAppear:, etc.) won't ever get called on your child view controllers. Containers like UINavigationController and UITabBarController have always known how to delegate methods like these appropriately to their children, but UIViewController didn't officially support the ability to nest view controllers under your own custom container before iOS 5. It was possible, but it took a lot more work to do it right.
If you want to stick with the approach of adding and removing subviews, you have two options:
Require iOS 5+, and call addChildViewController:, removeFromParentViewController,
transitionFromViewController:toViewController:duration:options:animations:completion:,
willMoveToParentViewController:, and
didMoveToParentViewController: as described in the Implementing a Container View Controller section of the UIViewController Class Reference.
To support older iOS versions, you'll have to override many of the methods of the UIViewController class and delegate those calls manually to your child view controllers to make them behave as expected. I'd pay particular attention to the sections titled, "Responding to View Events", and "Responding to View Rotation Events" in the UIViewController Class Reference.
A different approach for pre-iOS 5 support is to present your child view controllers using presentModalViewController:animated: rather than adding their views as subviews to a container. Apple describes this approach in the View Controller Programming Guide for iOS under the section, Creating an Alternate Landscape Interface. The advantage of this approach is that your child view controllers are officially supported as first-class members of the view controller hierarchy, so UIKit will automatically manage their life cycles appropriately. You won't have to override and delegate all those methods manually.
You might want to try getting your acceptance rate up a little bit - more people would be willing to help you.
Anyway, wild guess: in your root controller, try putting the contents of
deviceOrientationDidChange
into
deviceOrientationWillChange.
i have added a UIPopover to my navigationbar when the user touches the rightbarbuttonitem.
I want to know whats the cleanest coding for showing and dismissing the popover. With the code below, it works, but not correct.
If the user touches the rightbarbuttonitem first, the popover appears. Now he can choose a cell,.. works. Or you can touch outside the popover and it disappears. Both ways correctly.
But when touching the rightbarbuttonitem and after that, the same touch on that icon again, you have to touch it twice to bring up that popover again. (Not on the first touch).
How should that correctfunction look like? Thanks for your time..
-(void) downloads:(UIBarButtonItem*)button{
NSLog(#"downloads");
if(tableViewController == nil) {
tableViewController = [[[TableViewController alloc] initWithStyle:UITableViewStylePlain] autorelease];
self.popoverController = [[[UIPopoverController alloc] initWithContentViewController:tableViewController]autorelease] ;
[self.popoverController presentPopoverFromBarButtonItem:barButtonItem permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
tableViewController.delegate = self;
}else {
[self.popoverController dismissPopoverAnimated:NO];
tableViewController.delegate = nil;
tableViewController = nil;
//[self.popoverController release];
//self.popoverController = nil;
}
}
//delegate
-(void) selectedTable:(NSString*)text{
NSLog(#"selectedTable: %#", text);
[self.popoverController dismissPopoverAnimated:YES];
}
Comments
Your downloads: method checks for tableViewController value and make decision if UIPopoverController should be displayed or dismissed. But yours selectedTable: method does dismiss popoverController and does not set tableViewController to nil thus it doesn't work as expected by you.
And I also don't see UIPopoverControllerDelegate implementation, especially popoverControllerDidDismissPopover: which will tell you when UIPopoverController was dismissed (by touching outside your UIPopoverController for example).
Memory Management
Read memory management guide. [self.popoverController release] with self.popoverController = nil leads to crash - one release and second release during setting to nil. (I assume your popoverController property retains).
#Chiefly Izzy: Thanks for your help. You point me in the right direction.
Here´s the code i´m using. It works great. Hope that memory management is clean, too. Isn´t it?
-(void) downloads:(UIBarButtonItem*)button{
if([self.popoverController isPopoverVisible])
{
[self.popoverController dismissPopoverAnimated:YES];
[self.popoverController.delegate popoverControllerDidDismissPopover:self.popoverController];
return;
}
// PopOver Bookmarks-Icon
if(tableViewController == nil) {
tableViewController = [[PdfTableViewController alloc] initWithStyle:UITableViewStylePlain]; //autorelease];
self.popoverController = [[UIPopoverController alloc] initWithContentViewController:tableViewController]; //autorelease] ;
[self.popoverController presentPopoverFromBarButtonItem:barButtonItem permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
popoverController.delegate = self;
tableViewController.delegate = self;
}
/* Using "isPopoverVisible" from top or that else. both works
else {
[self.popoverController dismissPopoverAnimated:YES];
[self.popoverController.delegate popoverControllerDidDismissPopover:self.popoverController];
}
*/
}
-(void) selectedTableView:(NSString*)text{
NSLog(#"selectedTableView: %#", text);
[self.popoverController dismissPopoverAnimated:YES];
[self.popoverController.delegate popoverControllerDidDismissPopover:self.popoverController];
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popover{
NSLog(#"popoverControllerDidDismissPopover");
tableViewController.delegate = nil;
[tableViewController release];
tableViewController = nil;
popoverController.delegate = nil;
[popoverController release];
popoverController = nil;
}
I have a MKMapView (also a UIPopoverControllerDelegate) with Annotations. This MapView has, in the MKTestMapView.h file, a UIPopoverController* popoverController defined in the #interface and a #property (nonatomic, retain) UIPopoverController* popoverController; defined outside of the #interface section. This controller is #synthesized in the MKTestMapView.m file and it is released in the - (void)dealloc section. The Annotations in this MapView have rightCalloutAccessoryViews defined to the following:
- (void)mapView:(MKMapView *)mapView2 annotationView:(MKAnnotationView *)aview calloutAccessoryControlTapped:(UIControl *)control{
...
CGPoint leftTopPoint = [mapView2 convertCoordinate:aview.annotation.coordinate toPointToView:mapView2];
int boxDY=leftTopPoint.y;
int boxDX=leftTopPoint.x;
NSLog(#"\nDX:%d,DY:%d\n",boxDX,boxDY);
popoverController = [[UIPopoverController alloc] initWithContentViewController:controller];
popoverController.delegate = self;
CGSize maximumLabelSize = CGSizeMake(320.0f,600.0f);
popoverController.popoverContentSize = maximumLabelSize;
CGRect rect = CGRectMake(boxDX, boxDY, 320.0f, 600.0f);
[popoverController presentPopoverFromRect:rect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];
...
}
Now here comes the fun part. First of all, I am not sure if I need maximumLabelSize and the rect to be the same size. I am new to the popovercontroller so I am playing this by ear..
Okay, the popover shows. Now to dismissing it. I can click anywhere on mapView2 and the popover goes away...but I need the user to click a button in the view if they change anything. URGH!
The docs show:
To dismiss a popover programmatically,
call the dismissPopoverAnimated:
method of the popover controller.
Well, here is the problem: By definition of how the popoverController works, you are clicking inside the view of the displayed popover (to click the button) but have to trigger the dismissPopoverAnimated: method of the controller that launched this popover view, in my case, the popoverController inside the MKTestMapView.m file.
Now, having said all that, remember, [popoverController release] doesn't happen until:
- (void)dealloc {
[popoverController release];
[mapView release];
[super dealloc];
}
So, do i just do the following inside the button (messy but may work):
(Assuming my popover view is a TableView) In the:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
MKTestMapView * mKTestMapView = [[MKTestMapView alloc] init];
[[mKTestMapView popoverController].dismissPopoverAnimated:YES];
}
Here is my issue: I cannot figure out whether doing the above gives me a reference (if there is such a thing) to the existing view that is on the screen -- and therefore the view that is the owner of that popoverController. If it is as simple as
[[[self parentView] popoverController].dismissPopoverAnimated:YES];
I will shoot myself cos I don't think that is the correct syntax either!
This should be easy...yet I am lost. (probably just frustrated with so many iPad differences that I am learning).
Can anyone explain more?
I had the same problem... I had a neat "close" button (X) in the top of my view loaded by the popover, but it didn't work. In my universal app it will be presented as a new view, so that code should stay.
What I did now was that I added the following to my detailedPinView (the view the popover loads):
in the detailedPinView.h file:
#interface detailedPinView : UIViewController {
[...]
UIPopoverController *popover;
[...]
}
-(void)setPopover:(UIPopoverController*)aPopover;
In the detailedPinView.m file:
- (void)setPopover:(UIPopoverController*)aPopover
{
popover = aPopover;
}
The X button closes the view using an IBAction, this is what I did there:
In the detailedPinView.m file:
-(IBAction)releaseDetailedView:(UIButton *)sender
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
if (popover != nil)
{
[popover dismissPopoverAnimated:YES];
}
else {
NSLog(#"Nothing to dismiss");
}
}
else{
[self.parentViewController dismissModalViewControllerAnimated: YES];
}
}
In the class loading the my map and the popover view I added the following code:
[...]
-(void)mapView:(MKMapView *)theMapView annotationView:(MKAnnotationView *)pin calloutAccessoryControlTapped:(UIControl *)control
{
UIViewController *detailController = [[detailedPinView alloc] initWithNibName:#"detailedPinView"
bundle:nil
annotationView:pin];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
UIPopoverController* aPopover = [[UIPopoverController alloc] initWithContentViewController:detailController];
[aPopover setDelegate:self];
[aPopover setPopoverContentSize:CGSizeMake(320, 320) animated:YES];
[detailController setPopover:aPopover];
[detailController release];
[mapView deselectAnnotation:pin.annotation animated:YES];
self.popoverController = aPopover;
[mapView setCenterCoordinate:pin.annotation.coordinate animated:YES];
[self.popoverController presentPopoverFromRect:CGRectMake(382,498,0,0) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
else
{
[self presentModalViewController: detailController animated:YES];
}
[detailController release];
}
[...]
I don't know if the was the answer you were hoping for, I think it might be a bit of a messy way to do it... but giving the time schedule this worked like a charm :)
Here is another simple solution.
I found that we should follow the following steps for clearly dismissing popovers.
dismiss a popover.
release a view loaded by the popover.
Before iOS8, almost all of us may release a view loaded by a popover first, and then we programmatically dismiss the popover.
However, in iOS8, we do the steps in revers.
Before iOS8, my code of dismissing a popover
// creating a popover loading an image picker
picker = [[UIImagePickerController alloc] init];
...
pickerPopover = [[UIPopoverController alloc] initWithContentViewController:picker];
[pickerPopover presentPopoverFromRect:aFrame inView:aView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
// dismissing the popover
[picker.view removeFromSuperview]; // (1) release a view loaded by a popover
[picker release], picker = nil;
if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
[pickerPopover dismissPopoverAnimated:YES]; // (2) dismiss the popover
}
In iOS8, the dismissing code part should be changed as below,
if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
[pickerPopover dismissPopoverAnimated:YES]; // (2) dismiss the popover first
}
[picker.view removeFromSuperview]; // (1) and then release the view loaded by the popover
[picker release], picker = nil;