Agregar UIPickerView y un botón en la hoja de acción: ¿cómo?

120

Mi aplicación requiere que se agreguen las siguientes cosas en una hoja de acción.

  • UIToolbar
  • Botón en UIToolbar
  • Control de UIPicker

He incluido una imagen para comprender mis requisitos.

texto alternativo

¿Podría explicar cómo se puede implementar?

Sagar R. Kothari
fuente
3
¡Gracias por publicar este interesante problema!
Tuyen Nguyen
En lugar de jugar con la gestión de actionSheet, pickerView, etc., recomendaría usar EAActionSheetPicker . Realmente limpia mucho tu código.
ebandersen
@eckyzero Desafortunadamente, EAActionSheetPicker parece estar roto en iOS 7, hay muchos errores que comienzan con "contexto no válido 0x0".
Magnus
por supuesto que es demasiado bueno para ser verdad ... me engañó, y ios b tan difícil
ChuckKelly
EAActionSheetPicker ya no funciona.
osxdirk

Respuestas:

25

Actualización para iOS 7

Documentos de Apple para UIActionSheet :UIActionSheet is not designed to be subclassed, nor should you add views to its hierarchy

Recomiendo no intentar personalizar el contenido de una hoja de acción, ya que puede provocar errores graves de contexto no válido en iOS 7. Acabo de pasar unas horas trabajando en este problema y finalmente decidí adoptar un enfoque diferente. Reemplacé la llamada para mostrar la hoja de acción con un controlador de vista modal que contiene una vista de tabla simple.

Hay muchas formas de lograr esto. Aquí hay una forma que acabo de implementar en un proyecto actual. Es bueno porque puedo reutilizarlo entre 5 o 6 pantallas diferentes donde todos los usuarios pueden seleccionar de una lista de opciones.

  1. Crear una nueva subclase UITableViewController, SimpleTableViewController.
  2. Cree un UITableViewController en su guión gráfico (incrustado en un controlador de navegación) y establezca su clase personalizada en SimpleTableViewController.
  3. Asigne al controlador de navegación para SimpleTableViewController un ID de Storyboard de "SimpleTableVC".
  4. En SimpleTableViewController.h, cree una propiedad NSArray que represente los datos en la tabla.
  5. También en SimpleTableViewController.h, cree un protocolo SimpleTableViewControllerDelegatecon un método requerido itemSelectedatRow:y una propiedad débil llamada delegado de tipo id<SimpleTableViewControllerDelegate>. Así es como devolveremos la selección al controlador padre.
  6. En SimpleTableViewController.m, implemente la fuente de datos tableview y delegue los métodos, llamando itemSelectedatRow:a tableView:didSelectRowAtIndexPath:.

Este enfoque tiene la ventaja adicional de ser bastante reutilizable. Para usar, importe la clase SimpleTableViewController en su ViewController.h, cumpla con SimpleTableViewDelegate e implemente el itemSelectedAtRow:método. Luego, para abrir el modal, simplemente cree una nueva instancia de SimpleTableViewController, configure los datos de la tabla y delegue, y preséntelo.

UINavigationController *navigationController = (UINavigationController *)[self.storyboard instantiateViewControllerWithIdentifier:@"SimpleTableVC"];
SimpleTableViewController *tableViewController = (SimpleTableViewController *)[[navigationController viewControllers] objectAtIndex:0];
tableViewController.tableData = self.statesArray;
tableViewController.navigationItem.title = @"States";
tableViewController.delegate = self;
[self presentViewController:navigationController animated:YES completion:nil];

Creo un ejemplo simple y lo publiqué en github .

Consulte también Mostrar la hoja de acciones provoca errores de contexto no válido CGContext .

