Is it possible to pop up an UIViewController (xib file) like UIPopOverControl in iPad ?
I have a separate NIB file which is linked to an UIViewController. I want to popup that NIB file along with the button pressed with a customised size (200,200).
Is this possible?
I am trying to get something like this on the iPhone - http://www.freeimagehosting.net/c219p
You can also use one of these custom made clases to show a popup:
https://github.com/sonsongithub/PopupView
https://github.com/werner77/WEPopover
https://github.com/50pixels/FPPopover
Example with FPPopover:
//the view controller you want to present as popover
YourViewController *controller = [[YourViewController alloc] init];
//our popover
FPPopoverController *popover = [[FPPopoverController alloc] initWithViewController:controller];
//the popover will be presented from the okButton view
[popover presentPopoverFromView:okButton];
//release if you arent using ARC
[controller release];
yes it is. load Your pOpOver controller lazily at the point when it is needed. add its view as a subview (you could animate the addition). make its frame size what You need and add the image You have shown as a background subview of the pOpOver controller along with other controls You want in the pop up.
good luck
UPDATE:
alright, ii will show You how ii do this in my app Lucid Reality Check (deployment target iOS4.3).
one can use a UIPopoverController to present another controllers view. what ii do first is to make sure ii always know the current orientation of the device, so ii can reposition the popup on rotation (maybe this works by itself on iOS6?). so in my base controller (from where ii want to show a popup) ii have an instance variable like this:
UIInterfaceOrientation toOrientation;
and also:
UIPopoverController *popover;
UIButton *popover_from_button;
BOOL representPopover;
popover will be reused for all popups, and popover_from_button will hold the button from which the popup is initiated.
then the next code comes into the base controller:
- (void)popoverWillRotate {
if ([popover isPopoverVisible]) {
[self dismissPopover];
representPopover = YES;
}
}
- (void)popoverDidRotate {
if (popover && representPopover) {
representPopover = NO;
[self representPopover];
}
}
these two methods have to be called every time the device is rotated, like this:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
//DLOG(#"willRotateTo %i", toInterfaceOrientation);
toOrientation = toInterfaceOrientation;
if ([Kriya isPad ]) {
[self popoverWillRotate];
}
}
as one can see, first the orientation is captured then popoverWillRotate is called. this would hide the popover during the orientation animation. and after rotating, the popover must be redisplayed like this:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
//DLOG(#"didRotateFrom %i", fromInterfaceOrientation);
//[self layout:toOrientation]; //do some layout if You need
if ([Kriya isPad]) {
[self popoverDidRotate];
}
}
- (void)layout:(UIInterfaceOrientation)toInterfaceOrientation {
//one can do view layout here, and call other controllers to do their layout too
}
now that the orientation changes are worked out, the code for presenting the popover arrives here:
#pragma mark Popovers
- (void)presentPopoverWith:(id)controller fromButton:(UIButton*)button {
if (popover)
[popover release];
if (popover_from_button)
[popover_from_button release];
popover_from_button = [button retain];
popover = [[UIPopoverController alloc] initWithContentViewController:controller];
[popover setDelegate:self];
[self representPopover];
}
- (void)representPopover{
if (popover) {
UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny;
UIViewController *vc = (UIViewController*)[popover contentViewController];
CGSize contentSize = [vc contentSizeForViewInPopover];
if (contentSize.width > 0 && contentSize.height > 0) {
[popover setPopoverContentSize:contentSize animated:NO];
}
//DLOG(#"representPopover rect:%#", [Kriya printRect:popover_from_button.frame]);
[popover presentPopoverFromRect:CGRectOffset(popover_from_button.frame, 0, popover_from_button.frame.size.height + 7.0) inView:self.view permittedArrowDirections:arrowDirection animated:YES];
}
}
- (void)dismissPopover {
if (popover) {
[popover dismissPopoverAnimated:YES];
}
}
finally, if one wants to be notified when the popover is dismissed, the base controller must implement a delegate method:
#pragma mark UIPopoverControllerDelegate
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
//do something important here like drink some water
}
and don't forget to make the base controller a UIPopoverControllerDelegate in its header.
a use case for this way of doing popups would then look like this:
- (void)takeImage {
UIImagePickerController *picker = [[[UIImagePickerController alloc] init] autorelease];
[picker setDelegate:self];
[picker setAllowsEditing:NO];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[picker setSourceType:UIImagePickerControllerSourceTypeCamera];
if ([Kriya isPad]) {
[self presentPopoverWith:picker fromButton:backgroundImageButton];
} else {
//modals on iPhone/iPod
//DLOG(#"takeImage addSubview picker");
[self presentModalViewController:picker animated:YES];
}
} else {
//DLOG(#"no camera");
}
}
this would use an image picker as the content for the popup, but one can use any controller with a valid view. so just do this:
[self presentPopoverWith:popupsContentController fromButton:tappedButton];
one should not have any missing information, :), the method [Kriya isPad] is just this:
+ (BOOL)isPad {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200
// iPad capable OS
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
//this is an iPad
return YES;
}else {
//this is an iPod/iPhone
return NO;
}
#else
//can not pissible be iPad
return NO;
#endif
}
ENJOY!
Related
My application is set in info.plist to support only portrait mode.
However, the UIImagePickerController, rotates when the user rotates the screen to landscape.
Since in io6 the method shouldAutoRotate is not being called, I tried to extend it like this:
#interface NonRotatingUIImagePickerController : UIImagePickerController
#end
#implementation NonRotatingUIImagePickerController
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationMaskPortrait;
}
#end
But it doesn't help. Any idea why?
And in the log I see the above methods being called. The UIImagePickerController at first is displayed in portrait and when the user rotates - it rotates as well instead of staying portrait.
I set the image picker in the view like this:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (!self.imagePickerController) {
self.imagePickerController = [[NonRotatingUIImagePickerController alloc] init];
self.imagePickerController.delegate = self;
}
return self;
}
- (void)viewDidAppear:(BOOL)animated{
self.imagePickerController.showsCameraControls = NO;
CGRect imagePickerControllerFrame = CGRectMake(0, topBar.frame.size.height, self.view.frame.size.width, self.view.frame.size.height - topBar.frame.size.height - bottomBar.frame.size.height);
self.imagePickerController.view.frame = imagePickerControllerFrame;
self.imagePickerController.allowsEditing = YES;
self.imagePickerController.view.clipsToBounds = YES;
self.imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera
[self.view.window addSubview:self.imagePickerController.view];
}
self.imagePickerController.view.frame = imagePickerControllerFrame;
// ...
[self.view.window addSubview:self.imagePickerController.view];
Well, that's all totally illegitimate. Apple makes this very clear in the docs:
This class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified
There is only one correct way to use an image picker controller that uses UIImagePickerControllerSourceTypeCamera - as a fullscreen presented view controller:
BOOL ok = [UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeCamera];
if (!ok) {
NSLog(#"no camera");
return;
}
NSArray* arr = [UIImagePickerController availableMediaTypesForSourceType:
UIImagePickerControllerSourceTypeCamera];
if ([arr indexOfObject:(NSString*)kUTTypeImage] == NSNotFound) {
NSLog(#"no stills");
return;
}
UIImagePickerController* picker = [UIImagePickerController new];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.mediaTypes = #[(NSString*)kUTTypeImage];
picker.delegate = self;
[self presentViewController:picker animated:YES completion:nil];
If you want to present a live picture-taking interface inside your own interface, use AVFoundation and the camera capture API that it gives you.
Downloadable working example here:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch30p816cameraCaptureWithAVFoundation/p683cameraCaptureWithAVFoundation/ViewController.m
Perhaps you'll consider this answer unhelpful; but I'll just paste a snippet from Apple's documentation:
Important: The UIImagePickerController class supports portrait mode only. This class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified, with one exception. You can assign a custom view to the cameraOverlayView property and use that view to present additional information or manage the interactions between the camera interface and your code.
UIImagePickerController Doc Link
Sorry to be a kill-joy. You should look for a replacement class. Quickie search shows there are a bunch.
I want to make a custom animation to pop my navigation controller. I only want to animate the view, not the navigationBar. With this code I animate both, the view and the navigationBar. How can I only animate the view??
CATransition* transition = [CATransition animation];
transition.duration = 0.3;
transition.type = kCATransitionFade;
transition.subtype = kCATransitionFromTop;
[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController popViewControllerAnimated:NO];
this code is fired when a custom back button added inside the navigationcontroller bar is pressed.
Here is a code that does custom animation both for back button and when you call popRootViewController: method.
It's a class that extends UINavigationViewController which by itself contradicts Apple's docs also it assigns private variable using KVO, which might stop working as soon as engineers change UINavigationController class so use it a t your own risk.
#import "MyNavigationController.h"
#interface MyNavigationController () <UINavigationBarDelegate> {
// Flag that we will use to avoid collisions between navgiation bar
// when we call popViewControllerAnimated: method directly
BOOL _isPopping;
}
- (UIViewController *)myPopViewControllerAniamted:(BOOL)animated;
#end
#implementation MyNavigationController
- (id)init
{
self = [super init];
if (!self) return nil;
// We can't intercept delegation of the original navigation bar,
// we have to replace it with our own, by assigning new instance to
// the private _navigationBar vairable
UINavigationBar *navigationBar = [[UINavigationBar alloc] init];
navigationBar.delegate = self;
[self setValue:navigationBar forKey:#"_navigationBar"];
return self;
}
// This is the delegate method called when you're about to pop navigation item
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
// If we're in the process of popping items we don't want to reenter
if (!_isPopping) {
[self myPopViewControllerAniamted:YES];
}
return YES;
}
// Similarly we have to override popToRootViewControllerAnimated:
// The only difference would be that we use not previous view as a
// target for the transfition, but the very first view
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
return [self myPopViewControllerAniamted:animated];
}
// Our custom popping method
- (UIViewController *)myPopViewControllerAniamted:(BOOL)animated
{
_isPopping = YES;
// If we got here, we have at least two view controllers in the stack
UIViewController *currentViewController = self.topViewController;
if (animated && self.viewControllers.count > 1) {
UIView *currentView = currentViewController.view;
UIViewController *previousViewController = [self.viewControllers objectAtIndex:self.viewControllers.count - 2];
UIView *previousView = previousViewController.view;
previousView.alpha = 0.0;
[currentView.superview insertSubview:previousView belowSubview:currentView];
// I use UIView just for the sake of the simplicity of this example
// In case of core animation you will have to deal with delegates
// to trigger view controller popping when animation finishes
[UIView animateWithDuration:0.33 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
currentView.alpha = 0.0;
previousView.alpha = 1.0;
} completion:^(BOOL finished) {
[super popViewControllerAnimated:NO];
_isPopping = NO;
}];
} else {
[super popViewControllerAnimated:NO];
_isPopping = NO;
}
return currentViewController;
}
#end
Once again, it was done purely as exercise of what is possible, I would highly recommend reading UIViewController guide, probably Container View Controller can satisfy you needs as a designated way of customizing view controller behaviour.
Hope it helps!
I have UIViewControllers A and B, they are allocated in AppDelegate. I need to apply transition to them. How to transit them without reallocating and replacing UIViews?
This code calls from my UIBarButtonItem in UINavigationController:
[UIView transitionFromView:self.view //UIViewController A
toView:appDelegate.secondViewController.view //UIViewController B
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromLeft
This method replaces UIViews in my UIViewControllers, and I can transit them back, or just don't know how to do that. Can you tell me how to do this?
If you're in iOS 5 world and want to jump between various view controllers, you might want to pursue View Controller Containment. Or check out WWDC 2011 session 102.
View controller containment basically assumes that you have some parent view controller which is governing the navigation between multiple child controllers. In your case, the parent view would be one with the navigation bar with the button on it.
Update:
If you pursue containment, you could create a parent view controller that has a nav bar with the button on it. When you load that view, you can add the first child view. Thus viewDidLoad might look like:
- (void)viewDidLoad
{
[super viewDidLoad];
// this is my model, where I store data used by my view controllers
_model = [[MyModel alloc] init];
// let's create our first view controller
OneViewController *controller = [[OneViewController alloc] initWithNibName:#"OneViewController" bundle:nil];
// pass it our model (obviously, `model` is a property that I've set up in my child controllers)
controller.model = _model;
// let's put the new child in our container and add it to the view
[self addChildViewController:controller];
[self configureChild:controller];
[self.view addSubview:controller.view];
[controller didMoveToParentViewController:self];
// update our navigation bar title and the label of the button accordingly
[self updateTitles:controller];
}
The configureChild just does final configuration. As a matter of convenience, I frequently will have a UIView that I've set up in IB (in this case, called childView) which I use for setting up the frame, which gets me out of the world of manually creating frames, but you can do it any way you want:
- (void)configureChild:(UIViewController *)controller
{
// configure it to be the right size (I create a childView in IB that is convenient for setting the size of the views of our child view controllers)
controller.view.frame = self.childView.frame;
}
This is the action if you touch the button in the navigation bar. If you're in the first controller, set up the second controller. If you're in the second controller, set up the first one:
- (IBAction)barButtonTouchUpInside:(id)sender
{
UIViewController *currentChildController = [self.childViewControllers objectAtIndex:0];
if ([currentChildController isKindOfClass:[OneViewController class]])
{
TwoViewController *newChildController = [[TwoViewController alloc] initWithNibName:#"TwoViewController" bundle:nil];
newChildController.model = _model;
[self transitionFrom:currentChildController To:newChildController];
}
else if ([currentChildController isKindOfClass:[TwoViewController class]])
{
OneViewController *newChildController = [[OneViewController alloc] initWithNibName:#"OneViewController" bundle:nil];
newChildController.model = _model;
[self transitionFrom:currentChildController To:newChildController];
}
else
NSAssert(FALSE, #"Unknown controller type");
}
This does the basic transition (including the various containment related calls):
- (void)transitionFrom:(UIViewController *)oldController To:(UIViewController *)newController
{
[self addChildViewController:newController];
[self configureChild:newController];
[self transitionFromViewController:oldController
toViewController:newController
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
[self updateTitles:newController];
}
completion:^(BOOL finished){
[oldController willMoveToParentViewController:nil];
[oldController removeFromParentViewController];
[newController didMoveToParentViewController:self];
}];
}
This method just sets up the title in the nav bar in our parent view controller based upon which child is selected. It also sets up the button to reference the other controller.
- (void)updateTitles:(UIViewController *)controller
{
if ([controller isKindOfClass:[OneViewController class]])
{
self.navigationItemTitle.title = #"First View Controller"; // current title
self.barButton.title = #"Two"; // title of button to take me to next controller
}
else if ([controller isKindOfClass:[TwoViewController class]])
{
self.navigationItemTitle.title = #"Second View Controller"; // current title
self.barButton.title = #"One"; // title of button to take me to next controller
}
else
NSAssert(FALSE, #"Unknown controller type");
}
This all assumes you are going to create and destroy controllers as you jump between them. I generally do this, but use a model object to store my data so I keep whatever data I want.
You said you don't want to do this "without reallocating and replacing UIViews": If so, you can also change the above code to create both child view controllers up-front and change the transition to be simply jump between them:
- (void)viewDidLoad
{
[super viewDidLoad];
// this is my model, where I store data used by my view controllers
_model = [[MyModel alloc] init];
// let's create our first view controller
_controller0 = [[OneViewController alloc] initWithNibName:#"OneViewController" bundle:nil];
_controller0.model = _model;
[self addChildViewController:_controller0];
[self configureChild:_controller0];
[_controller0 didMoveToParentViewController:self];
// let's create our second view controller
_controller1 = [[OneViewController alloc] initWithNibName:#"OneViewController" bundle:nil];
_controller1.model = _model;
[self addChildViewController:_controller1];
[self configureChild:_controller1];
[_controller1 didMoveToParentViewController:self];
// let's add the first view and update our navigation bar title and the label of the button accordingly
_currentChildController = _controller0;
[self.view addSubview:_currentChildController.view];
[self updateTitles:_currentChildController];
}
- (void)transitionFrom:(UIViewController *)oldController To:(UIViewController *)newController
{
[self transitionFromViewController:oldController
toViewController:newController
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
[self updateTitles:newController];
}
completion:^(BOOL finished){
_currentChildController = newController;
}];
}
- (IBAction)barButtonTouchUpInside:(id)sender
{
UIViewController *newChildController;
if ([_currentChildController isKindOfClass:[OneViewController class]])
{
newChildController = _controller1;
}
else if ([_currentChildController isKindOfClass:[TwoViewController class]])
{
newChildController = _controller0;
}
else
NSAssert(FALSE, #"Unknown controller type");
[self transitionFrom:_currentChildController To:newChildController];
}
I've seen it both ways, so you can do whatever works for you.
please see here. You basically want to implement UIViewController containment which is a new feature in iOS5. The link provided above provides some code and a link to a github project.
Good luck
t
I found solution for my problem. This code works on iOS 4.x
[UIView beginAnimations:#"transition" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown
forView:self.navigationController.view
cache:NO];
[self.navigationController
pushViewController:self.alternateView animated:NO];
[UIView commitAnimations];
try
UIViewController* controller1;
UIViewController* controller2;
[controller1 transitionFromViewController:controller1 toViewController:controller2 duration:0.5f options:0 animations:nil completion:nil];
or
if on top of navigationtroller - controller1 then
UINavigationController* nav;
[nav pushViewController:controller2 animated:YES];
I've got a QuickLook view that I view some of my app's documents in. It works fine, but I'm having my share of trouble closing the view again. How do I create a touch event / gesture recognizer for which I can detect when the user wants to close the view?
I tried the following, but no events seem to trigger when I test it.
/------------------------ [ TouchPreviewController.h ]---------------------------
#import <Quicklook/Quicklook.h>
#interface TouchPreviewController : QLPreviewController
#end
//------------------------ [ TouchPreviewController.m ]---------------------------
#import "TouchPreviewController.h"
#implementation TouchPreviewController
- (id)init:(CGRect)aRect {
if (self = [super init]) {
// We set it here directly for convenience
// As by default for a UIImageView it is set to NO
UITapGestureRecognizer *singleFingerDTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSingleDoubleTap:)];
singleFingerDTap.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:singleFingerDTap];
[self.view setUserInteractionEnabled:YES];
[self.view setMultipleTouchEnabled:YES];
//[singleFingerDTap release];
}
return self;
}
- (IBAction)handleSingleDoubleTap:(UIGestureRecognizer *) sender {
CGPoint tapPoint = [sender locationInView:sender.view.superview];
[UIView beginAnimations:nil context:NULL];
sender.view.center = tapPoint;
[UIView commitAnimations];
NSLog(#"TouchPreviewController tap!" ) ;
}
// I also tried adding this
- (BOOL)gestureRecognizer:(UIGestureRecognizer *) gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*) otherGestureRecognizer {
return YES;
}
#end
Edit: For clarification, this is how I instantiate the controller:
documents = [[NSArray alloc] initWithObjects: filename , nil ] ;
preview = [[TouchPreviewController alloc] init];
preview.dataSource = self;
preview.delegate = self;
//set the frame from the parent view
CGFloat w= backgroundViewHolder.frame.size.width;
CGFloat h= backgroundViewHolder.frame.size.height;
preview.view.frame = CGRectMake(0, 0,w, h);
//refresh the preview controller
[preview reloadData];
[[preview view] setNeedsLayout];
[[preview view] setNeedsDisplay];
[preview refreshCurrentPreviewItem];
//add it
[quickLookView addSubview:preview.view];
Also, I've defined the callback methods as this:
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller
{
return [documents count];
}
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index
{
return [NSURL fileURLWithPath:[documents objectAtIndex:index]];
}
Edit2: One thing i noticed. If I try making swiping gestures, I get the following message. This could shed some light on what is wrong/missing?
Ignoring call to [UIPanGestureRecognizer setTranslation:inView:] since
gesture recognizer is not active.
I think your example code is incomplete. It isn't clear how you are instantiating the TouchPreviewController (storyboard, nib file or loadView.)
I have never used the class so I could be way out in left field.
If you've already instantiated a UITapGestureRecognizer in the parent viewController, it is absorbing the tap events and they aren't passed on to your TouchPreviewController.
I would implement the view hierarchy differently by attaching the UITapGestureRecognizer to the parent viewController and handle presentation and unloading of the QLPreviewController there.
I think you might not have to subclass QLPreviewController by instantiating the viewController from a nib file.
When your parent viewController's UITapGestureRecognizer got an event you would either push the QLPreviewController on the navigation stack or pop it off the navigation stack when done.
Hope this is of some help.
I have a strange issue on ios 4.3.I have one of my screen in landscape mode, a button click presents a popover.My popover has a search bar.Whenever keyboard appears it automatically pushes my popover bit up.When I resign the keyboard , popover reduces in height.This is the issue only on ios 4.3.While in rest of the ios , my popover doesnot reduces in height after keyboard dismissal.
None of the answers above worked for me. Apparently the keyboard scales the view and restores this scaling after the UIKeyboardDidHideNotification notification, making the presentPopoverFromRect method useless when applied handling this notification.
The way I solved it was by delaying the latter call as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
popup = nil; //my ViewController with UITextField
popover = nil; //my UIPopoverController
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(resizePopup:)
name:UIKeyboardDidHideNotification
object:nil];
}
- (void)doDelayedResize
{
[popover presentPopoverFromRect:myButton.bounds inView:myButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
- (void)resizePopup:(NSNotification*)note
{
[self performSelector:#selector(doDelayedResize) withObject:nil afterDelay:0.01];
}
I answered a very similar question here: UIPopoverController's view controller gets resized after keyboard disappears
The way I got around it was to observe the keyboard disappearing in the controller which controls the UIPopoverController:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(presentSearchPopover) name:UIKeyboardDidHideNotification object:nil];
And then in -presentSearchPopover, present the UIPopoverController again (it's quite a seamless transition):
- (void)presentSearchPopover
{
self.searchPopoverController.popoverContentSize = CGSizeMake(width, height));
[self.searchPopoverController presentPopoverFromRect:someRect) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
Don't forget to remove the observer in -dealloc or similar too:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
[super dealloc];
}
I found an answer for this.It was a bug with the top arrow of popover.If I use the left arrow direction for popover, everything works fine.
I ran into this issue as well - specifically, the popover wasn't growing back to its pre-keyboard size after tapping away from the popover. (The popover would grow back if the user dismissed the keyboard directly or the popover's view controller resigned first responder).
Unfortunately, I have to use the top arrow direction for the popover due to the UI's layout. To solve this, the view controller responsible for the popover implements - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController. For example:
#interface MyController : UIViewController <UIPopoverControllerDelegate>
{
// ...
}
//...
#end
Set that controller as the popover's delegate:
MyPopoverViewController *popoverVC = [[MyPopoverViewController alloc] init];
UIPopoverController *myPopover = [[UIPopoverController alloc] initWithContentViewController:popoverVC];
myPopover.delegate = self;
// Hang on to popoverVC, myPopover or release them as desired...
In addition, my popover's view controller sets its contentSizeForViewInPopover property to the desired size:
#implementation MyPopoverViewController
- (id)init
{
self = [super init];
if (self)
{
// ...
self.contentSizeForViewInPopover = CGSizeMake(320, 400); // desired size
}
return self;
}
When the keyboard causes the popover to shrink, it affects the popover's popoverContentSize and not its view controller's contentSizeForViewInPopover. Therefore, reset popoverContentSize in MyController's delegate method:
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
// Check if popoverController is valid, the popover you want, etc
popoverController.popoverContentSize = popoverController.contentViewController.contentSizeForViewInPopover;
}
Here is my solution:
1. Register for keyboard Notifications (UIKeyboardWillShowNotification, UIKeyboardWillHideNotification)
2. Create local variables:
CGSize _currentPopoverContentSize; //if you want to have custom size for popover
UIView *_currentPopoverSender; //to remember from wich view you will present popover
BOOL _keyboardIsShown; //enable in keyboardWillShow, and disable in keyboardWillHide
3. In my presentPopover method:
- (void)presentPopoverControllerWithSize:(CGSize)size fromView:(UIView *)sender{
MyController *controller = [[[MyController alloc] init] autorelease];
if (self.popover)
{
[_popover release];
_popover = nil;
}
_popover = [[UIPopoverController alloc] initWithContentViewController:controller];
_popover.popoverContentSize = size;
_popover.delegate = self;
//checking if keyboard is shown - if NO, than present popover, if YES - just `resignFirstResponder` for your _`activeTextField`(you can set it in -textFieldDidBeginEditing: and nullify in -textFieldDidEndEditing:)
if (!_keyboardIsShown)
{
[_popover presentPopoverFromRect:[sender bounds]
inView:sender
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
}
else
{
[_activeTextField resignFirstResponder];
}
_currentPopoverContentSize = size;
_currentPopoverSender = sender;
}
4. Than:
- (void)keyboardWillBeHidden:(NSNotification*)aNotification{
[UIView animateWithDuration:0.3
animations:^{
//do some stuff
[self.scrollView setContentSize:_scrollViewContentSize];
} completion:^(BOOL finished) {
if (_popover && _currentPopoverSender)
{
[_popover presentPopoverFromRect:[_currentPopoverSender bounds]
inView:_currentPopoverSender
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
}
}];
_keyboardIsShown = NO;
}
Hi After going through the forum, I don't think it's a bug after playing with frame sizes a lot, working on IOS 4,5,6,7 it's the same behaviour.
The solution for me was to:
1) Go into the designer by
2) Opening the XIB ViewController that is causing the problem (i.e. the PopOver one).
3) Click to select it's VIEW.
4) Uncheck "AutoResizeSubviews"
5) When loading the PopOver in code, make sure you do:
6) Your_Popup_Window.popoverContentSize = Your_ViewController.view.bounds.size;
I hope this helps.
Kind Regards
Heider Sati