Ajouter UIPickerView et un bouton dans la feuille d'action - Comment?

120

Mon application nécessite l'ajout des éléments suivants dans une feuille d'action.

  • UIToolbar
  • Bouton sur UIToolbar
  • Contrôle UIPicker

J'ai inclus une image pour comprendre mes besoins.

texte alternatif

Pouvez-vous expliquer comment cela peut être mis en œuvre?

Sagar R. Kothari
la source
3
Merci d'avoir posté ce problème intéressant!
Tuyen Nguyen
Au lieu de jouer avec la gestion de l'actionSheet, pickerView, etc., je recommanderais d'utiliser EAActionSheetPicker . Cela nettoie vraiment beaucoup votre code.
ebandersen
@eckyzero Malheureusement, l'EAActionSheetPicker semble être cassé dans iOS 7, il y a beaucoup d'erreurs commençant toutes par "contexte invalide 0x0.".
Magnus
bien sûr, c'est trop beau pour être vrai ... fook me, y ios b so
hard
EAActionSheetPicker ne fonctionne plus.
osxdirk

Réponses:

25

Mise à jour pour iOS 7

Documents Apple pour UIActionSheet :UIActionSheet is not designed to be subclassed, nor should you add views to its hierarchy

Je déconseille d'essayer de personnaliser le contenu d'une feuille d'action, car cela peut entraîner de graves erreurs de contexte invalides dans iOS 7. Je viens de passer quelques heures à résoudre ce problème et j'ai finalement décidé d'adopter une approche différente. J'ai remplacé l'appel pour afficher la feuille d'action par un contrôleur de vue modale contenant une vue de table simple.

Il y a plusieurs façons d'accomplir ceci. Voici une façon que je viens de mettre en œuvre dans un projet en cours. C'est sympa car je peux le réutiliser entre 5 ou 6 écrans différents où j'ai tous les utilisateurs à choisir parmi une liste d'options.

  1. Créer une nouvelle sous - classe de UITableViewController, SimpleTableViewController.
  2. Créez un UITableViewController dans votre storyboard (intégré dans un contrôleur de navigation) et définissez sa classe personnalisée sur SimpleTableViewController.
  3. Donnez au contrôleur de navigation pour SimpleTableViewController un ID de Storyboard de "SimpleTableVC".
  4. Dans SimpleTableViewController.h, créez une propriété NSArray qui représentera les données de la table.
  5. Toujours dans SimpleTableViewController.h, créez un protocole SimpleTableViewControllerDelegateavec une méthode requiseitemSelectedatRow: et une propriété faible appelée délégué de type id<SimpleTableViewControllerDelegate>. C'est ainsi que nous allons renvoyer la sélection au contrôleur parent.
  6. En SimpleTableViewController.m, mettre en œuvre la source de données et méthodes tableview délégué, appelant itemSelectedatRow:àtableView:didSelectRowAtIndexPath: .

Cette approche a l'avantage supplémentaire d'être assez réutilisable. Pour l'utiliser, importez la classe SimpleTableViewController dans votre ViewController.h, conformez-vous à SimpleTableViewDelegate et implémentez la itemSelectedAtRow:méthode. Ensuite, pour ouvrir le modal, instanciez simplement un nouveau SimpleTableViewController, définissez les données de la table et déléguez-le, puis présentez-le.

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];

Je crée un exemple simple et posté sur github .

Voir également Afficher la feuille d'actions provoque des erreurs de contexte CGContext non valides .

Kyle Clegg
la source
2
Ahh iOS 7! Vous avez ruiné tout ce qui était développé jusqu'à présent. :(
Sagar R. Kothari
@Kyle Pouvez-vous élargir votre réponse en expliquant quelle a été votre approche? Merci
Daniel Sanchez
1
@DanielSanchez Mise à jour avec une suggestion alternative et un exemple de code.
Kyle Clegg
3
Une solution beaucoup plus élégante à mon humble avis est de définir la vue d'entrée de votre champ de texte sur UIPickerView et son accessoire sur UIToolbar. Vous pouvez consulter le projet QuickDialog pour un exemple.
Steve Moser
111

Encore une solution:

  • pas de barre d'outils mais un contrôle segmenté (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