Kyle Clegg
fuente
2
¡Ahh iOS 7! Arruinaste todo lo que se había desarrollado hasta ahora. :(
Sagar R. Kothari
@Kyle ¿Puedes ampliar tu respuesta diciendo cuál fue tu enfoque? Gracias
Daniel Sanchez
1
@DanielSanchez Actualizado con una sugerencia alternativa y una muestra de código.
Kyle Clegg
3
En mi humilde opinión, una solución mucho más elegante es configurar la vista de entrada de su campo de texto en UIPickerView y su accesorio en UIToolbar. Puede consultar el proyecto QuickDialog para ver un ejemplo.
Steve Moser
111

Una solución más:

  • sin barra de herramientas pero con un control segmentado (eyecandy)

    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil 
                                                        delegate:nil
                                                        cancelButtonTitle:nil
                                                        destructiveButtonTitle:nil
                                                        otherButtonTitles:nil];
    
    [actionSheet setActionSheetStyle:UIActionSheetStyleBlackTranslucent];
    
    CGRect pickerFrame = CGRectMake(0, 40, 0, 0);
    
    UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:pickerFrame];
    pickerView.showsSelectionIndicator = YES;
    pickerView.dataSource = self;
    pickerView.delegate = self;
    
    [actionSheet addSubview:pickerView];
    [pickerView release];
    
    UISegmentedControl *closeButton = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:@"Close"]];
    closeButton.momentary = YES; 
    closeButton.frame = CGRectMake(260, 7.0f, 50.0f, 30.0f);
    closeButton.segmentedControlStyle = UISegmentedControlStyleBar;
    closeButton.tintColor = [UIColor blackColor];
    [closeButton addTarget:self action:@selector(dismissActionSheet:) forControlEvents:UIControlEventValueChanged];
    [actionSheet addSubview:closeButton];
    [closeButton release];
    
    [actionSheet showInView:[[UIApplication sharedApplication] keyWindow]];
    
    [actionSheet setBounds:CGRectMake(0, 0, 320, 485)];
marcio
fuente
1
Estoy recibiendo UIApplication puede no responder a la advertencia de la ventana principal y la aplicación finaliza.
Mahesh Babu
Estoy tratando de usar UIPickerView pero no he podido integrarlo en mi aplicación. Quiero que UIPickerView se muestre al hacer clic en un botón cuya acción está registrada en MYViewController: UIViewController. En el método de acción he puesto el código anterior de pickerview. ¿Que más deberia hacer? Por favor ayuda.
Namratha
1
En realidad, fredrik, debes usar [[UIApplication sharedApplication] keyWindow]. Fuente editada para cambiar eso.
Eric Goldberg
3
Van Du Tran - (void) despedirAcciónSheet: (UISegmentedControl *) remitente {UIActionSheet actionSheet = (UIActionSheet ) [supervista del remitente]; [hoja de acción desecharConClickButtonIndex: 0 animado: SÍ]; }
WINSergey
1
Aviso: esto causa muchos errores CGContext en iOS 7. Consulte stackoverflow.com/questions/19129091/…
Kyle Clegg
75

Aunque esta pregunta es antigua, mencionaré rápidamente que he reunido una clase ActionSheetPicker con una función de conveniencia, para que pueda generar una ActionSheet con un UIPickerView en una línea. Se basa en el código de las respuestas a esta pregunta.

Editar: ahora también admite el uso de DatePicker y DistancePicker.


UPD:

Esta versión está obsoleta: utilice ActionSheetPicker-3.0 en su lugar.

animación

TimCinel
fuente
1
Super ... usando esto en mi aplicación.
Michael Morrison
Increíble. Usando esto ahora.
James Skidmore
@ Enfermo, necesito agregar su nombre en mi proyecto si uso su código, gracias
vinothp
Hola, estoy usando esta clase en mi aplicación y funciona muy bien. Gracias. Tengo una pregunta. En el ActionSheetDatePickermodo, puede agregar varios botones a la barra de herramientas en la parte superior. ¿Es esto posible también con lo normal ActionSheetStringPicker?
Isuru
¿Puede hacer una selección múltiple?
Kyle Clegg
32

¡Sí! Finalmente lo encuentro.

implemente el siguiente código en su evento de clic de botón, para que aparezca la hoja de acción como se muestra en la imagen de la pregunta.

