UIPopoverController for iphone not working? - iphone

I need to use a UIPopOverController for my iPhone app ,i searched stackoverflow someone said UIPopoverController does not run on iphone iphone device WHY?.when i run on iphone device
i got this error reason: '-[UIPopoverController initWithContentViewController:]
called when not running under UIUserInterfaceIdiomPad.'
-(void)btnSetRemainderTapped:(UIButton *)button
{
setReminderView =[[SetRemainderView alloc]initWithNibName:#"SetRemainderView" bundle:[NSBundle mainBundle]];
setReminderView.contentSizeForViewInPopover = CGSizeMake(setReminderView.view.frame.size.width, setReminderView.view.frame.size.height);
setReminderView.delegate = self;
popOverController = [[UIPopoverController alloc]
initWithContentViewController:setReminderView] ;
CGRect rect = CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/2, 1, 1);
[popOverController presentPopoverFromRect:rect
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
can any one help me?

You CAN use popoverController in iPhone apps.
1. Create a category
// UIPopoverController+iPhone.h file
#interface UIPopoverController (iPhone)
+ (BOOL)_popoversDisabled;
#end
// UIPopoverController+iPhone.m file
#implementation UIPopoverController (iPhone)
+ (BOOL)_popoversDisabled {
return NO;
}
#end
2. Import it to your class and use popover in iPhone as usual.
But remember that this is private method and Apple can reject your app. But I know people who use this normally and Apple published their apps.

Edit: As stated by Soberman, since iOS 8 it is possible to present popovers on iPhone using public APIs, so this answer is probably not relevant anymore.
As stated in Apple's documentation on UIPopoverController:
Popover controllers are for use exclusively on iPad devices.
So there is no way to use this class in iPhone application unfortunately. But there are a couple of custom third-party implementations of the functionality provided by UIPopoverController which add iPhone support and more. See https://github.com/50pixels/FPPopover for example.
Edit: There also is another highly customizable popover implementation for both iPhone/iPad worth checking out: https://github.com/nicolaschengdev/WYPopoverController.

Since iOS8 we are now able to create popovers, that will be the same on iPhone, as on iPad, which would be especially awesome for those who make universal apps, thus no need to make separate views or code.
You can get the class as well as demo project here: https://github.com/soberman/ARSPopover
All you need to do is subclass UIViewController, conform to the UIPopoverPresentationControllerDelegate protocol and set desired modalPresentationStyle along with the delegate value:
// This is your CustomPopoverController.m
#interface CustomPopoverController () <UIPopoverPresentationControllerDelegate>
#end
#implementation CustomPopoverController.m
- (instancetype)init {
if (self = [super init]) {
self.modalPresentationStyle = UIModalPresentationPopover;
self.popoverPresentationController.delegate = self;
}
return self;
}
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone; //You have to specify this particular value in order to make it work on iPhone.
}
Afterwards, instantiate your newly created subclass in the method from which you want to show it and assign two more values to sourceView and sourceRect. It looks like this:
CustomPopoverController *popoverController = [[CustomPopoverController alloc] init];
popoverController.popoverPresentationController.sourceView = sourceView; //The view containing the anchor rectangle for the popover.
popoverController.popoverPresentationController.sourceRect = CGRectMake(384, 40, 0, 0); //The rectangle in the specified view in which to anchor the popover.
[self presentViewController:popoverController animated:YES completion:nil];
And there you have it, nice, neat blurred popover.

So #Sobermans answer didn't really solve the issue from start to finish for me so I want to detail how I got it done using the docs. That being said I do like the idea of using your own presentation controller subclass to manage all of the customisation you want to exhibit.
1. Create your controller to present
The first step is instantiating the controller you want to present:
let vc: UIViewController = ...
vc.modalPresentationStyle = .Popover
vc.preferredContentSize = CGSize(width: CGRectGetWidth(view.bounds)/2, height: 100)
Now we have a controller with the popover presentation style and an arbitrary content size.
2. Implement adaptivePresentationStyleForPresentationController
By default UIPopoverPresentationController will present on full screen on iPhone so to prevent this behaviour you need to force the adaptive presentation style to none.
First we set the delegate of the popover presentation controller
vc.popoverPresentationController.delegate = self;
Then we implement UIPopoverPresentationControllerDelegate
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None;
}
3. Present and configure popup
First we need to call presentViewController and only after that can we configure the popover:
presentViewController(vc, animated:true, completion:nil)
if let popover = vc.popoverPresentationController {
popover.permittedArrowDirections = .Right | .Left
popover.sourceView = button
popover.sourceRect = button.bounds
}

