UIPickerView que se parece a UIDatePicker pero con segundos

Estoy haciendo una aplicación de temporizador y necesito mostrar horas, minutos y segundos para el usuario. Intenté usar UIDatePicker pero muestra solo horas y minutos como selecciones. No segundos. Después de hacer un poco de investigación en línea, descubrí que no hay forma de obtener segundos en UIDatePicker y tuve que escribir mi propio UIPickerView desde cero.

Así que mi pregunta es, ¿hay código de muestra para eso, es decir, alguien escribió un CustomUIPickerView durante horas, minutos y segundos que puedo incorporar a mi proyecto? UIDatePicker tiene una buena superposición de texto de Hours & Mins que se mantiene mientras el usuario gira los diales. Sería bueno si alguien agrega eso en su selector personalizado también. Prefiero no escribir un UIPickerView personalizado desde cero si no es necesario. Gracias.

Muy bien amigos, aquí está el código para obtener horas / minutos / segundos en su UIPickerView . Puede agregar 3 tags y colocarlas estratégicamente en el selector. También adjunté una foto.

Si te gusta la respuesta, ¡califica! Los buenos desarrolladores comparten el conocimiento, no lo ocultan como sugirieron algunas personas. Diviértete codificando!

enter image description here

En su archivo .h de encabezado pon esto

 @interface v1AddTableViewController : UITableViewController { IBOutlet UIPickerView *pickerView; NSMutableArray *hoursArray; NSMutableArray *minsArray; NSMutableArray *secsArray; NSTimeInterval interval; } @property(retain, nonatomic) UIPickerView *pickerView; @property(retain, nonatomic) NSMutableArray *hoursArray; @property(retain, nonatomic) NSMutableArray *minsArray; @property(retain, nonatomic) NSMutableArray *secsArray; 