UIActionSheet *aac = [[UIActionSheet alloc] initWithTitle:@"How many?"
                                             delegate:self
                                    cancelButtonTitle:nil
                               destructiveButtonTitle:nil
                                    otherButtonTitles:nil];

UIDatePicker *theDatePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0.0, 44.0, 0.0, 0.0)];
if(IsDateSelected==YES)
{
    theDatePicker.datePickerMode = UIDatePickerModeDate;
    theDatePicker.maximumDate=[NSDate date];
}else {
    theDatePicker.datePickerMode = UIDatePickerModeTime;
}

self.dtpicker = theDatePicker;
[theDatePicker release];
[dtpicker addTarget:self action:@selector(dateChanged) forControlEvents:UIControlEventValueChanged];

pickerDateToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
pickerDateToolbar.barStyle = UIBarStyleBlackOpaque;
[pickerDateToolbar sizeToFit];

NSMutableArray *barItems = [[NSMutableArray alloc] init];

UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[barItems addObject:flexSpace];

UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(DatePickerDoneClick)];
[barItems addObject:doneBtn];

[pickerDateToolbar setItems:barItems animated:YES];

[aac addSubview:pickerDateToolbar];
[aac addSubview:dtpicker];
[aac showInView:self.view];
[aac setBounds:CGRectMake(0,0,320, 464)];
Sagar R. Kothari
fuente
hola sagar, gracias por el código, evento del botón DatePickerDoneClick, cómo declararlo gracias
Apache
sagar, cómo descartar la hoja de acción emergente y agregar el valor seleccionado en el campo de texto gracias
Apache
[aac dimisswithclickedindex] // algo como este método. (Ver He colocado un botón hecho en la hoja de acción y agregar el objetivo del botón hecho - selector y en el lugar del selector código dimissal.
Sagar R. Kothari
gracias por la respuesta sagar, agrego [aac dispatsWithClickedButtonIndex]; pero recibo una advertencia: 'UIActionSheet' puede no responder a '-dismissWithClickedButtonIndex', entonces creo un nuevo método para el selector 'DatePickerDoneClick' como se muestra a continuación - (void) DatePickerDoneClick {NSLog (@ "Hecho clic"); ratings.text = pickerView objectAtIndex: row]} ¿qué debo hacer? Entonces, cuando hago clic en el botón listo, UIActionSheet (aac) descarta y el campo de texto (ratings.text) se llena con el valor seleccionado del selector, gracias sagar
Apache
1
@Spark es posible dar una idea sobre cómo obtener el texto del selector ... gracias por su publicación + 1
vinothp
10

La excelente solución de Marcio a esta pregunta fue de gran ayuda para mí al agregar subvistas de cualquier tipo a una UIActionSheet.

Por razones que (todavía) no están del todo claras para mí, los límites de UIActionSheet solo se pueden establecer después de que se haya mostrado; las soluciones de sagar y marcio abordan con éxito esto con un mensaje setBounds: CGRectMake (...) que se envía a la hoja de acciones después de que se muestra.

Sin embargo, establecer los límites de UIActionSheet después de que se haya mostrado la hoja crea una transición brusca cuando aparece ActionSheet, donde "aparece" a la vista, y luego solo se desplaza sobre los últimos 40 píxeles aproximadamente.

Al dimensionar un UIPickerView después de agregar subvistas, recomiendo envolver el mensaje setBounds enviado a actionSheet dentro de un bloque de animación. Esto hará que la entrada de actionSheet parezca más suave.

UIActionSheet *actionSheet = [[[UIActionSheet alloc] initWithTitle:nil delegate:nil cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];


// add one or more subviews to the UIActionSheet
// this could be a UIPickerView, or UISegmentedControl buttons, or any other 
// UIView.  Here, let's just assume it's already set up and is called 
// (UIView *)mySubView
[actionSheet addSubview:myView];

// show the actionSheet
[actionSheet showInView:[UIApplication mainWindow]];


// Size the actionSheet with smooth animation
    [UIView beginAnimations:nil context:nil];
    [actionSheet setBounds:CGRectMake(0, 0, 320, 485)];
    [UIView commitAnimations]; 
Junco
fuente
6
Este es un gran consejo. En ios 4 y posteriores, este estilo de animación está "desaconsejado" aunque según los documentos. En iOS 4 o posterior, intente esto en su lugar: UIView animateWithDuration: 0.3f delay: 0 opciones: UIViewAnimationOptionCurveEaseInOut animaciones: ^ {[actionSheet setBounds: CGRectMake (0,0,320,485)];} finalización: NULL];
ceperry
9

