UIPickerView que se parece a UIDatePicker pero con segundos

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.

preguntado el 12 de junio de 12 a las 16:06

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) -

@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:

@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:

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. -

8 Respuestas

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!

enter image description here

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

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:

Selector H/M/S

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

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

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:

enter image description here

Respondido 16 Oct 15, 03:10

En este caso, los componentes de horas, minutos y segundos también se desplazan. ¿Es esto posible de prevenir? - Aliens

Esta es una implementación fácil y simple que no debería interrumpirse con las actualizaciones de vista: 07lodgeT

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

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

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

enter image description here

Respondido 19 Jul 18, 04:07

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.

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 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 or haz tu propia pregunta.