UIPickerView que se parece a UIDatePicker pero con segundos
Frecuentes
Visto 32,840 veces
22
Estoy creando una aplicación de temporizador y necesitaba mostrar la hora, los minutos y los segundos al usuario. Traté de usar UIDatePicker
pero muestra solo horas y minutos como selecciones. No segundos. Después de investigar un poco en línea, descubrí que no hay forma de obtener segundos en UIDatePicker
y tuve que escribir el mio UIPickerView
desde cero.
Entonces mi pregunta es, ¿hay un código de muestra para tal, es decir, alguien escribió un CustomUIPickerView
por horas, minutos y segundos que puedo incorporar en mi proyecto? UIDatePicker
tiene una buena superposición de texto de Horas y Minutos que permanece mientras el usuario gira los diales. Sería bueno si alguien también agregara eso en su selector personalizado. Prefiero no escribir una costumbre UIPickerView
desde cero si no es necesario. Gracias.
8 Respuestas
42
Muy bien amigos, aquí está el código para obtener horas/minutos/segundos en su UIPickerView
. Puede agregar 3 etiquetas y colocarlas estratégicamente en el selector. He adjuntado una imagen también.
¡Si te gusta la respuesta, califícala! Los buenos desarrolladores comparten el conocimiento, no lo ocultan como sugirieron algunas personas. ¡Diviértete codificando!
En su archivo de cabecera .h ponga 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];
}
contestado el 24 de mayo de 13 a las 13:05
¿Cómo podemos mostrar "Horas", "Min", "Sec" en el selector? - Nam vu
Esta tiene que ser una de las formas más terribles de hacer las cosas... ¿¡¿Por qué querrías preasignar todas las cadenas?! tampoco configura las etiquetas requeridas para mostrar "hora", "min", "segundos" como se muestra en la captura de pantalla: jyavenard
31
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 retrato 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:
contestado el 13 de mayo de 15 a las 00:05
¿Has probado esto con Auto Layout? Me pregunto cómo explicaría la rotación del dispositivo, los diferentes tamaños de pantalla, etc. ¡Buen trabajo! - Barra
Yo no he. Como está escrito ahora, es prácticamente solo para la vista vertical de iPhone, por lo que definitivamente hay algunas limitaciones. - Stonz2
se ve muy bonito ¿Podría esto posiblemente ser empaquetado y compartido? - fatuhoku
Lo haría, pero estoy tratando de resolver los problemas para que se vea bien en modo horizontal. Lo tengo claro cuando uso el selector como un inputView
menos UITextField
(para que el teléfono maneje la actualización del marco) pero no como una vista independiente. Definitivamente no soy el mejor cuando se trata de manejar cambios de orientación. - Stonz2
¡Esto definitivamente debería estar por encima de la respuesta principal, 32 porque eso es ridículo! Creo que sería mejor usar el diseño automático, aunque como sugiere Rod, entonces podrías usar los atributos multiplicador y central. ¡Buen trabajo! - Rico
8
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
}
}
Respondido 09 Abr '15, 11:04
7
No me gustaron las respuestas que no dependían de agregar UILabel
como subvistas a UIPickerView
, porque codificar los marcos se romperá si iOS decide cambiar la apariencia algún día.
Un truco simple es tener las etiquetas de hora/min/seg como componentes con 1 fila.
Las etiquetas de hora/min/s estarán más alejadas de sus valores, pero está bien. La mayoría de los usuarios ni siquiera lo notarán. Ver por ti mismo:
Respondido 16 Oct 15, 03:10
4
Hice algunas mejoras leves a la versión de @Stonz2. También hubo un error con el cálculo de la posición y de las etiquetas.
Aquí el archivo .h
@interface CustomUIDatePicker : UIPickerView <UIPickerViewDataSource, UIPickerViewDelegate>
@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
respondido 01 mar '15, 19:03
ty! ¿Puedo preguntarle si solo quiero 2 columnas (minutos, segundos) qué debo cambiar? Intenté editar un poco el código pero no pude obtener el resultado que quería: LS_
numberOfComponentsInPickerView
tiene que devolver 2 en su lugar. y en initialize
solo debe crear dos subvistas. también el offsetX solo debería ser algo como self.frame.size.width / 2;
- ph1lb4
1
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 ActionSheetPicker.
Respondido 01 ago 17, 21:08
1
Tengo una solución simple aquí, puede 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;
}
}
Respondido 19 Jul 18, 04:07
0
Aquí hay otra solución para la superposición de "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.
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 muestre la vista del selector, las filas deben seleccionarse mediante programació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)
}
Respondido 01 ago 17, 10:08
Buena respuesta @nyg pero no deberías usar label.text?.append("min") como si seleccionaras dos veces la fila, obtuviste la palabra dos veces. Es mucho mejor usar label.text = String(row)+" min".. Pero gracias funciona muy bien - Pierre
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas iphone ios xcode ios5 or haz tu propia pregunta.
use uiPickerView con 3 componentes y cada uno tenga una matriz. HourArray de (1 a 12). MinutesArray con (1 a 60). segundosArray (1 a 60) - Paresh Navadiya
@casoseguro. Entiendo técnicamente cómo hay que hacerlo. Todo lo que pregunto es si a alguien le gustaría compartir este fragmento de código conmigo en lugar de escribirlo desde cero: Sam B
@EmilioPelaez: absténgase de usar "nosotros". No hablas por toda la comunidad de desarrollo de iOS aquí. Hay muchos buenos desarrolladores que felizmente comparten conocimiento y código. Si termino escribiendo el código, lo compartiré aquí con el resto de la comunidad y cualquier persona que pueda estar interesada en el futuro y lea mi publicación: Sam B
Dije "nosotros" porque Stack Overflow tiene un propósito bastante claro, y solicitar código no lo es (si alguien tiene ese código y quiere compartirlo, hay muchas formas de buscar para hacerlo). "Nosotros" estamos aquí con esa premisa. - EmilioPelaez