Para aquellos tipos que buscan encontrar la función DatePickerDoneClick ... aquí está el código simple para descartar la Hoja de Acción. Obviamente, aac debería ser un ivar (el que va en su archivo .h de implementación)


- (void)DatePickerDoneClick:(id)sender{
    [aac dismissWithClickedButtonIndex:0 animated:YES];
}
Accilies
fuente
9

Realmente no entiendo por qué UIPickerViewestá entrando un UIActionSheet. Esta parece ser una solución desordenada y hacky, que puede romperse en una futura versión de iOS. (He tenido cosas como esta interrupción en una aplicación antes, donde el UIPickerViewno se presentaba en el primer toque y tenía que volver a insertarse, peculiaridades extrañas con UIActionSheet).

Lo que hice fue simplemente implementar un UIPickerViewy luego agregarlo como una subvista a mi vista, y animarlo moviéndose hacia arriba como si se presentara como una hoja de acción.

/// Add the PickerView as a private variable
@interface EMYourClassName ()

@property (nonatomic, strong) UIPickerView *picker;
@property (nonatomic, strong) UIButton *backgroundTapButton;

@end

///
/// This is your action which will present the picker view
///
- (IBAction)showPickerView:(id)sender {

    // Uses the default UIPickerView frame.
    self.picker = [[UIPickerView alloc] initWithFrame:CGRectZero];

    // Place the Pickerview off the bottom of the screen, in the middle set the datasource delegate and indicator
    _picker.center = CGPointMake([[UIScreen mainScreen] bounds].size.width / 2.0, [[UIScreen mainScreen] bounds].size.height + _picker.frame.size.height);
    _picker.dataSource = self;
    _picker.delegate = self;
    _picker.showsSelectionIndicator = YES;

    // Create the toolbar and place it at -44, so it rests "above" the pickerview.
    // Borrowed from @Spark, thanks!
    UIToolbar *pickerDateToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, -44, 320, 44)];
    pickerDateToolbar.barStyle = UIBarStyleBlackTranslucent;
    [pickerDateToolbar sizeToFit];

    NSMutableArray *barItems = [[NSMutableArray alloc] init];

    UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
    [barItems addObject:flexSpace];

    // The action can whatever you want, but it should dimiss the picker.
    UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(backgroundTapped:)];
    [barItems addObject:doneBtn];

    [pickerDateToolbar setItems:barItems animated:YES];
    [_picker addSubview:pickerDateToolbar];

    // If you have a UITabBarController, you should add the picker as a subview of it
    // so it appears to go over the tabbar, not under it. Otherwise you can add it to 
    // self.view
    [self.tabBarController.view addSubview:_picker];

    // Animate it moving up
    [UIView animateWithDuration:.3 animations:^{
        [_picker setCenter:CGPointMake(160, [[UIScreen mainScreen] bounds].size.height - 148)]; //148 seems to put it in place just right.
    } completion:^(BOOL finished) {
        // When done, place an invisible button on the view behind the picker, so if the
        // user "taps to dismiss" the picker, it will go away. Good user experience!
        self.backgroundTapButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _backgroundTapButton.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
        [_backgroundTapButton addTarget:self action:@selector(backgroundTapped:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:_backgroundTapButton];
    }];

}