Use a custom popover controller, such as:
https://github.com/sammcewan/WYPopoverController
(this seems to be the best supported one that I have found).

I ended up creating my custom tooltip/popover class.
Can be initalised with any content view and dynamically adjusts it's frame.
Hope it helps.
https://github.com/akeara/AKETooltip

If you want to do it in Swift, I believe the code is the following:
extension UIPopoverController {
class var _popoversDisabled : Bool {
get { return false }
}
}
Edit: It is working in Xcode 6 beta 4 on iPhone with iOs7.1

This is a really interesting (and depressing) thread to read. I can't believe Apple prevents popup dialogs on iPhones, with absolutely no justification.
And, it's true, on iOS 8, if you try to work around this limitation, it'll make your popups appear as a full-screen modal dialog.
The following excellent webpage describes "How Apple Cheats" to let its own iBooks and iTunes apps break its own rules, and allow popups - but just from within their own iPhone apps.
HowAppleCheats
Have a read (warning: it'll make you hate Apple & XCode even more..)
Want to get around the "UIPopoverController called when not running under UIUserInterfaceIdiomPad" error on iOS 8 ?
Simple.
Just go into your .plist file, and change the Bundle ID to "com.apple.itunesu" to make XCode think that your app is actually iTunes.
Then your popup will work fine.
(Sigh.)
The alternative way of doing this is to directly add your UIViewController to your screen.
In this example, I wanted a "helper screen" to appear on top of my iPhone screen. It's a UIViewController, it is stored in it's own .xib file, and it has a few lines to add a pretty border:
- (void)viewDidLoad {
[super viewDidLoad];
// Give our popup a pretty curved border
self.view.layer.borderColor = [[UIColor blueColor] CGColor];
self.view.layer.borderWidth = 1.0;
self.view.layer.cornerRadius = 8;
}
To display it, I simply create an instance of this UIViewController, add it to my screen, then center it:
-(void)showHelperScreen
{
if (self.helperScreen == nil)
{
// Add the popup UIViewController to our screen
self.helperScreen = [[HelperViewController alloc] init];
[self.view addSubview:self.helperScreen.view];
}
// Center the popup in the middle of the screen
CGSize screenSize = [[UIScreen mainScreen] applicationFrame].size;
self.helperScreen.view.center = CGPointMake(screenSize.width/2, screenSize.height/2);
}
Of course, I also needed to add some code to make the popup disappear when the user taps outside of it, but this does at least show that you can (safely) display popups on an iPhone, even if your app isn't specifically called iTunes or iBook.
Voila.
Hope this helps, and if anyone needs me, I'll be back in my safe, happy place (Visual Studio, in other words).

Related

Moving status bar in iOS 7

The problem I'm facing is this:
I want to implement an iOS 7 app with nice design and left/right menu, which appears after the main view animate itself to the right/left. I'm doing this with [UIView animateWithDuration...] code, but that's not really important. What I want to achieve is the same effect the Mailbox iOS 7 app has: to move the status bar away (to the right/left) with the main view
Image for better explanation:
What I only found is this article about the issue, with some working code using Private APIs, which I'd like not to use, since I want my app to be accepted on the App Store.
I'd like to achieve the same effect ('legally'). Does anybody knows how to?
Thanks!
The gist of it is to use this method introduced in iOS 7:
https://developer.apple.com/documentation/uikit/uiscreen/1617814-snapshotview:
With that you get a UIView containing a screenshot that includes the status bar. Once you have that, it's just a matter of hiding your current view then pushing the screenshot view around.
I posted a proof of concept here:
https://github.com/simonholroyd/StatusBarTest
NOTE I haven't submitted code that does this through the Apple review process, but this is not a private API method.
So, after the initial push by Mr. Simon Holroyd and some searching, I've found the solution of how to achieve this "effect" functionality. This is the code:
statusbarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 20)];
EDIT: mister pcholberg correctly pointed out that the former code did not work on the actual device, only on the iOS Simulator, so I've edited it by his recommendation
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0"))
{
UIView *screenShot = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
[statusbarView addSubview:screenShot];
[statusbarView setClipsToBounds:YES];
[self.view addSubview:statusbarView];
[self setPrefersStatusBarHidden:YES];
[self prefersStatusBarHidden];
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
}
...
- (BOOL)prefersStatusBarHidden
{
return prefersStatusBarHidden;
}
...
So the first part creates context, uses the method Simon mentioned, draws the view with the statusbar, and saves that as an UIImage
The second part adds the snapshot UIView to my viewController's UIView
And the third part sets my bool for statusbar to YES (for easier use in the method above), and calls methods to redraw it
This then sets the UIView as not-functional statusbar at its place and hides the original one, so there is no double-rendering. Then I can use this view in my [UIView animateWithDuration... method
And when I return, I use this code in the completion handler of the animation block:
[statusbarView removeFromSuperview];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0"))
{
[self setPrefersStatusBarHidden:NO];
[self prefersStatusBarHidden];
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
}
And voilá! This works as the described effect in my question.
Hope this helps somebody!
I use this method to move statuebar with slider view,in a application there are two window,one normal window,other statuBarWindow,i get statuBarView which superView is statuBarWindows ,and move it with slider view.
- (UIView *)statuBarView
{
NSString *key = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char []){0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x61, 0x72} length:9] encoding:NSASCIIStringEncoding];
id object = [UIApplication sharedApplication];
UIView *statusBar = nil;
if ([object respondsToSelector:NSSelectorFromString(key)]) {
statusBar = [object valueForKey:key];
}
return statusBar;
}
I just created BSPanViewController which makes it extremely easy to achieve this effect. The control and instructions on how to use it can be found on GitHub.
The implementation is the same as the one explained by Simon Holroyd.
In my experience, App Store reviewers generally don't care about private API's use, especially this simple and harmless.
For the task you can get a pointer to application's status bar view through several methods, which you can find in iOS complete headers like https://github.com/nst/iOS-Runtime-Headers

