As I understand from reading documentation, UIPopoverControllers are only supported on the iPad. Therefore if you try to declare a variable as a UIPopoverController and run the app in the iPhone simulator or on an iPhone, you get an error such as:
UIPopoverController initWithContentViewController:] called when not running under UIUserInterfaceIdiomPad
So I have a universal monotouch app I am trying out, where I would like to use a UIPopoverController when the user is using an iPad, for the iPhone I have another solution.
This is how I am declaring it at the moment, but obviously running on the iPhone does not work, and I get the above error message.
public partial class IOPSCalculatorViewController : UIViewController
{
static bool UserInterfaceIdiomIsPhone {
get { return UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone; }
}
static bool UserInterfaceIdiomIsIPAD {
get { return UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad; }
}
UIPopoverController DetailViewPopover = new UIPopoverController(new PopoverContentViewController());
}
How can I only declare the:
UIPopoverController DetailViewPopover = new UIPopoverController(new PopoverContentViewController());
part if the device is an iPad? I need the UIPopoverController to be in the public partial class so that I can access it publically in other places.
Instead of declaring and allocating in one line just split it in two lines. E.g.
UIPopoverController DetailViewPopover = null;
if (IOPSCalculatorViewController.UserInterfaceIdiomIsIPAD) {
DetailViewPopover = new UIPopoverController (new PopoverContentViewController ());
}
That will also work if DetailViewPopover is a (public) field instead of an instance variable and, as long as the UIPopoverController constructor is not invoked, you won't be getting the error.
You need to find out what is your current device and write code for iphone and iPad as well. here is a snap of code that I've used in my case.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
//Do your coding here
}
else {
if(![popoverController isPopoverVisible]){
contact = [[[ContactViewController alloc] initWithNibName:#"ContactViewController_iPad" bundle:nil] autorelease];
popoverController = [[[UIPopoverController alloc] initWithContentViewController:contact] retain];
[popoverController setPopoverContentSize:CGSizeMake(400.0f, 400.0f)];
[popoverController presentPopoverFromRect:CGRectMake(230, 860, 320.0f, 320.0f) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
[popoverController release];
}else{
[popoverController dismissPopoverAnimated:YES];
}
}
hope this will help you. Happy Coding!!!
Related
I'm working on a universal iOS app and wanting to use different xib files for iPhone 5. The way it currently works is by automatically selecting the file with the right device modifier (as specified here: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/LoadingResources/Introduction/Introduction.html#//apple_ref/doc/uid/10000051i-CH1-SW2). However there's no mention there of how to handle the iPhone 5. I'm surprised because I'm under the impression they'd prefer developers to create a different/unique experience on the 5 instead of just auto scaling...
Anyone aware of a modifier that works? (eg myxib~iphone5.xib) It would be more convenient than having to handle the device detection and switching myself.
Cheers!
There's no naming convention unfortunately, but there's a way. This is what I'm using:
I have a global .h file called GlobalConstants that I put all my #define macros in. There I have this:
static BOOL is4InchRetina()
{
if ((![UIApplication sharedApplication].statusBarHidden && (int)[[UIScreen mainScreen] applicationFrame].size.height == 548) || ([UIApplication sharedApplication].statusBarHidden && (int)[[UIScreen mainScreen] applicationFrame].size.height == 568))
return YES;
return NO;
}
then in any of my view controller classes - I override the init method like this:
#import GlobalConstants.h
-(id) init {
if(is4InchRetina()) self = [super initWithNibName:#"myNibName5" bundle:[NSBundle mainBundle]];
else self = [super initWithNibName:#"myNibName" bundle:[NSBundle mainBundle]];
return self
}
I want to change the orientation of the app without changing the device orientation in iphone app.
I want to change my view from portraid mode to landscap mode programmatically.
And also want to know that will this be accepted by the apple store or not ?
Thanks
Now I got the solution from other that is as follow
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
when you add this line at that time one warning appear and for remove this warning just add bellow code on you implementation file..
#interface UIDevice (MyPrivateNameThatAppleWouldNeverUseGoesHere)
- (void) setOrientation:(UIInterfaceOrientation)orientation;
#end
and after that in bellow method just write this code if required..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
But now want to know is this accepted by apple app store or not ?
thanks
use this line for programmatically change orientation...
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
and also when you add this line at that time one warning appear and for remove this warning just add bellow code on you implementation file..
#interface UIDevice (MyPrivateNameThatAppleWouldNeverUseGoesHere)
- (void) setOrientation:(UIInterfaceOrientation)orientation;
#end
and after that in bellow method just write this code if required..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
// return NO;
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
i hope this help you..
:)
Add a class variable
Bool isInLandsCapeOrientation;
in viewDidLoad
set this flag to
isInLandsCapeOrientation = false;
Add the following function
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (!isInLandsCapeOrientation) {
return (UIInterfaceOrientationIsPortrait(interfaceOrientation));
}else {
return (UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
}
To changing orientation from portrait to landscape, let it happens on a button action
- (IBAction)changeOrientationButtonPressed:(UIButton *)sender
{
isInLandsCapeOrientation = true;
UIViewController *viewController = [[UIViewController alloc] init];
[self presentModalViewController:viewController animated:NO];
[self dismissModalViewControllerAnimated:NO];
}
This works fine for me.
To change Orientation portraid mode to landscap mode use this code
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
use this code for programmatically change orientation...
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
If you want to change the particular view only in landscape..then u can try the following in its viewDidLoad
float angle = M_PI / 2;
CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
[ [self.view] setTransform:transform];
The documentation describes the orientation property as being read-only, so if it works, I'm not sure you can rely on it working in the future (unless Apple does the smart thing and changes this; forcing orientation changes, regardless of how the user is currently holding their device, is such an obvious functional need).
As an alternative, the following code inserted in viewDidLoad will successfully (and somewhat curiously) force orientation (assuming you've already modified you shouldAutorotateToInterfaceOrientation ):
if (UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation]))
{
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];
}
Clearly, this does it if the user is currently holding their device in portrait orientation (and thus presumably your shouldAutorotateToInterfaceOrientation is set up for landscape only and this routine will shift it to landscape if the user's holding their device in portrait mode). You'd simply swap the UIDeviceOrientationIsPortrait with UIDeviceOrientationIsLandscape if your shouldAutorotateToInterfaceOirentation is set up for portrait only.
For some reason, removing the view from the main window and then re-adding it forces it to query shouldAutorotateToInterfaceOrientation and set the orientation correctly. Given that this isn't an Apple approved approach, maybe one should refrain from using it, but it works for me. Your mileage may vary. But this also refers to other techniques, too. Check
SO discussion
I am making my app compatible to iOS 5 but the app crashes on the code where i have used setContentViewController.
Here is my code.
[[ChoicesViewController sharedChoices] setCurrentValue:[[(UIButton *)sender titleLabel] text]];
[self.choicesPopoverController setContentViewController:[ChoicesViewController sharedChoices]];
CGFloat popoverHeight = 44.0f * [[[ChoicesViewController sharedChoices] choices] count];
[self.choicesPopoverController setPopoverContentSize:CGSizeMake(380.0f, MIN(400.0f, popoverHeight))];
if ([self.choicesPopoverController isPopoverVisible]) {
[self.choicesPopoverController dismissPopoverAnimated:YES];
} else {
[self.choicesPopoverController presentPopoverFromRect:[(UIButton *)sender frame]
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
here is what shared choices returns:
static ChoicesViewController *_sharedChoices = nil;
+(id)sharedChoices
{
if (!_sharedChoices)
{
_sharedChoices = [[[self class] alloc] init];
}
return _sharedChoices;
}
//When i comment the below code , the app wont crash in iOS 5 but the UIPopover is not shown too. And if I uncomment it it will crash in iOS 5.
-(UIPopoverController *)choicesPopoverController
{
if (!choicesPopoverController)
{
choicesPopoverController = [[UIPopoverController alloc] initWithContentViewController:self];
}
return choicesPopoverController;
}
You say you set breakpoints and found this line was the problem :
[self.choicesPopoverController setContentViewController:[ChoicesViewController sharedChoices]];
but there's a few things going on there. Where does it crash if you change that line to
id controller = self.choicesPopoverController;
id shared = [ChoicesViewController sharedChoices];
[controller setContentViewController:shared];
?
Finally i found the solution :
instead of writing
[self.choicesPopoverController setContentViewController:[ChoicesViewController sharedChoices]];
i did
choicesPopoverController = [[UIPopoverController alloc] initWithContentViewController:[ChoicesViewController sharedChoices]];
and commented out the this code
/*
-(UIPopoverController *)choicesPopoverController
{
if (!choicesPopoverController)
{
choicesPopoverController = [[UIPopoverController alloc] initWithContentViewController:self];
}
return choicesPopoverController;
}
*/
Now it doesnt crash in iOS 5.
I am using the following function to activate either the device camera or the image picker depending on the result of a UIActionSheet. if fromCamera=YES then it works on both iPhone and iPad. if fromCamera=NO then it works on iPhone and the image picker appears. But it crashes on the iPad with the following error: UIStatusBarStyleBlackTranslucent is not available on this device. I already know that the iPad can't display the UIStatusBarStyleBlackTranslucent statusBar, but how do I avoid this crash?
-(void)addPhotoFromCamera:(BOOL)fromCamera{
if(fromCamera){
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
}
else{
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
[self presentModalViewController:picker animated:YES];
}
If you set the picker to UIImagePickerControllerSourceTypePhotoLibrary on the iPad, then you must(!) present it in a popoverview, otherwise you get exceptions. I do it like this, to atleast control the size of the popover (the standard size is too small in my opinion):
-(void)openPhotoPicker
{
imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagePicker.navigationBar.opaque = true;
//put the image picker in its own container controller, to control its size
UIViewController *containerController = [[UIViewController alloc] init];
containerController.contentSizeForViewInPopover = rightPane.frame.size;
[containerController.view addSubview:imagePicker.view];
//then, put the container controller in the popover
popover = [[UIPopoverController alloc] initWithContentViewController:containerController];
//Actually, I would like to do the following, but iOS doesn't let me:
//[rightPane addSubview:imagePicker.view];
//So, put the popover over my rightPane. You might want to change the parameters to suit your needs.
[popover presentPopoverFromRect:CGRectMake(0.0, 0.0, 10.0,0.0)
inView:rightPane
permittedArrowDirections:UIPopoverArrowDirectionLeft
animated:YES];
//There seems to be some nasty bug because of the added layer (the container controller), so you need to call this now and each time the view rotates (see below)
[imagePicker.view setFrame:containerController.view.frame];
}
I also have the following, to counter a rotation bug:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if(imagePicker!=nil && rightPane.frame.size.width>0)
[imagePicker.view setFrame:imagePicker.view.superview.frame];
}
It ain't perfect, but it is ok for my testing purposes at the moment. I consider writing my own Imagepicker, because I don't like being forced to use the popoverview... but well, that's a different story.
I suspect the UIImagePicker is inheriting the translucent status bar from your Info.plist file or from the currently displayed view controller.
What happens if you make the app not have a translucent status bar?
I was having a similar issue, take a look at my answer here:
https://stackoverflow.com/questions/7677058/uiimagepickercontroller-crash-in-ipad-ios5/7839969#7839969
I just updated my app so that it's a universal app. In doing so I added support for UIPopoverController in a certain place. Now the app seems to be crashing on 3.1.3 iPhone/iTouch devices:
OS Version: iPhone OS 3.1.3 (7E18)
Report Version: 104
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x00000001, 0xe7ffdefe
Crashed Thread: 0
Dyld Error Message:
Symbol not found: _OBJC_CLASS_$_UIPopoverController
What I don't get is that I'm only trying to call UIPopoverController if the hardware is an iPad:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:btc];
CGSize popoverSize = { 300.0, 500.0 };
popover.delegate = self;
popover.popoverContentSize = popoverSize;
self.bmPopover = popover;
[popover release];
[self.bmPopover presentPopoverFromBarButtonItem:self.bmBarButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
} else {
[self presentModalViewController:nav animated:YES];
}
I do have an iVar and a property of type UIPopoverController declared but I wouldn't have expected this to cause an issue at runtime if I didn't actually try to call methods in the class.
What am I supposed to do to make sure that the system doesn't try to link with UIPopoverController at runtime when this isn't supported?
For Universal app you should not only check if this is ipad or does this class exists but you should link UIKit as Weak reference a not default ( strong ), to do this:
get to target info
select general
in linked libraries change UIKit required to UIKit weak
Have fun making universal apps :]
Even though this code would most likely never run on the iPhone, it will still be linked and thus you are receiving the error. Before instantiating, you need to check if the class exists. You can modify your code above to the following which will fix it.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
Class UIPopoverControllerClass = NSClassFromString(#"UIPopoverController");
if (UIPopoverControllerClass != nil) {
UIPopoverController *popover = [[UIPopoverControllerClass alloc] initWithContentViewController:btc];
CGSize popoverSize = { 300.0, 500.0 };
popover.delegate = self;
popover.popoverContentSize = popoverSize;
self.bmPopover = popover;
[popover release];
[self.bmPopover presentPopoverFromBarButtonItem:self.bmBarButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
} else {
[self presentModalViewController:nav animated:YES];
}
Also, you could weak link against the UIKit framework, which would also solve the problem. I prefer the code solution as it is more safe.
You need to stop the compiler errors by referencing the class from a string. This should be used in tandem with UI_USER_INTERFACE_IDIOM() and will compile on an SDK that does not know about UIPopoverController. Here is an example:
Class popClass = NSClassFromString(#"UIPopoverController");
if(popClass) {
id infoPop = [[popClass alloc] initWithContentViewController:popViewController];
[infoPop presentPopoverFromRect:CGRectMake(20, 70, 10, 10) inView:self.view permittedArrowDirections:4 animated:YES];
}
The easy answer:
In your Xcode project, Select the top level project icon. Do a Get Info, go to the BUILD panel. set Configurations to All Configurations, set Show: to ** All Settings**
Set the iOS Deployment Target to iOS 3.1.
Recompile. Your program is failing because the minimum OS is set too high, so the loader can't resolve the symbol UIPopoverController. The change I just walked you through makes that symbol weak: it will resolve to NULL, but at least your program will load.
Are you loading a NIB file that implicitly creates a UIPopoverController? That's another way to crash.
I found it useful to add this, so that I could use UI_USER_INTERFACE_IDIOM() on pre-3.2 systems.
#ifndef __IPHONE_3_2 // if iPhoneOS is 3.2 or greater then __IPHONE_3_2 will be defined
typedef enum {
UIUserInterfaceIdiomPhone, // iPhone and iPod touch style UI
UIUserInterfaceIdiomPad, // iPad style UI
} UIUserInterfaceIdiom;
#define UI_USER_INTERFACE_IDIOM() UIUserInterfaceIdiomPhone
#endif // ifndef __IPHONE_3_2