// And lastly, the method to hide the picker.  You should handle the picker changing
// in a method with UIControlEventValueChanged on the pickerview.
- (void)backgroundTapped:(id)sender {

    [UIView animateWithDuration:.3 animations:^{
        _picker.center = CGPointMake(160, [[UIScreen mainScreen] bounds].size.height + _picker.frame.size.height);
    } completion:^(BOOL finished) {
        [_picker removeFromSuperview];
        self.picker = nil;
        [self.backgroundTapButton removeFromSuperview];
        self.backgroundTapButton = nil;
    }];
}
Ethan Mick
fuente
Esto es un gran agradecimiento. ¿Has encontrado que esto funciona bien con iOS 7?
avance
1
+1 para una gran vista de selector simple que se anima a la pantalla. Pero tienes un error en el código. La vista backgroundTapButton se genera en la parte superior de la vista del selector y allí bloquea la vista del selector de la interacción del usuario. lo que realmente quieres hacer es crear esta vista en el espacio restante de la pantalla que la vista del selector aún no llena ... (no puedes hacer una vista detrás de la vista del selector como quieres)
aZtraL-EnForceR
7

Para agregar a la increíble solución de marcio, dismissActionSheet:se puede implementar de la siguiente manera.

  1. Agrega un objeto ActionSheet a tu archivo .h, sintetízalo y haz referencia a él en tu archivo .m.
  2. Agrega este método a tu código.

    - (void)dismissActionSheet:(id)sender{
      [_actionSheet dismissWithClickedButtonIndex:0 animated:YES];
      [_myButton setTitle:@"new title"]; //set to selected text if wanted
    }
Kyle Clegg
fuente
3

Creo que esta es la mejor forma de hacerlo.

ActionSheetPicker-3.0

Es más o menos lo que todos sugieren, pero usa bloques, ¡lo cual es un buen toque!

Tony
fuente
Bueno, si lees arriba, verás que esta clase ActionSheetPicker surgió en primer lugar, debido a este hilo. Así que sí, esa es la mejor manera, gracias a la solución aceptada (¬_¬). stackoverflow.com/questions/1262574/…
OpenUserX03
2
Parece que Apple está a punto de comenzar a hacer cumplir su regla de que las hojas de acción no deben subclasificarse, ya que recibo este mensaje de error: "<Error>: CGContextSetFillColorWithColor: contexto no válido 0x0. Este es un error grave. Esta aplicación o una biblioteca que usa , utiliza un contexto no válido y, por lo tanto, contribuye a una degradación general de la estabilidad y confiabilidad del sistema. Este aviso es una cortesía: solucione este problema. Se convertirá en un error fatal en una próxima actualización ".
Stuart P.
@StuartP. vea mi respuesta
Kyle Clegg
2

Desde iOS 8, no puede, no funciona porque Apple cambió la implementación interna de UIActionSheet. Consulte la documentación de Apple :

Notas de subclasificación

UIActionSheet no está diseñado para ser subclasificado, ni debe agregar vistas a su jerarquía . Si necesita presentar una hoja con más personalización que la proporcionada por la API UIActionSheet, puede crear la suya propia y presentarla de manera modal con presentViewController: animated: complete :.

Mutawe
fuente
1

Me gustó el enfoque adoptado por Wayfarer y flexaddicted, pero descubrí (como aZtral) que no funcionaba ya que backgroundTapButton era el único elemento que respondía a la interacción del usuario. Esto me llevó a poner sus tres subvistas: _picker, _pickerToolbar y backgroundTapButton dentro de una vista contenedora (emergente) que luego se animó dentro y fuera de la pantalla. También necesitaba un botón Cancelar en _pickerToolbar. Aquí están los elementos de código relevantes para la vista emergente (necesita proporcionar su propia fuente de datos de selector y métodos delegados).

#define DURATION            0.4
#define PICKERHEIGHT        162.0
#define TOOLBARHEIGHT       44.0

@interface ViewController ()
@property (nonatomic, strong) UIView        *popup;
@property (nonatomic, strong) UIPickerView  *picker;
@property (nonatomic, strong) UIToolbar     *pickerToolbar;
@property (nonatomic, strong) UIButton      *backgroundTapButton;
@end