SetUserTrackingMode not working

I am programming a map app on iPhone and want the map to rotate as the user changes his direction. I have read most of the posts on stackoverflow. Most of them suggest the use of setUserTrackingMode with MKUserTrackingModeFollowWithHeading if we are working with iOS 5 or later. This does not seem to work with me for some reason. Following is my code:
-(IBAction)getLocation //This is a button
{
mapView.showsUserLocation=YES; //mapView is the instance of MKMapView
[mapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading animated:YES];
}
This only shows the user location but if I move the phone, it doesn't rotate.
One more thing is, I downloaded a project from internet, and I included this line. It worked there only for the first time. I have no idea why this is happening.
Any suggestions?
You need to wait for the 'MapView' finish loading...
follow:
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView {
mapView.userTrackingMode = MKUserTrackingModeFollow;
}
follow & heading:
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView {
mapView.userTrackingMode = MKUserTrackingModeFollowWithHeading;
}
The easier way to do this is to include an MKUserTrackingBarButtonItem instead of creating your own button. It acts exactly the same as the button in the iOS 5 Maps app and is easy to set up.
Here's how to use it:
// You should have an outlet to your map view called mapView
MKUserTrackingBarButtonItem *userTrackingButton;
userTrackingButton = [[MKUserTrackingBarButtonItem alloc] initWithMapView:self.mapView];
// You need an outlet to your toolbar too
[self.toolbar setItems:[NSArray arrayWithObject:userTrackingButton]];
Change "Animated" to "animated" and try again

UIPickerView on Separate Page

I am working on an iPhone app. Initially, I had my pickerview in the same screen so this was just a one page app. After skinning it i realized that i want the pickerview on it's own separate page. So i did that. However, my pickerview originally would update uilabels and other objects on that same page. How can I have my pickerview access those objects from it's new view?
- (IBAction)ShowPickerAction:(id)sender {
if (self.theView == nil) {
theView = [containerView initWithNibName:#"containerView" bundle:nil];
theView.parentView = self;
}
self.theView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:self.theView animated:YES];
}
That is the method I am using to call my new view. But line 3 of the above code gives me the error "No known class name for selector initWithNibName:bundle". I think that error is related to something that i did wrong in my header file. My new class is called containerView. So i did this in my header:
#interface ViewController : UIViewController {
containerView *theView;
But that gives me the error "Unknown type name containerView" even though i do have a class named containerView!!
Look into uiappdelegate protocol or try passing values to through a static function to the previous page.
Use a delegate to pass information back and forth to the view object that instantiatrd the picker view. You want to keep your code coupling as loose as possible, especially if you might like to drop it into your next project. Using a delegate and/or blocks are some of the best ways.