en tu archivo .m pon esto

 @synthesize pickerView; @synthesize hoursArray; @synthesize minsArray; @synthesize secsArray; @synthesize interval; - (void)viewDidLoad { [super viewDidLoad]; //initialize arrays hoursArray = [[NSMutableArray alloc] init]; minsArray = [[NSMutableArray alloc] init]; secsArray = [[NSMutableArray alloc] init]; NSString *strVal = [[NSString alloc] init]; for(int i=0; i<61; i++) { strVal = [NSString stringWithFormat:@"%d", i]; //NSLog(@"strVal: %@", strVal); //Create array with 0-12 hours if (i < 13) { [hoursArray addObject:strVal]; } //create arrays with 0-60 secs/mins [minsArray addObject:strVal]; [secsArray addObject:strVal]; } NSLog(@"[hoursArray count]: %d", [hoursArray count]); NSLog(@"[minsArray count]: %d", [minsArray count]); NSLog(@"[secsArray count]: %d", [secsArray count]); } //Method to define how many columns/dials to show - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { return 3; } // Method to define the numberOfRows in a component using the array. - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent :(NSInteger)component { if (component==0) { return [hoursArray count]; } else if (component==1) { return [minsArray count]; } else { return [secsArray count]; } } // Method to show the title of row for a component. - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { switch (component) { case 0: return [hoursArray objectAtIndex:row]; break; case 1: return [minsArray objectAtIndex:row]; break; case 2: return [secsArray objectAtIndex:row]; break; } return nil; } -(IBAction)calculateTimeFromPicker { NSString *hoursStr = [NSString stringWithFormat:@"%@",[hoursArray objectAtIndex:[pickerView selectedRowInComponent:0]]]; NSString *minsStr = [NSString stringWithFormat:@"%@",[minsArray objectAtIndex:[pickerView selectedRowInComponent:1]]]; NSString *secsStr = [NSString stringWithFormat:@"%@",[secsArray objectAtIndex:[pickerView selectedRowInComponent:2]]]; int hoursInt = [hoursStr intValue]; int minsInt = [minsStr intValue]; int secsInt = [secsStr intValue]; interval = secsInt + (minsInt*60) + (hoursInt*3600); NSLog(@"hours: %d ... mins: %d .... sec: %d .... interval: %f", hoursInt, minsInt, secsInt, interval); NSString *totalTimeStr = [NSString stringWithFormat:@"%f",interval]; } 

A diferencia de la solución aceptada anteriormente, se me ocurrió algo un poco menos terrible. Definitivamente no es perfecto, pero tiene el efecto deseado (solo lo he probado en iOS 7 en iPhone, solo funciona en vertical como está escrito). Las ediciones para mejorar son bienvenidas. Código de visualización relevante a continuación:

 // assumes you conform to UIPickerViewDelegate and UIPickerViewDataSource in your .h - (void)viewDidLoad { [super viewDidLoad]; // assumes global UIPickerView declared. Move the frame to wherever you want it picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 100, self.view.frame.size.width, 200)]; picker.dataSource = self; picker.delegate = self; UILabel *hourLabel = [[UILabel alloc] initWithFrame:CGRectMake(42, picker.frame.size.height / 2 - 15, 75, 30)]; hourLabel.text = @"hour"; [picker addSubview:hourLabel]; UILabel *minsLabel = [[UILabel alloc] initWithFrame:CGRectMake(42 + (picker.frame.size.width / 3), picker.frame.size.height / 2 - 15, 75, 30)]; minsLabel.text = @"min"; [picker addSubview:minsLabel]; UILabel *secsLabel = [[UILabel alloc] initWithFrame:CGRectMake(42 + ((picker.frame.size.width / 3) * 2), picker.frame.size.height / 2 - 15, 75, 30)]; secsLabel.text = @"sec"; [picker addSubview:secsLabel]; [self.view addSubview:picker]; } - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { return 3; } - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { if(component == 0) return 24; return 60; } - (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component { return 30; } - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view { UILabel *columnView = [[UILabel alloc] initWithFrame:CGRectMake(35, 0, self.view.frame.size.width/3 - 35, 30)]; columnView.text = [NSString stringWithFormat:@"%lu", (long) row]; columnView.textAlignment = NSTextAlignmentLeft; return columnView; } 

Y el resultado:

Selector H / M / S

implementación rápida

 class TimePickerView: UIPickerView, UIPickerViewDataSource, UIPickerViewDelegate { var hour:Int = 0 var minute:Int = 0 override init() { super.init() self.setup() } required internal init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.setup() } override init(frame: CGRect) { super.init(frame: frame) self.setup() } func setup(){ self.delegate = self self.dataSource = self /*let height = CGFloat(20) let offsetX = self.frame.size.width / 3 let offsetY = self.frame.size.height/2 - height/2 let marginX = CGFloat(42) let width = offsetX - marginX let hourLabel = UILabel(frame: CGRectMake(marginX, offsetY, width, height)) hourLabel.text = "hour" self.addSubview(hourLabel) let minsLabel = UILabel(frame: CGRectMake(marginX + offsetX, offsetY, width, height)) minsLabel.text = "min" self.addSubview(minsLabel)*/ } func getDate() -> NSDate{ let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "HH:mm" let date = dateFormatter.dateFromString(String(format: "%02d", self.hour) + ":" + String(format: "%02d", self.minute)) return date! } func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int { return 2 } func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { switch component { case 0: self.hour = row case 1: self.minute = row default: println("No component with number \(component)") } } func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { if component == 0 { return 24 } return 60 } func pickerView(pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { return 30 } func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView!) -> UIView { if (view != nil) { (view as UILabel).text = String(format:"%02lu", row) return view } let columnView = UILabel(frame: CGRectMake(35, 0, self.frame.size.width/3 - 35, 30)) columnView.text = String(format:"%02lu", row) columnView.textAlignment = NSTextAlignment.Center return columnView } } 

No me UILabel las respuestas que no dependieron de agregar UILabel como subvistas a UIPickerView , porque la encoding de los marcos se romperá si iOS decide cambiar la apariencia algún día.

Un truco simple es tener las tags de hora / min / seg como componentes con 1 fila .

Las tags de hora / min / seg estarán más lejos de sus valores, pero está bien. La mayoría de los usuarios ni siquiera lo notarán. Ver por ti mismo:

enter image description here

Hice algunas mejoras ligeras a la versión de @ Stonz2. También hubo un error con el cálculo de la posición y de las tags.

Aquí el archivo .h

 @interface CustomUIDatePicker : UIPickerView  @property NSInteger hours; @property NSInteger mins; @property NSInteger secs; -(NSInteger) getPickerTimeInMS; -(void) initialize; @end 