-(void)viewDidLoad {
    // These are ivars for convenience
    rect = self.view.bounds;
    topNavHeight = self.navigationController.navigationBar.frame.size.height;
    bottomNavHeight = self.navigationController.toolbar.frame.size.height;
    navHeights = topNavHeight + bottomNavHeight;
}

-(void)showPickerView:(id)sender {
    [self createPicker];
    [self createToolbar];

    // create view container
    _popup = [[UIView alloc] initWithFrame:CGRectMake(0.0, topNavHeight, rect.size.width, rect.size.height - navHeights)];
    // Initially put the centre off the bottom of the screen
    _popup.center = CGPointMake(rect.size.width / 2.0, rect.size.height + _popup.frame.size.height / 2.0);
    [_popup addSubview:_picker];
    [_popup insertSubview:_pickerToolbar aboveSubview:_picker];

    // Animate it moving up
    // This seems to work though I am not sure why I need to take off the topNavHeight
    CGFloat vertCentre = (_popup.frame.size.height - topNavHeight) / 2.0;

    [UIView animateWithDuration:DURATION animations:^{
        // move it to a new point in the middle of the screen
        [_popup setCenter:CGPointMake(rect.size.width / 2.0, vertCentre)];
    } completion:^(BOOL finished) {
        // When done, place an invisible 'button' on the view behind the picker,
        // so if the user "taps to dismiss" the picker, it will go away
        self.backgroundTapButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _backgroundTapButton.frame = CGRectMake(0, 0, _popup.frame.size.width, _popup.frame.size.height);
        [_backgroundTapButton addTarget:self action:@selector(doneAction:) forControlEvents:UIControlEventTouchUpInside];
        [_popup insertSubview:_backgroundTapButton belowSubview:_picker];
        [self.view addSubview:_popup];
    }];
}

-(void)createPicker {
    // To use the default UIPickerView frame of 216px set frame to CGRectZero, but we want the 162px height one
    CGFloat     pickerStartY = rect.size.height - navHeights - PICKERHEIGHT;
    self.picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0.0, pickerStartY, rect.size.width, PICKERHEIGHT)];
    _picker.dataSource = self;
    _picker.delegate = self;
    _picker.showsSelectionIndicator = YES;
    // Otherwise you can see the view underneath the picker
    _picker.backgroundColor = [UIColor whiteColor];
    _picker.alpha = 1.0f;
}

-(void)createToolbar {
    CGFloat     toolbarStartY = rect.size.height - navHeights - PICKERHEIGHT - TOOLBARHEIGHT;
    _pickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, toolbarStartY, rect.size.width, TOOLBARHEIGHT)];
    [_pickerToolbar sizeToFit];

    NSMutableArray *barItems = [[NSMutableArray alloc] init];
    UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAction:)];
    [barItems addObject:cancelButton];

    // Flexible space to make the done button go on the right
    UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
    [barItems addObject:flexSpace];

    // The done button
    UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneAction:)];
    [barItems addObject:doneButton];
    [_pickerToolbar setItems:barItems animated:YES];
}

// The method to process the picker, if we have hit done button
- (void)doneAction:(id)sender {
    [UIView animateWithDuration:DURATION animations:^{
        _popup.center = CGPointMake(rect.size.width / 2.0, rect.size.height + _popup.frame.size.height / 2.0);
    } completion:^(BOOL finished) { [self destroyPopup]; }];
    // Do something to process the returned value from your picker
}

// The method to process the picker, if we have hit cancel button
- (void)cancelAction:(id)sender {
    [UIView animateWithDuration:DURATION animations:^{
        _popup.center = CGPointMake(rect.size.width / 2.0, rect.size.height + _popup.frame.size.height / 2.0);
    } completion:^(BOOL finished) { [self destroyPopup]; }];
}

-(void)destroyPopup {
    [_picker removeFromSuperview];
    self.picker = nil;
    [_pickerToolbar removeFromSuperview];
    self.pickerToolbar = nil;
    [self.backgroundTapButton removeFromSuperview];
    self.backgroundTapButton = nil;
    [_popup removeFromSuperview];
    self.popup = nil;
}
VaughanR
fuente