Are there examples of how to use UIPopoverController on iOS?

I've seen some demos where UIPopoverController is used and would like to use it in my apps.
So does anyone have any good tutorials that you could link me?
Is it possible to use it in relation to UISegmentedControl where different popover windows are summoned when different segments are selected as a type of a switch view function?
Here are some tutorials:
iPad for iPhone Developers 101: UIPopoverController Tutorial
[iPad App Video] iPad App Tutorial – UIPopoverController
Segmented Popover:
Using a UISegmentedControl in the footer of UIPopoverController
Buttons inside of a Pop Over Controller on the iPad, no UISegmentUISegmentedControl used but looks like one
For popover in iPad we can use ActionSheetStringPicker, and for implementing it in your project you need to import ActionSheetStringPicker into your controller.
like - #imaport "ActionSheetStringPicker.h"
After importing this you have to create an array which has only string type value.
e.g.
NSArray *sourceArray=[[NSArray alloc]initWithObjects:#"Take Photo",#"Choose Photo", nil];
And last you have to implement below method.
[ActionSheetStringPicker showPickerWithTitle:#"Any title"
rows:sourceArray
initialSelection:yourInitialSelectionIntValue
doneBlock:^(ActionSheetStringPicker *picker, NSInteger selectedIndex, id selectedValue) {
NSLog(#" Index : %ld, value : %#",
(long)selectedIndex, selectedValue);
if ([selectedValue isEqualToString:#"Choose Photo"]) {
// open photo lib
[self youerMethdeOpenPhotoLib];
}
else
{
// open Camera
[self yourMethodOpenCamera];
}
}
cancelBlock:^(ActionSheetStringPicker *picker) {
NSLog(#"Select photo Cancel");
}origin:YourTapButtonObject];

Force scope bar below UISearchBar

I have a UISearchBar and UISearchDisplayController, everything works great but my scope selector displays beside the text field instead of below it. I know that this is the expected action when the device is in landscape, but since I have the UISearchBar in the master view of a UISplitViewController it ends up looking like this:
Is there any way to force the scope bar to display below the text field in all interface orientations (I know that this works nicely in Mail.app on the iPad, so its possibly, but who knows if Apple decided to hide the option to do so)
This is not part of the public API. Hopefully it will be added in the future. It can be accomplished using Private APIs, looking through a UIKit class dump will help you there.
Good luck!
edit: Note that using private APIs can get you rejected from the app store, as most people already know.
I used the UISearchDisplayDelegate to show and hide the scope bar.
-(void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
[controller.searchBar setShowsScopeBar:YES];
}
-(void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[controller.searchBar setShowsScopeBar:NO];
}
Don't use private APIs. Instead, follow Apple's recommendation from IOS User Interface Guidelines:
When a search bar is present, a scope bar can appear near it. The
scope bar displays below the search bar, regardless of orientation,
unless you use a search display controller in your code (for more
information on the way this works, see UISearchDisplayController Class
Reference). When you use a search display controller, the scope bar is
displayed within the search bar to the right of the search field when
the device is in landscape orientation (in portrait orientation, it’s
below the search bar).
This worked for me using storyboards.
Why not just set up your search bar programattically? Here's what I'm using on my current project. It hides the search bar until the user scrolls up, and the scope bar is loaded below the text field when they start typing items. From there just filter as usual:
- (void)setupSearchBar {
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.searchBar.showsScopeBar = YES;
self.searchBar.scopeButtonTitles = [NSArray arrayWithObjects:#"Users", #"Groups", nil];
self.tableView.tableHeaderView = self.searchBar;
CGPoint offset = CGPointMake(0, self.searchBar.frame.size.height);
self.tableView.contentOffset = offset;
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar
contentsController:self];
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
self.searchController.delegate = self;
}
call [self setupSearchBar]; in viewDidLoad you're off to the races.