How to toggle status bar with a fade effect in iOS7 (like Photos app)? - objective-c

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.

Related

Animating a sub layer during focus change

Xcode 8 beta 6, tvOS Beta 6
I have a tvOS app where I want to animate a background of a control when it gets or looses focus. I have set the control to have 'Custom' focus and implemented didUpdateFocusInContext:withAnimationCoordinator: on the control. Here's the code:
-(void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
// Create the layer if we don't have it.
if (!self->_focusLayer) {
// ... Create a new CALayer and store it in _focusLayer
}
// Animate in or out.
if (context.nextFocusedView == self) {
if (! self->_focusLayer.superlayer) {
STLog(self, #"Adding focus");
self->_focusLayer.opacity = 0.0f;
[self.layer addSublayer:self->_focusLayer];
[coordinator addCoordinatedAnimations:^{
self->_focusLayer.opacity = 1.0f;
}
completion:NULL];
}
} else {
if (self->_focusLayer.superlayer) {
STLog(self, #"Removing focus");
[coordinator addCoordinatedAnimations:^{
self->_focusLayer.opacity = 0.0f;
}
completion:^{
[self->_focusLayer removeFromSuperlayer];
}];
}
}
}
Everything works except the animation of the opacity of the sub layer.
I've searched the net and all the examples I've found indicate to me this should work. I've also tried switching to using a CABasicAnimation with no luck.
Anyone know why this is not working?
The focus coordinator is not an animation block itself. It just coordinates various animations to happen simultaneously. Since an opacity change is not an animation itself, you need to do your opacity or alpha changes within a UIView Animation block to make it an animation, which you add to the coordinator.
Try this:
-(void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
// Create the layer if we don't have it.
if (!self->_focusLayer) {
// ... Create a new CALayer and store it in _focusLayer
}
// Animate in or out.
if (context.nextFocusedView == self) {
if (! self->_focusLayer.superlayer) {
STLog(self, #"Adding focus");
self->_focusLayer.opacity = 0.0f;
[self.layer addSublayer:self->_focusLayer];
[coordinator addCoordinatedAnimations:^{
[UIView animateWithDuration: 0.4 animations:^{
self->_focusLayer.opacity = 1.0f;
} completion:NULL];
} completion:NULL];
}
} else {
if (self->_focusLayer.superlayer) {
STLog(self, #"Removing focus");
[coordinator addCoordinatedAnimations:^{
[UIView animateWithDuration:0.4 animations:^{
self->_focusLayer.opacity = 0.0f;
} completion:NULL];
} completion:^{
[self->_focusLayer removeFromSuperlayer];
}];
}
}
}
Please note that the above code is typed here and not tested in a project.
Also note that the Animation duration is ignored by the animation coordinator and the default focus animation duration is used, unless you set an animation option to override inherited durations using:
options:UIViewAnimationOptionOverrideInheritedDuration
More about managing the focus animation coordinator here:
https://developer.apple.com/reference/uikit/uifocusanimationcoordinator

Add UISplitViewController via addChildViewController

I have one UIViewController in which another viewcontroller(1) and splitviewcontroller(2) do switching via addchildviewcontroller method. So when I add splitviewcontroller it doesn't handle correct the rotations. Look at the video – https://dl.dropbox.com/u/2139277/IMG_0180.MOV.
Here is the code that does switching:
- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController
{
if (fromViewController == toViewController)
return;
// animation setup
toViewController.view.frame = self.view.bounds;
// notify
[fromViewController willMoveToParentViewController:nil];
[self addChildViewController:toViewController];
// select animation direction
UIViewAnimationOptions animation = (_contentState == ContentStateViewingMap) ? UIViewAnimationOptionTransitionCurlUp : UIViewAnimationOptionTransitionCurlDown;
// transition
ContentState previousState = _contentState;
_contentState = ContentStateAnimating;
[self transitionFromViewController:fromViewController
toViewController:toViewController
duration:0.6
options:animation | UIViewAnimationOptionCurveEaseInOut
animations:nil
completion:^(BOOL finished) {
[toViewController didMoveToParentViewController:self];
[fromViewController removeFromParentViewController];
_contentState = (previousState == ContentStateViewingMap) ? ContentStateViewingList : ContentStateViewingMap;
}];
}
AFAIK a UISplitViewController MUST be the root view controller of the window. Adding it as child view controller is not supported.

hide UITableView header section

I'm using the following code to hide a view and the space taken by the view based on a condition in viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
Data* data = [Data shared];
if (data.something == 0) {
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = 0;
self.tableView.tableHeaderView.frame = frame;
self.tableView.tableHeaderView.hidden = YES;
} else {
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = 44;
self.tableView.tableHeaderView.frame = frame;
self.tableView.tableHeaderView.hidden = NO;
}
}
The above code works, but I'm pretty sure that is not the right way to do that. I tried to set the tableHeaderView to nil, but once the code is called, the headerView is gone until the UITableView is destroyed (I think I can fix it using a IBOutlet to the tableHeader, but doesn't sounds right too.
UPDATE1: another try, but the code doesn't work:
- (CGFloat)tableView:(UITableView*)tableView heightForHeaderInSection:(NSInteger)section {
self.tableView.tableHeaderView.hidden = YES;
return 0;
}
The data source method tableView:heightForHeaderInSection: actually has nothing to do with the view that is associated with the table view's tableViewHeader property. There are two different types of headers here, the one header at the top of the tableView, in which can be placed things like a search bar, and the multiple headers that can be made to occur one per section within the table view.
To my knowledge, the tableViewHeader view is typically configured in the nib file, and I don't know that the table view calls any data source methods that allow any configuration for it, so you would have to do it manually. Frankly, if your code works, that would be a good way to do it. Hiding it would make the table view still act as if it's there...removing it entirely makes it so you can't get it back because it gets deallocated.
(However, as you said, you could use an IBOutlet pointing to the header view, as long as you make it a strong reference, and then you could somehow reinsert it into the table later. ...Hm, although the mechanics of how you add a view into the table view's scroll view, and position it correctly, is probably just annoying.)
My only suggestion would be animating the frame height to zero so you get a nice transition effect, something like animateWithDuration. But yeah, I would say you have the best method figured out already.
EDIT:
Code, you say? I take that as a challenge :)
- (void)setTableViewHeaderHidden:(BOOL)hide
{
// Don't want to muck things up if we are mid an animation.
if (self.isAnimatingHeader) {
return;
}
// This is our IBOutlet property, I am just saving a bit of typing.
UIView *theHeader = self.theHeaderView;
if (hide) {
// Save the original height into the tag, should only be done once.
if (!theHeader.tag) {
theHeader.tag = theHeader.frame.size.height;
}
// Transform and hide
if (theHeader.frame.size.height > 0) {
self.isAnimatingHeader = YES;
// New frame...
CGRect frame = theHeader.frame;
frame.size.height = 0;
// Figure out some offsets here so we prevent jumping...
CGPoint originalOffset = self.tableView.contentOffset;
CGPoint animOffset = originalOffset;
animOffset.y += MAX(0, theHeader.tag - animOffset.y);
CGPoint newOffset = originalOffset;
newOffset.y = MAX(0, newOffset.y - theHeader.tag);
// Perform the animation
[UIView animateWithDuration:0.35
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{
theHeader.frame = frame;
self.tableView.contentOffset = animOffset;
}
completion:^(BOOL finished){
if (finished) {
// Hide the header
self.tableView.tableHeaderView = nil;
theHeader.hidden = YES;
// Shift the content offset so we don't get a jump
self.tableView.contentOffset = newOffset;
// Done animating.
self.isAnimatingHeader = NO;
}
}
];
}
} else {
// Show and transform
if (theHeader.frame.size.height < theHeader.tag) {
self.isAnimatingHeader = YES;
// Set the frame to the original before we transform, so that the tableview corrects the cell positions when we re-add it.
CGRect originalFrame = theHeader.frame;
originalFrame.size.height = theHeader.tag;
theHeader.frame = originalFrame;
// Show before we transform so that you can see it happen
self.tableView.tableHeaderView = theHeader;
theHeader.hidden = NO;
// Figure out some offsets so we don't get the table jumping...
CGPoint originalOffset = self.tableView.contentOffset;
CGPoint startOffset = originalOffset;
startOffset.y += theHeader.tag;
self.tableView.contentOffset = startOffset; // Correct for the view insertion right off the bat
// Now, I don't know if you want the top header to animate in or not. If you think about it, you only *need* to animate the header *out* because the user might be looking at it. I figure only animate it in if the user is already scrolled to the top, but hey, this is open to customization and personal preference.
if (self.animateInTopHeader && originalOffset.y == 0) {
CGPoint animOffset = originalOffset;
// Perform the animation
[UIView animateWithDuration:0.35
delay:0.0
options: UIViewAnimationCurveEaseIn
animations:^{
self.tableView.contentOffset = animOffset;
}
completion:^(BOOL finished){
// Done animating.
self.isAnimatingHeader = NO;
}
];
} else {
self.isAnimatingHeader = NO;
}
}
}
}
Built this in the table view template that comes with Xcode. Just to throw it together I used a UILongPressGestureRecognizer with the selector outlet pointing to this method:
- (IBAction)longPress:(UIGestureRecognizer *)sender
{
if (sender.state != UIGestureRecognizerStateBegan) {
return;
}
if (self.hidingHeader) {
self.hidingHeader = NO;
[self setTableViewHeaderHidden:NO];
} else {
self.hidingHeader = YES;
[self setTableViewHeaderHidden:YES];
}
}
And, I added these to my header:
#property (strong, nonatomic) IBOutlet UIView *theHeaderView;
#property (nonatomic) BOOL hidingHeader;
#property (nonatomic) BOOL isAnimatingHeader;
#property (nonatomic) BOOL animateInTopHeader;
- (IBAction)longPress:(id)sender;
Anyway, it works great. What I did discover is that you definitely have to nil out the table view's reference to the header view or it doesn't go away, and the table view will shift the cells' position based on the height of the frame of the header when it is assigned back into its header property. Additionally, you do have to maintain a strong reference via your IBOutlet to the header or it gets thrown away when you nil out the table view's reference to it.
Cheers.
Instead of,
if (1 == 1) {
CGRect frame = self.viewHeader.frame;
frame.size.height = 0;
self.viewHeader.frame = frame;
self.viewHeader.hidden = YES;
}
use it as,
if (1 == 1) {
self.viewHeader.hidden = YES;
}
If you do not want the view anymore instead of just hiding, use [self.viewHeader removeFromSuperview];
And if you want to add it after removing [self.view addSubview:self.viewHeader]; All these depends on your requirement.
Update:
for eg:-
if (data.something == 0) {
//set frame1 as frame without tableHeaderView
self.tableView.frame = frame1;
self.tableView.tableHeaderView.hidden = YES;
} else {
//set frame2 as frame with tableHeaderView
self.tableView.frame = frame2;
self.tableView.tableHeaderView.hidden = NO;
}
or,
if (data.something == 0) {
//set frame1 as frame without tableHeaderView
self.tableView.frame = frame1;
self.tableView.tableHeaderView = nil;
} else {
//set frame2 as frame with tableHeaderView
self.tableView.frame = frame2;
self.tableView.tableHeaderView = self.headerView; //assuming that self.headerview is the tableHeaderView created while creating the tableview
}
Update2: Here is a very simple version of animation block.
if (data.something == 0) {
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationCurveEaseOut
animations:^{
//set frame1 as frame without tableHeaderView
self.tableView.frame = frame1;
self.tableView.tableHeaderView.hidden = YES; // or self.tableView.tableHeaderView = nil;
}
completion:^(BOOL finished){
//if required keep self.tableView.frame = frame1;
}
];
} else {
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationCurveEaseIn
animations:^{
//set frame2 as frame with tableHeaderView
self.tableView.frame = frame2;
self.tableView.tableHeaderView.hidden = NO;// or self.tableView.tableHeaderView = self.headerView;
}
completion:^(BOOL finished){
//if required keep self.tableView.frame = frame2;
}];
}

error in Button Jiggle algorithm

i have impletemented a button jiggle animation.where pressing a button wobbles ,but the problem is the animation does'nt stop and getting an error in [self.layer removeAllAnimations];
below is the code;
-(IBAction)button1Clicked:(id)sender
{
UIButton *no1 =sender;
output= [self answerCheck:no1.titleLabel.text];
self.label.text=output;
[self enableOptions:NO];
[self loadingView];
[self startJiggling:2];
}
- (void)startJiggling:(NSInteger)count
{
CGAffineTransform leftWobble = CGAffineTransformMakeRotation(degreesToRadians( kAnimationRotateDeg * (count%2 ? +1 : -1 ) ));
CGAffineTransform rightWobble = CGAffineTransformMakeRotation(degreesToRadians( kAnimationRotateDeg * (count%2 ? -1 : +1 ) ));
CGAffineTransform moveTransform = CGAffineTransformTranslate(rightWobble, -kAnimationTranslateX, -kAnimationTranslateY);
CGAffineTransform conCatTransform = CGAffineTransformConcat(rightWobble, moveTransform);
self.btnOption1.transform = leftWobble; // starting point
[UIView animateWithDuration:0.1
delay:(count * 0.08)
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse
animations:^{ self.btnOption1.transform = conCatTransform; }
completion:nil];
[self stopJiggling];
}
-(void)stopJiggling
{
[self.btnOption1.layer removeAllAnimations];
self.btnOption1.transform = CGAffineTransformIdentity; // Set it straight
}
You're setting the animation on self.btnOption1, so you would need to remove it from self.btnOption1:
- (void)stopJiggling {
[self.btnOption1.layer removeAllAnimations];
self.btnOption1.transform = CGAffineTransformIdentity;
}
but in fact if you just set the transform property of the button again, outside of an animation block, it will remove the animation:
- (void)stopJiggling {
self.btnOption1.transform = CGAffineTransformIdentity;
}
(This worked in my test project.)
Update:
I notice that you're starting the animation with a delay, and you're calling stopJiggling immediately after you call animateWithDuration:.... I don't know why you're using a delay or why you're calling stopJiggling immediately.
I created a test case to match your code:
#implementation ViewController {
__unsafe_unretained IBOutlet UIButton *btnOption1;
}
- (IBAction)startJiggling {
btnOption1.transform = CGAffineTransformMakeRotation(-.1);
[UIView animateWithDuration:.1 delay:2 * 0.08 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse animations:^{
btnOption1.transform = CGAffineTransformMakeRotation(.1);
} completion:nil];
[self stopJiggling];
}
- (void)stopJiggling {
[btnOption1.layer removeAllAnimations];
btnOption1.transform = CGAffineTransformIdentity;
}
#end
I hooked up my btnOption1 ivar to a button, and connected the button to the startJiggling method. With the code as shown, clicking the button does nothing, because the animation is removed immediately after it is added. If I comment out the removeAllAnimations message, clicking the button makes the button start jiggling and it jiggles forever. I tested on the iPhone 4.3 simulator, the iPhone 5.0 simulator, the iPhone 5.1 simulator, and my iPhone 4S running iOS 5.1.
So, I could not reproduce your problem. Sending removeAllAnimations removes the animation in my test.
I suspect that you just want the animation to repeat twice and then stop (since you have an argument named count and you're passing 2). If that's what you want to do, you can do it like this:
- (IBAction)startJiggling {
btnOption1.transform = CGAffineTransformMakeRotation(-.1);
[UIView animateWithDuration:.1 delay:2 * 0.08 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse animations:^{
[UIView setAnimationRepeatCount:2];
btnOption1.transform = CGAffineTransformMakeRotation(.1);
} completion:^(BOOL completed){
btnOption1.transform = CGAffineTransformIdentity;
}];
}
You set the repeat count inside the animation block using +[UIView setAnimationRepeatCount:], and you restore the button's transform in the completion block.
Remember to import QuartzCore/QuartzCore.h to talk with layers.

Closing a UIpicker in an iphone app

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!