la source
1
J'obtiens UIApplication peut ne pas répondre à l'avertissement de mainwindow et l'application se termine.
Mahesh Babu
J'essaie d'utiliser UIPickerView mais je n'ai pas pu l'intégrer dans mon application. Je veux que UIPickerView soit affiché en cliquant sur un bouton dont l'action est enregistrée dans MYViewController: UIViewController. Dans la méthode d'action, j'ai mis le code ci-dessus de pickerview. que dois-je faire d'autre? Veuillez aider.
Namratha
1
En fait, fredrik, vous devriez utiliser [[UIApplication sharedApplication] keyWindow]. Source modifiée pour changer cela.
Eric Goldberg
3
Van Du Tran - (void) ignoreActionSheet: (UISegmentedControl *) expéditeur {UIActionSheet actionSheet = (UIActionSheet ) [ superview de l' expéditeur]; [actionSheet rejetWithClickedButtonIndex: 0 animé: OUI]; }
WINSergey
1
Attention: cela provoque de nombreuses erreurs CGContext dans iOS 7. Voir stackoverflow.com/questions/19129091/...
Kyle Clegg
75

Même si cette question est ancienne, je mentionnerai rapidement que j'ai jeté une classe ActionSheetPicker avec une fonction de commodité, afin que vous puissiez générer une ActionSheet avec un UIPickerView en une seule ligne. Il est basé sur le code des réponses à cette question.

Edit: Il prend désormais également en charge l'utilisation d'un DatePicker et DistancePicker.


UPD:

Cette version est obsolète: utilisez plutôt ActionSheetPicker-3.0 .

animation

TimCinel
la source
1
Super ... en utilisant cela dans mon application.
Michael Morrison
Impressionnant. Utilisant ceci maintenant.
James Skidmore
@Sick dois-je ajouter votre nom dans mon projet si j'utilise votre code, merci
vinothp
Bonjour, j'utilise cette classe dans mon application et cela fonctionne très bien. Merci. J'ai une question. Dans le ActionSheetDatePickermode, vous pouvez ajouter plusieurs boutons à la barre d'outils en haut. Est-ce possible avec juste ActionSheetStringPickeraussi normal ?
Isuru
Peut-il faire une sélection multiple?
Kyle Clegg
32

Ouais! Je le trouve enfin.

implémentez le code suivant sur votre événement de clic de bouton, pour faire apparaître la feuille d'action comme indiqué dans l'image de la question.

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
la source
salut sagar, merci pour le code, l'événement du bouton DatePickerDoneClick, comment le déclarer merci
Apache
sagar, comment ignorer la feuille d'action contextuelle et ajouter la valeur sélectionnée dans le champ de texte merci
Apache
[aac dimisswithclickedindex] // quelque chose comme cette méthode. (voir j'ai placé un bouton terminé dans la feuille d'action et ajouter la cible du bouton terminé - sélecteur et dans le sélecteur placer le code dimissal.
Sagar R. Kothari
merci pour la réponse sagar, j'ajoute [aac licencierWithClickedButtonIndex]; mais je reçois un avertissement: 'UIActionSheet' peut ne pas répondre à '-dismissWithClickedButtonIndex' alors je crée une nouvelle méthode pour le sélecteur 'DatePickerDoneClick' comme ci-dessous - (void) DatePickerDoneClick {NSLog (@ "Done clicked"); notes.text = pickerView objectAtIndex: row]} que dois-je faire, alors quand je clique sur le bouton terminé, UIActionSheet (aac) rejette et textfield (notes.text) soit rempli avec la valeur sélectionnée du sélecteur merci sagar
Apache
1
@Spark est-il possible de donner une idée sur la façon d'obtenir le texte du sélecteur .. merci pour votre message + 1
vinothp
10

L'excellente solution de Marcio à cette question m'a été d'une grande aide en ajoutant des sous-vues de tout type à une feuille UIActionSheet.

Pour des raisons qui ne sont pas (encore) tout à fait claires pour moi, les limites de la feuille UIActionSheet ne peuvent être définies qu'après son affichage; les solutions de sagar et de marcio résolvent ce problème avec un message setBounds: CGRectMake (...) envoyé à la feuille d'actions après son .

Toutefois, la définition des limites UIActionSheet une fois que la feuille a été affichée crée une transition instable lorsque l'ActionSheet apparaît, où il «apparaît» dans la vue, puis défile uniquement sur les 40 derniers pixels environ.

Lors du dimensionnement d'un UIPickerView après l'ajout de sous-vues, je recommande d'encapsuler le message setBounds envoyé à la feuille d'action dans un bloc d'animation. Cela rendra l'entrée de la feuille d'action plus fluide.

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]; 
roseau
la source
6
C'est un bon conseil. Sous iOS 4 et plus tard, ce style d'animation est "déconseillé" bien que selon la documentation. Sous iOS 4 ou version ultérieure, essayez plutôt ceci: UIView animateWithDuration: 0.3f delay: 0 options: UIViewAnimationOptionCurveEaseInOut animations: ^ {[actionSheet setBounds: CGRectMake (0,0,320,485)];} completion: NULL];
ceperry
9

