I know this question has been asked a few times. I understand how to move the view up or a textfield up when the keyboard is presented, however I am running into a glitch that I cannot seem to figure out.
The view I am trying to move up is inside a UIViewController that acts as a container for two views. This container is itself a view inside a sliding view controller (kind of like the one Facebook implements).
The text field moves up fine when you first load the view however if you go to a different view and come back, the keyboard causes the view to disappear.
Here is the code I am using to move the view up:
- (void) animateTextField:(BOOL)up {
const int movementDistance = 350; // tweak as needed
const float movementDuration = 0; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.sendingToolbar.frame = CGRectOffset(self.sendingToolbar.frame, 0, movement);
self.messagesTable.frame = CGRectOffset(self.messagesTable.frame, 0, movement);
//[splitController moveViewUp:up];
[UIView commitAnimations];
}
- (void)registerForKeyboardNotifications
{
NSLog(#"was this method called");
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
[self animateTextField:YES];
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
[self animateTextField:NO];
}
Any ideas why this glitch might be happening? Thanks
After adding the suggestion for a boolean to check if the text field is already up the view disappears all the time when the keyboard is shown and not just when you leave the view and come back. Here is the revised method:
- (void) animateTextField:(BOOL)up {
const int movementDistance = 350; // tweak as needed
const float movementDuration = 0; // tweak as needed
int movement = movementDistance;
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
if(textFieldIsUp && (up == YES)) {
NSLog(#"Case 1");
// Do nothing since text field is already up
}
else if(textFieldIsUp && (up == NO)) {
NSLog(#"Case 2");
// Move the text field down
self.sendingToolbar.frame = CGRectOffset(self.sendingToolbar.frame, 0, -movement);
self.messagesTable.frame = CGRectOffset(self.messagesTable.frame, 0, -movement);
textFieldIsUp = NO;
}
else if((textFieldIsUp == NO) && (up == YES)) {
NSLog(#"Case 3");
// Move the text field up
self.sendingToolbar.frame = CGRectOffset(self.sendingToolbar.frame, 0, movement);
self.messagesTable.frame = CGRectOffset(self.messagesTable.frame, 0, movement);
textFieldIsUp = YES;
}
else if((textFieldIsUp == NO) && (up == NO)) {
NSLog(#"Case 4");
// Do nothing since the text field is already down
}
else {
NSLog(#"Default");
// Default catch all case. Does nothing
}
[UIView commitAnimations];
}
Here are some more details about my setup:
The view controller is a messaging center for the app. The view controller contains two subviews the subview on the left side is a menu for picking the conversation and the right menu is a subview with the messages within that conversation. Since I want the messages to load from the bottom up, the table view is rotated 180 degrees and the cells are also rotated 180 degrees the opposite direction. Also the table view reloads every 5 seconds using an NSTimer so that the messages can be updated with any new messages.
The textfield has already been moved up but didn't move back down when you left the view. When you revisit the view it gets moved up again - and off the screen.
I've had this problem before; solved it by keeping a boolean variable the indicated whether the textfield was in the up or down position. Check the variable to make sure the textfield isn't already up before you move it up again.
How I did it. I'm using an NSNumber property on my view to store whether the view has been pushed up or not so that other views can communicate whether they have pushed the view up or down.
//In viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidHide:) name:UIKeyboardWillHideNotification object:nil];
//Pushes the view up if one of the table forms is selected for editing
- (void) keyboardDidShow:(NSNotification *)aNotification
{
if ([isRaised boolValue] == NO)
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25];
self.view.center = CGPointMake(self.view.center.x, self.view.center.y-moveAmount);
[UIView commitAnimations];
isRaised = [NSNumber numberWithBool:YES];
}
}
//Push view back down
- (void) keyboardDidHide:(NSNotification *)aNotification
{
if ([isRaised boolValue])
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25];
self.view.center = CGPointMake(self.view.center.x, self.view.center.y+moveAmount);
[UIView commitAnimations];
isRaised = [NSNumber numberWithBool:NO];
}
}
-EDIT-
Looking at your code it appears that you're subtracting from the y-coordinate of a frame to move frame down. The iOS coordinate system for CGRect is not the same as a normal coordinate system - the y-axis is flipped (this is relatively common for graphic systems). You're going to want to do the opposite of what you're doing.
Related
I have a simple KeyboardAdjuster class that is a property of my form views. If one of the form fields is hidden by the keyboard, then entering that field will have an animation to move the whole frame's origin.y up, so that the field appears above the keyboard. A very common approach. It also has a few complexities like calculating how much to scroll by when navigating between fields, but that's not important right now. . . I've been using this utility class since iOS5.
Example Form:
The problem:
On iOS8 it has simply stopped working.
When animating the frame starts by snapping in the opposite direction exactly the amount that its supposed to scroll by. And then scrolling back to the origin.
I tried commenting the animation part out, and simply setting the frame. No effect.
For example if the frame is supposed to be: {0, -127, 320, 480} then it will simply stay at {0, 0, 320, 480}
Why doesn't this work on iOS8? Has something changed that I've missed?
About the views:
My views are hand-coded, they're a sub-class of a simple form base-view. (Contains keyboard adjuster and a scroll-view). The other elements are added with initial frames of CGRectZero and then laid out manually in layoutSubviews
How the view/controller created:
There is a RootViewController that acts as a container controller (UIView containment). It:
Has a main navigation controller
Has a container to present / dismiss a hamburger menu for the nav controller's top views (these can change).
Has a container to present overlays with a custom bounce animation.
So the view is created as follows:
- (instancetype)initWithView:(INFAcceptGiftView *)view offerDao:(id <INFOfferDao>)offerDao
locationTracker:(INFLocationTracker *)locationTracker
{
self = [super init];
if (self)
{
self.view = view;
_offerDao = offerDao;
_locationTracker = locationTracker;
}
return self;
}
What triggers the keyboard animation?:
The UIView is a sub-class of form base view, which is a UITextFieldDelegate:
interface INFFormBaseView : UIView <UITextFieldDelegate, INFInputAccessoryDelegate>
{
UIResponder *_currentResponder;
INFInputValidator *_validator;
}
When a field is entered:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[_validator dismissMessages];
[_keyboardAdjuster scrollToAccommodateField:textField];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[_keyboardAdjuster scrollToAccommodateField:nil];
}
I have a different solution for you which works on both iOS 7 & 8 and Auto Layout.
In my example I have two UITextFields which I move and hide depending on their position and the position of the keyboard. In this particular case, I switch the UITextFields between them and hide the inactive one.
In viewDidLoad you register for the following notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardShowed:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardHidden:)
name:UIKeyboardWillHideNotification object:nil];
After that you grab a hold of the default frames of your views, in this case the two UITextFields:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
/*** FOR AUTOLAYOUT MODIFICATIONS & ADDITIONS # RUNTIME ***/
self.mailTextFieldDefaultFrame = self.mailTextField.frame;
self.passwordTextFieldDefaultFrame = self.passwordTextField.frame;
}
And when you receive UIKeyboardWillShowNotification you'll start moving your views:
- (void) keyboardShowed:(NSNotification*)notification {
//GET KEYBOARD FRAME
CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
//CONVERT KEYBOARD FRAME TO MATCH OUR COORDINATE SYSTEM (FOR UPSIDEDOWN ROTATION)
CGRect convertedFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];
if ([self.mailTextField isFirstResponder]) {
[UIView transitionWithView:self.mailTextField
duration:.3f
options:UIViewAnimationOptionCurveLinear
animations:^{
self.mailTextField.alpha = 1.0f;
self.mailTextField.frame = CGRectMake(self.mailTextField.frame.origin.x,
convertedFrame.origin.y -
self.mailTextField.frame.size.height - 25,
self.mailTextField.frame.size.width,
self.mailTextField.frame.size.height);
self.passwordTextField.alpha = 0.0f;
}
completion:nil];
} else if ([self.passwordTextField isFirstResponder]) {
[UIView transitionWithView:self.passwordTextField
duration:.3f
options:UIViewAnimationOptionCurveLinear
animations:^{
self.passwordTextField.alpha = 1.0f;
self.mailTextField.frame = self.passwordTextField.frame;
self.passwordTextField.frame = CGRectMake(self.passwordTextField.frame.origin.x,
convertedFrame.origin.y -
self.passwordTextField.frame.size.height - 25,
self.passwordTextField.frame.size.width,
self.passwordTextField.frame.size.height);
self.mailTextField.alpha = 0.0f;
}
completion:(void (^)(BOOL finished)) ^{
}];
}
And when you hide the keyboard:
- (void) keyboardHidden:(NSNotification*)notification {
//RESTORE ORIGINAL STATE OF TEXTFIELDS
[UIView transitionWithView:self.view
duration:.3f
options:UIViewAnimationOptionCurveLinear
animations:^{
self.mailTextField.frame = self.mailTextFieldDefaultFrame;
self.passwordTextField.frame = self.passwordTextFieldDefaultFrame;
self.mailTextField.alpha = 1.0f;
self.passwordTextField.alpha = 1.0f;
}
completion:nil];
}
Here's the solution, posting in case it helps someone.
I mentioned above that I'm using UIView containment, so I have a root view controller that:
Contains a UINavigationController (the root view is replaceable).
Contains a Menu Controller (dealloc'd when not in use)
Presents an overlay with custom animation
My root view had layout subviews as follows:
- (void)layoutSubviews
{
[super layoutSubviews];
[_mainContentViewContainer setFrame:self.bounds];
}
This behaved the way that I wanted it to pre iOS8, but not afterwards. Technically it appears that iOS8 is doing the right thing - I should only be laying out the _mainConentViewContainer on startup or orientation change.
I want to toggle the visibility of the status bar on tap, just like it does in the Photos app.
Prior to iOS 7, this code worked well:
-(void)setStatusBarIsHidden:(BOOL)statusBarIsHidden {
_statusBarIsHidden = statusBarIsHidden;
if (statusBarIsHidden == YES) {
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
}else{
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
}
}
But I can't get it to work in iOS 7. All the answers that I found only offer suggestions for permanently hiding the bar but not toggling.
Yet, there must be a way since Photos does it.
By default on iOS 7 or above, to hide the status bar for a specific view controller, do the following:
if the view controller you want to hide the status bar with is being presented modally and the modalPresentationStyle is not UIModalPresentationFullScreen, manually set modalPresentationCapturesStatusBarAppearance to YES on the presented controller before it is presented (e.g. in -presentViewController:animated:completion or -prepareForSegue: if you're using storyboards)
override -prefersStatusBarHidden in the presented controller and return an appropriate value
call setNeedsStatusBarAppearanceUpdate on the presented controller
If you want to animate it's appearance or disappearance, do step three within an animation block:
[UIView animateWithDuration:0.33 animations:^{
[self setNeedsStatusBarAppearanceUpdate];
}];
You can also set the style of animation by returning an appropriate UIStatusBarAnimation value from -preferredStatusBarUpdateAnimation in the presented controller.
First set View controller-based status bar appearance in Info.plist to YES
This Swift Example shows how to toggle the StatusBar with an Animation, after pressing a Button.
import UIKit
class ToggleStatusBarViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func prefersStatusBarHidden() -> Bool {
return !UIApplication.sharedApplication().statusBarHidden
}
override func preferredStatusBarUpdateAnimation() -> UIStatusBarAnimation {
return UIStatusBarAnimation.Slide
}
#IBAction func toggleStatusBar(sender: UIButton) {
UIView.animateWithDuration(0.5,
animations: {
self.setNeedsStatusBarAppearanceUpdate()
})
}
}
I was able to simplify #Jon's answer and still get behavior indistinguishable from the Photos app on iOS 7. It looks like the delayed update when showing isn't necessary.
- (IBAction)toggleUI:(id)sender {
self.hidesUI = !self.hidesUI;
CGRect barFrame = self.navigationController.navigationBar.frame;
CGFloat alpha = (self.hidesUI) ? 0.0 : 1.0;
[UIView animateWithDuration:0.33 animations:^{
[self setNeedsStatusBarAppearanceUpdate];
self.navigationController.navigationBar.alpha = alpha;
}];
self.navigationController.navigationBar.frame = CGRectZero;
self.navigationController.navigationBar.frame = barFrame;
}
- (BOOL)prefersStatusBarHidden {
return self.hidesUI;
}
This might be considered a bit of a hack but it's the closest I've come to reproducing the effect. There's still one minor issue. When fading out, you can see the navigation bar being resized from the top. It's subtle enough but still not a perfect fade. If anyone knows how to fix it, let me know!
- (BOOL)prefersStatusBarHidden {
if (_controlsAreHidden == YES)
return YES;
else
return NO;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationFade;
}
-(void)setControlsAreHidden:(BOOL)controlsAreHidden {
_controlsAreHidden = controlsAreHidden;
if (controlsAreHidden == YES) {
// fade out
//
CGRect barFrame = self.navigationController.navigationBar.frame;
[UIView animateWithDuration:0.3 animations:^ {
[self setNeedsStatusBarAppearanceUpdate];
self.navigationController.navigationBar.alpha = 0;
}];
self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 44);
}else{
// fade in
//
CGRect barFrame = self.navigationController.navigationBar.frame;
self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 64);
[UIView animateWithDuration:0.3 animations:^ {
[self setNeedsStatusBarAppearanceUpdate];
self.navigationController.navigationBar.alpha = 1;
}];
}
}
This code works perfectly fine:
-(void)setControlsAreHidden:(BOOL)controlsAreHidden {
if (_controlsAreHidden == controlsAreHidden)
return;
_controlsAreHidden = controlsAreHidden;
UINavigationBar * navigationBar = self.navigationController.navigationBar;
if (controlsAreHidden == YES) {
// fade out
//
CGRect barFrame = self.navigationController.navigationBar.frame;
[UIView animateWithDuration:0.3 animations:^ {
[self setNeedsStatusBarAppearanceUpdate];
self.navigationController.navigationBar.alpha = 0;
}];
self.navigationController.navigationBar.frame = CGRectZero;
self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 44);
} else {
// fade in
//
[UIView animateWithDuration:UINavigationControllerHideShowBarDuration animations:^ {
[self setNeedsStatusBarAppearanceUpdate];
}];
double delayInSeconds = 0.01;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self.navigationController setNavigationBarHidden:NO animated:NO];
navigationBar.alpha = 0;
[UIView animateWithDuration:UINavigationControllerHideShowBarDuration animations:^ {
navigationBar.alpha = 1;
}];
});
}
}
Actually there is now need to mess with navigation bar frames. You can achieve smooth animation just by using 2 separate animation blocks. Something like this should work just fine.
#property (nonatomic, assign) BOOL controlsShouldBeHidden;
...
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated {
if (self.controlsShouldBeHidden == hidden) {
return;
}
self.controlsShouldBeHidden = hidden;
NSTimeInterval duration = animated ? 0.3 : 0.0;
[UIView animateWithDuration:duration animations:^(void) {
[self setNeedsStatusBarAppearanceUpdate];
}];
[UIView animateWithDuration:duration animations:^(void) {
CGFloat alpha = hidden ? 0 : 1;
[self.navigationController.navigationBar setAlpha:alpha];
}];
}
- (BOOL)prefersStatusBarHidden {
return self.controlsShouldBeHidden;
}
For compatibility with iOS 6 just make sure to check [self respondsToSelector:#selector(setNeedsStatusBarAppearanceUpdate)]
The way to resolve this depends on the value of the "View controller-based status bar appearance" setting in your app's plist.
If "View controller-based status bar appearance" is NO in your plist, then this code should work:
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
If "View controller-based status bar appearance" is on, in your view controllers, add this method:
- (BOOL) prefersStatusBarHidden {
// I've hardcoded to YES here, but you can return a dynamic value to meet your needs for toggling
return YES;
}
For toggling, when you want to change whether the status bar is hidden/shown based on the value of the above method, your view controller can call the setNeedsStatusBarAppearanceUpdate method.
To correct this issue with navigation bar sliding up when fading, you should add the following code:
self.navigationController.navigationBar.frame = CGRectZero;
into your "fade in" section before the following code line:
self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 64);
This is necessary because the frame is the same and setting the same frame will be ignored and will not stop the navigation bar from sliding. Therefore you need to change the frame to something different and then set it again to the correct frame to trigger the change.
I'm new to the iphone dev. The app I'm making uses the picker to input value from the user. I have managed to make the picker hidden until the user hits the button. I used the mypicker.alpha = 0; in viewdidload so the the picker is invisible when the program starts. When the user hits the start button it executes the code mypicker.alpha=1;. I want the picker to close after the user chooses a value. How do I do that? anyone have any hints or tutorials ? I looked at few but they were confusing! Also how do I make the picker appear from the bottom up ? (like the keyboard !)
One method I recently started using is to put a shade button behind the picker, a large transparent black button the size of the screen, color black with alpha=0.3 ([UIColor colorWithWhite:0 alpha:0.3f] I think it was). This just puts a transparent "shade" over the rest of the screen except for the picker, similar to how it looks when you use UIAlertView. Then hook up the button so that it sends resignFirstResponder to the picker. Now when the user is done picking, they just tap anywhere outside the picker in the shaded area, and the button resigns the picker, and the picker can be slid down and the button faded out with an animation.
The picker slide up/down animation can be done and I have the code for it at home but don't have access to it right now. You can make it appear just like the keyboard and send the same notifications that the keyboard sends.
Don't use:
mypicker.alpha = 1;
mypicker.alpha = 0;
You should use:
mypicker.hidden = YES;
mypicker.hidden = NO;
in order to show or hide the picker.
In order to make it appear from the bottom, you can use block animations. I would use:
The .h file:
#interface viewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
BOOL shouldMoveDown;
IBOutlet UIPickerView * picker;
}
- (IBAction)movePicker;
The .m file:
#pragma mark - View lifecycle
- (void)viewDidLoad; {
[super viewDidLoad];
picker.hidden = YES;
shouldMoveDown = NO;
picker.userInteractionEnabled = NO;
}
- (IBAction)movePicker; {
if(shouldMoveDown){
[UIView animateWithDuration:1.0
animations:^{
CGRect newRect = picker.frame;
newRect.origin.y += 236; // 480 - 244 (Height of Picker) = 236
picker.frame = newRect;
}
completion:^(BOOL finished){
[UIView animateWithDuration:1.0
animations:^{
picker.hidden = YES;
shouldMoveDown = NO;
picker.userInteractionEnabled = NO;
}
completion:^(BOOL finished){
;
}];
}];
}
else{
picker.hidden = NO;
//picker.frame = CGRectMake(picker.frame.origin.x, 480, picker.frame.size.width, picker.frame.size.height);
[UIView animateWithDuration:1.0
animations:^{
CGRect newRect = picker.frame;
newRect.origin.y -= 236; // 480 - 244 (Height of Picker) = 236
picker.frame = newRect;
}
completion:^(BOOL finished){
[UIView animateWithDuration:1.0
animations:^{
shouldMoveDown = YES;
picker.userInteractionEnabled = YES;
}
completion:^(BOOL finished){
;
}];
}];
}
}
#pragma mark -
#pragma mark Picker Delegate Methods
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView; {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component; {
return 1;
}
- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component; {
return #"1";
}
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component; {
}
Obviously you can set the picker up any way you would like to. You can also alter the speed at which this happens too! Hope this helps!
I am trying to write an app with some camera function, and I use an overlay view to decorate it with an image.
This is how I implement the app:
I use the UIImagePickerController to who the user what the camera takes in, and add a UIImageView onto the cameraOverlayView as a subview so that it works like this:
(image at http://www.manna-soft.com/test/uploads/UIImagePickerView-portrait.jpg)
This works fine until the iPad2 come into place... it autorotates like this and ruin the layout:
(image at http://www.manna-soft.com/test/uploads/UIImagePickerView-landscape.jpg)
The UIImagePickerController never rotates on iphone, ipod touch or the original iPad, but it does on iPad2.
the class reference of UIImagePickerContrller says that it "supports portrait mode only", but what happens is it autorotates like that....
Is there a way that I can disable the autorotation?
I tried returning NO in the method shouldAutorotateToInterfaceOrientation: of the view controller which the UIImagePickerController is presented, but it still rotates.
Thanks in advance.
The overlay view can be added to the window, and thn the window.superview can be set as cameraOverlayView.
While dismissing the ModalController the overlay view can be removed from the window.
This solution can be a little tricky to apply depending on how your app is structured.
YourAppDelegate *appDelegate = (YourAppDelegate *) [[UIApplication sharedApplication] delegate];
[appDelegate.window addSubview:overlayView];
imagePickerController.cameraOverlayView = appDelegate.window.superview;
//When dismissing the UIImagePicker
[self dismissModalViewControllerAnimated:YES];
[OverlayView removeFromSuperview];
Because UIImagePickerController derives from UINavigationController which derives from UIViewController, you can check out at "Handling View Rotations" in the UIViewController doc to see if that info helps.
You will notice that when you do this:
UIImagePickerController *camera = [UIImagePickerController new];
NSLog([self.camera shouldAutorotate] ? #"YES" : #"NO");
The result will be YES. I think by default, it is set to YES.
You can subclass UIImagePickerController and add this method to override that method:
- (BOOL)shouldAutorotate{
return NO;
}
Then instead of using UIImagePickerController, use your created subclass.
UIImagePickerSubclass *camera = [UIImagePickerSubclass new];
Hope this helps :)
You can compensate the rotation of your ipad by roatting the overlay view of your UIImagePickerController. Fisrt you have to capture the notifications using:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notificationCallback:) name:nil object:nil];
Then use this code:
- (void) notificationCallback:(NSNotification *) notification {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
if ([[notification name] isEqualToString:#"UIDeviceOrientationDidChangeNotification"]) {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
switch ( orientation ) {
case UIInterfaceOrientationLandscapeRight:
NSLog(#"LandcapeRight");
[UIView beginAnimations:#"LandscapeRight" context:UIGraphicsGetCurrentContext()];
[UIView setAnimationDuration:0.4];
m_uiCameraOverlayView.transform = CGAffineTransformIdentity;
[UIView commitAnimations];
break;
case UIInterfaceOrientationLandscapeLeft:
NSLog(#"LandscapeLeft");
[UIView beginAnimations:#"LandcapeLeft" context:UIGraphicsGetCurrentContext()];
[UIView setAnimationDuration:0.4];
m_uiCameraOverlayView.transform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(M_PI), 0, 0);
[UIView commitAnimations];
break;
case UIInterfaceOrientationPortraitUpsideDown:
NSLog(#"UpsideDown");
[UIView beginAnimations:#"UpsideDown" context:UIGraphicsGetCurrentContext()];
[UIView setAnimationDuration:0.4];
m_uiCameraOverlayView.transform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(-M_PI / 2), -128, -128);
[UIView commitAnimations];
break;
case UIInterfaceOrientationPortrait:
NSLog(#"Portrait");
[UIView beginAnimations:#"Portrait" context:UIGraphicsGetCurrentContext()];
[UIView setAnimationDuration:0.4];
m_uiCameraOverlayView.transform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(M_PI / 2), 128, 128);
[UIView commitAnimations];
break;
default:
NSLog(#"????");
break;
}
}
}
}
I'm using willRotateToInterfaceOrientation to swap views when my iPad rotates. If I have a modal view or an alert view open when my device rotates and swaps views, the view swaps and the alert disappears and does not reappear, even if the alert is "presented" again later.
Edit:
I've narrowed this problem a bit. When a modal view is presented with UIModalPresentationFullScreen, the modal view "survives" rotations.
What can I do to fix this?
Here is my implementation of willRotateToInterfaceOrientation:
- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
//
// Load an alternate view depending on the orientation
//
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
[UIView beginAnimations:#"" context:nil];
[self setView:theLandscapeView];
self.view.bounds = CGRectMake(0, 0, 1024, 768);
self.view.transform = CGAffineTransformMakeRotation(kDegreesToRadians * (-90));
[UIView commitAnimations];
}else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
[UIView beginAnimations:#"" context:nil];
[self setView:theLandscapeView];
self.view.bounds = CGRectMake(0, 0, 1024, 768);
self.view.transform = CGAffineTransformMakeRotation(kDegreesToRadians * (90));
[UIView commitAnimations];
}else if (toInterfaceOrientation == UIInterfaceOrientationPortrait) {
[UIView beginAnimations:#"" context:nil];
[self setView:thePortraitView];
self.view.transform = CGAffineTransformMakeRotation(kDegreesToRadians * (0));
self.view.bounds = CGRectMake(0, 0, 768, 1024);
[UIView commitAnimations];
}else if (toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
[UIView beginAnimations:#"" context:nil];
[self setView:thePortraitView];
self.view.transform = CGAffineTransformMakeRotation(kDegreesToRadians * (180));
self.view.bounds = CGRectMake(0, 0, 768, 1024);
[UIView commitAnimations];
}
}
If I were solving this problem, I would do one of the following
Add the alternate views to a parent view, and not change the view property
Create a design for the views that does not require a full view swap, but rearranges or hides subelements of the view.
Present any modal from the root ViewController of the hierarchy
I would work very hard not to swap out the view entirely for the sake of orientation. It seems like something that will continue to present problems even after you have solved this one.
If you swap views, you should also swap modal views I think.
For example, if you present popover controller - it'll automatically dismissed and then appeared with UI rotation.
Here's an idea: keep you main view constant, but change the subview to your portrait or landscape view. something like:
- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
// Remove the current subview from the main view
if (self.view.subviews.count) {
[self.view.subviews objectAtIndex:0] removeFromSuperview];
}
// Use your if-else block, but change [self setView:] for [self.view addSubview:]
}
So now when you create your modal, it will be linked to your controller, which now has a constant main view.
Note that I didn't test this, as I'm getting my head back into coding after two weeks off...
Good luck!