I found that using an animation with opposite curves looked best:
// animating in...
[UIView animateWithDuration:0.25 delay:0
options:UIViewAnimationCurveEaseIn
animations:^{
// ...
} completion:^(BOOL finished) {
// ...
}];
// animating out...
[UIView animateWithDuration:0.25 delay:0
options:UIViewAnimationCurveEaseOut
animations:^{
// ...
} completion:^(BOOL finished) {
// ...
}];
- (UIView *)backdropView {
UIView *backdropView = [[UIView alloc] initWithFrame:self.bounds];
backdropView.backgroundColor = [UIColor colorWithWhite:0 alpha:BSMODALPICKER_BACKDROP_OPACITY];
backdropView.alpha = 0;
UIGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onBackdropTap:)];
[backdropView addGestureRecognizer:tapRecognizer];
return backdropView;
}
- (void)presentInView:(UIView *)view withBlock:(BSModalPickerViewCallback)callback {
self.frame = view.bounds;
self.callbackBlock = callback;
[_panel removeFromSuperview];
[_backdropView removeFromSuperview];
_panel = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height - BSMODALPICKER_PANEL_HEIGHT, self.bounds.size.width, BSMODALPICKER_PANEL_HEIGHT)];
_picker = [self picker];
_toolbar = [self toolbar];
_backdropView = [self backdropView];
[self addSubview:_backdropView];
[_panel addSubview:_picker];
[_panel addSubview:_toolbar];
[self addSubview:_panel];
[view addSubview:self];
CGRect oldFrame = _panel.frame;
CGRect newFrame = _panel.frame;
newFrame.origin.y += newFrame.size.height;
_panel.frame = newFrame;
[UIView animateWithDuration:0.25 delay:0
options:UIViewAnimationCurveEaseOut
animations:^{
_panel.frame = oldFrame;
_backdropView.alpha = 1;
} completion:^(BOOL finished) {
}];
}
- (void)dismissPicker {
[UIView animateWithDuration:0.25 delay:0
options:UIViewAnimationCurveEaseOut
animations:^{
CGRect newFrame = _panel.frame;
newFrame.origin.y += _panel.frame.size.height;
_panel.frame = newFrame;
_backdropView.alpha = 0;
} completion:^(BOOL finished) {
[_panel removeFromSuperview];
_panel = nil;
[_backdropView removeFromSuperview];
_backdropView = nil;
[self removeFromSuperview];
}];
}
Finally, we just need to cancel the dialog if the user taps on the backdrop:
- (void)onBackdropTap:(id)sender {
[self onCancel:sender];
}