Pour ceux qui essaient de trouver la fonction DatePickerDoneClick ... voici le code simple pour ignorer la feuille d'action. Évidemment, aac devrait être un ivar (celui qui va dans votre fichier .h d'implémentation)


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

Je ne comprends pas vraiment pourquoi le UIPickerViewva à l'intérieur d'un UIActionSheet. Cela semble être une solution désordonnée et piratée, qui peut être cassée dans une future version iOS. (J'ai déjà eu des choses comme cette pause dans une application auparavant, où le UIPickerViewn'était pas présenté au premier robinet et devait être retappé - bizarreries étranges avec leUIActionSheet ).

Ce que j'ai fait, c'est simplement implémenter un UIPickerView, puis l'ajouter en tant que sous-vue à ma vue, et l'animer en se déplaçant vers le haut comme s'il était présenté comme une feuille d'action.

/// 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
la source
C'est un grand merci. Avez-vous trouvé que cela fonctionne bien avec iOS 7?
avance
1
+1 pour une vue de sélection simple et animée à l'écran. Mais vous avez une erreur dans le code. La vue backgroundTapButton apparaît en haut de la vue du sélecteur et là pour bloquer la vue du sélecteur de l'interaction de l'utilisateur. ce que vous voulez vraiment faire était de créer cette vue sur l'espace d'écran restant que la vue du sélecteur ne remplit pas déjà ... (vous ne pouvez pas faire une vue derrière la vue du sélecteur comme vous l'entendez)
aZtraL-EnForceR
7

Pour ajouter à la solution impressionnante de marcio, dismissActionSheet:peut être implémentée comme suit.

  1. Ajoutez un objet ActionSheet à votre fichier .h, synthétisez-le et référencez-le dans votre fichier .m.
  2. Ajoutez cette méthode à votre code.

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

Je pense que c'est la meilleure façon de le faire.

ActionSheetPicker-3.0

C'est à peu près ce que tout le monde suggère, mais utilise des blocs, ce qui est une bonne idée!

Tony
la source
Eh bien, si vous lisez ci-dessus, vous verrez que cette classe ActionSheetPicker est née en premier lieu, en raison de ce fil. Alors oui, c'est le meilleur moyen, grâce à la solution acceptée (¬_¬). stackoverflow.com/questions/1262574/…
OpenUserX03
2
On dirait qu'Apple est sur le point de commencer à appliquer sa règle selon laquelle les feuilles d'action ne doivent pas être sous-classées car j'obtiens ce message d'erreur - "<Error>: CGContextSetFillColorWithColor: contexte non valide 0x0. Il s'agit d'une erreur grave. Cette application ou une bibliothèque qu'elle utilise , utilise un contexte non valide et contribue ainsi à une dégradation globale de la stabilité et de la fiabilité du système. Cet avis est une courtoisie: veuillez corriger ce problème. Cela deviendra une erreur fatale dans une prochaine mise à jour. "
Stuart P.
@StuartP. voir ma réponse
Kyle Clegg
2

Depuis iOS 8, vous ne pouvez pas, cela ne fonctionne pas car Apple a modifié l'implémentation interne de UIActionSheet. Veuillez consulter la documentation Apple :

Notes de sous-classification

UIActionSheet n'est pas conçu pour être sous-classé et vous ne devez pas non plus ajouter des vues à sa hiérarchie . Si vous avez besoin de présenter une feuille avec plus de personnalisation que celle fournie par l'API UIActionSheet, vous pouvez créer la vôtre et la présenter de manière modale avec presentViewController: animated: completion :.

Mutawe
la source
1

J'ai aimé l'approche adoptée par Wayfarer et flexaddicted, mais j'ai trouvé (comme aZtral) que cela ne fonctionnait pas car le backgroundTapButton était le seul élément qui répondait à l'interaction de l'utilisateur. Cela m'a conduit à mettre ses trois sous-vues: _picker, _pickerToolbar et backgroundTapButton dans une vue contenant (popup) qui a ensuite été animée sur et en dehors de l'écran. J'avais également besoin d'un bouton Annuler sur la _pickerToolbar. Voici les éléments de code pertinents pour la vue contextuelle (vous devez fournir votre propre source de données de sélection et déléguer des méthodes).

#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
la source