Y aquí el archivo .m con las mejoras

 @implementation CustomUIDatePicker -(instancetype)init { self = [super init]; [self initialize]; return self; } -(id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; [self initialize]; return self; } -(instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; [self initialize]; return self; } -(void) initialize { self.delegate = self; self.dataSource = self; int height = 20; int offsetX = self.frame.size.width / 3; int offsetY = self.frame.size.height / 2 - height / 2; int marginX = 42; int width = offsetX - marginX; UILabel *hourLabel = [[UILabel alloc] initWithFrame:CGRectMake(marginX, offsetY, width, height)]; hourLabel.text = @"hour"; [self addSubview:hourLabel]; UILabel *minsLabel = [[UILabel alloc] initWithFrame:CGRectMake(marginX + offsetX, offsetY, width, height)]; minsLabel.text = @"min"; [self addSubview:minsLabel]; UILabel *secsLabel = [[UILabel alloc] initWithFrame:CGRectMake(marginX + offsetX * 2, offsetY, width, height)]; secsLabel.text = @"sec"; [self addSubview:secsLabel]; } -(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { if (component == 0) { self.hours = row; } else if (component == 1) { self.mins = row; } else if (component == 2) { self.secs = row; } } -(NSInteger)getPickerTimeInMS { return (self.hours * 60 * 60 + self.mins * 60 + self.secs) * 1000; } -(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { return 3; } -(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { if(component == 0) return 24; return 60; } - (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component { return 30; } -(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view { if (view != nil) { ((UILabel*)view).text = [NSString stringWithFormat:@"%lu", row]; return view; } UILabel *columnView = [[UILabel alloc] initWithFrame:CGRectMake(35, 0, self.frame.size.width/3 - 35, 30)]; columnView.text = [NSString stringWithFormat:@"%lu", row]; columnView.textAlignment = NSTextAlignmentLeft; return columnView; } @end 

Una opción es usar ActionSheetPicker

https://github.com/skywinder/ActionSheetPicker-3.0

y utilice ActionSheetMultipleStringPicker. Puede seguir el código de ejemplo en los documentos de ActionSheetPicker.

Tengo una solución simple aquí, puedes implementar 3 métodos de UIPickerViewDataSource para UIPickerView

  - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{ return 5; } - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{ switch (component) { case 0: return 24; break; case 1: case 3: return 1; break; case 2: case 4: return 60; break; default: return 1; break; } } - (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { switch (component) { case 1: case 3: return @":"; break; case 0: case 2: case 4: return [NSString stringWithFormat:@"%lu", (long) row]; break; default: return @""; break; } } 

enter image description here

Aquí hay otra solución para la superposición “hora / min / seg”. Utiliza el pickerView(UIPickerView, didSelectRow: Int, inComponent: Int) del delegado de la vista del selector para actualizar la etiqueta de la vista.

De esta manera, funcionará sin importar la orientación de la pantalla. Todavía no es óptimo, pero es bastante sencillo.

Ejemplo de UIPickerView

 func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { let label = UILabel() label.text = String(row) label.textAlignment = .center return label } func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { if let label = pickerView.view(forRow: row, forComponent: component) as? UILabel { if component == 0, row > 1 { label.text = String(row) + " hours" } else if component == 0 { label.text = String(row) + " hour" } else if component == 1 { label.text = String(row) + " min" } else if component == 2 { label.text = String(row) + " sec" } } } 

Para que la superposición aparezca cuando se muestra la vista del seleccionador, las filas deben seleccionarse mediante progtwigción …

 func selectPickerViewRows() { pickerView.selectRow(0, inComponent: 0, animated: false) pickerView.selectRow(0, inComponent: 1, animated: false) pickerView.selectRow(30, inComponent: 2, animated: false) pickerView(pickerView, didSelectRow: 0, inComponent: 0) pickerView(pickerView, didSelectRow: 0, inComponent: 1) pickerView(pickerView, didSelectRow: 30, inComponent: 2) } 

También me enfrenté al mismo problema. Es por eso que hice un selector de tiempo personalizado con segundos, días y muchas otras opciones y lo publiqué en GitHub:
iOSSecondsTimerPicker
Darle una oportunidad.