iphone - example - uipickerview done button
Agregue UIPickerView & a Button en la hoja de acción-¿Cómo? (11)
¡Sip! Finalmente lo encuentro.
implemente el siguiente código en su evento de clic de botón, para desplegar la hoja de acción como figura 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)];
Mi aplicación requiere que se agreguen las siguientes cosas en una hoja de acción.
- UIToolbar
- Botón en UIToolbar
- Control UIPicker
He incluido una imagen para entender mis requisitos.
¿Podría explicar por favor cómo se puede implementar esto?
Aunque esta pregunta es antigua, mencionaré rápidamente que he creado una clase ActionSheetPicker con una función de conveniencia, por lo que puede generar una Hoja de Acción con un UIPickerView en una línea. Se basa en el código de las respuestas a esta pregunta.
Editar: ahora también es compatible con el uso de un DatePicker y DistancePicker.
UPD:
Esta versión está en desuso: use ActionSheetPicker-3.0 lugar.
Creo que esta es la mejor manera de hacerlo.
Es más o menos lo que todos sugieren, pero usa bloques, ¡lo cual es un buen toque!
Desde iOS 8, no se puede, no funciona porque Apple cambió la implementación interna de UIActionSheet
. Por favor, consulte la documentación de Apple :
Subclassing Notes
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 que proporciona la API UIActionSheet, puede crear la suya propia y presentarla de manera modal con presentViewController: animated: completion :.
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 aún no me resultan del todo claras, los límites de UIActionSheet solo se pueden establecer después de que se hayan visualizado; Tanto las soluciones de sagar como de 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, al establecer los límites de UIActionSheet después de que se haya mostrado la hoja, se crea una transición vacilante cuando aparece la hoja de cálculo, donde se "muestra" en la vista, y luego solo se desplaza en 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 se vea 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];
Me gustó el enfoque adoptado por Wayfarer y flexaddicted, pero encontré (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 que contiene (popup) que luego se animaba dentro y fuera de la pantalla. También necesitaba un botón Cancelar en _pickerToolbar. Estos son los elementos de código relevantes para la vista emergente (debe proporcionar su propio origen de datos del selector y delegar métodos).
#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;
}
Para agregar a la asombrosa solución de marcio, dismissActionSheet:
se puede implementar de la siguiente manera.
- Agregue un objeto ActionSheet a su archivo .h, sintetícelo y haga referencia a él en su archivo .m.
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 }
Para aquellos tipos que están tratando de 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 está en su archivo de implmentación .h)
- (void)DatePickerDoneClick:(id)sender{
[aac dismissWithClickedButtonIndex:0 animated:YES];
}
Realmente no entiendo por qué UIPickerView
entra dentro de UIActionSheet
. Esto parece ser una solución desordenada y hacky, que se puede romper en una futura versión de iOS. (He tenido cosas como esta en una aplicación anterior, donde el UIPickerView
no se presentaba en el primer toque y tenía que ser descatalogado: rarezas extrañas con UIActionSheet
).
Lo que hice fue simplemente implementar un UIPickerView
y luego agregarlo como una subvista a mi vista, y animarlo moviéndome 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;
}];
}
Una solución más:
sin barra de herramientas, pero 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)];
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 conducir a graves errores de contexto no válidos 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. Esta es una forma que acabo de implementar en un proyecto actual. Es bueno porque puedo reutilizarlo entre 5 o 6 pantallas diferentes donde puedo seleccionar a todos los usuarios de una lista de opciones.
- Cree una nueva subclase
SimpleTableViewController
,SimpleTableViewController
. - Cree un UITableViewController en su guión gráfico (integrado en un controlador de navegación) y establezca su clase personalizada en SimpleTableViewController.
- Dele al controlador de navegación de SimpleTableViewController una ID Storyboard de "SimpleTableVC".
- En SimpleTableViewController.h, cree una propiedad NSArray que represente los datos en la tabla.
- También en SimpleTableViewController.h, cree un protocolo
SimpleTableViewControllerDelegate
con un método requeridoitemSelectedatRow:
y una propiedad débil llamada delegado de tipoid<SimpleTableViewControllerDelegate>
. Así es como volveremos a pasar la selección al controlador principal. - En SimpleTableViewController.m, implemente los métodos fuente y delegar datos de tabla, llamando a
itemSelectedatRow:
entableView:didSelectRowAtIndexPath:
Este enfoque tiene el beneficio adicional de ser bastante reutilizable. Para usar, importe la clase SimpleTableViewController en su ViewController.h, cumpla con SimpleTableViewDelegate e implemente el método itemSelectedAtRow:
. Luego, para abrir el modal, simplemente instale un nuevo SimpleTableViewController, establezca los datos de la tabla y delegue, y preséntelos.
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];
Creé un ejemplo simple y lo publiqué en github .
También vea Mostrar hoja de acciones que causa errores de contexto de contexto CGContext .