Custom RadioButton para iOS

Lector de feed de WordPress personalizado para Android
23 marzo, 2014
Insertar una imagen local dentro de un WebView
7 abril, 2014

Learn-Objective-CHe buscado la forma de implementar las opciones de los controles que me brinda el SDK de iOS para los RadioButton, y lo mas similar que encontré es el UISegmentedControl.
El UISegmentedControl cumple con la funcionalidad que corresponde, pero no le encontré posibilidad de cambiarle para que las opciones se muestren en vertical y el texto en Horizontal, considerando que muchas veces los RadioButton tienen textos largos.

Para los que no lo conocen el UISegmentedControl, muestra lo siguiente en la pantalla de nuestro dispositivo.

UISegmentedControl_styles

El tema es que si alguna de las opciones lleva un texto mas extenso, el mismo no se lee y pierde el sentido, con lo que decidí hacer un UITableView que tenga la posibilidad de seleccionar un ítem y quede marcado como seleccionado.

Para tal cree una clase llamada TableRadioButton

TableRadioButton.h

//
//  TableRadioButton.h
//
//  Created by Biott Mauricio on 25/03/14.
//

#import <UIKit/UIKit.h>

@interface TableRadioButton : UITableView < UITableViewDelegate, UITableViewDataSource>{
    int iIdFld;
    NSArray *arrTextos;
    NSArray *arrIds;
    NSMutableArray *dataArray;
    NSInteger selectedIndex;
}

-(id) initWithData:(NSMutableArray *)items;

-(void) setIdFld:(int) iF ;
-(int) getIdFld;

-(CGFloat) getHeightTable;

-(NSString *) getSelectedId;
-(NSString *) getSelectedText;

@end

TableRadioButton.m

//
//  TableRadioButton.m
//
//  Created by Biott Mauricio on 25/03/14.
//

#import "TableRadioButton.h"

@implementation TableRadioButton

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

/**
  * Creo un nuevo Init para enviarle los items que va a tener el Radio Button
  */

-(id) initWithData:(NSMutableArray *)items{
    // Le defino cualquier CGRect ya que después lo manejo con SetFrame dependiendo de la
    // cantidad de items.
    self = [super initWithFrame:CGRectMake(0, 0, 10, 10) style:UITableViewStylePlain];
    [self setUserInteractionEnabled:true];
    [self setAllowsSelection:true];
    [self setAllowsSelectionDuringEditing:true];
    [self setDelegate:self];
    [self setDataSource:self];
    [self setDelaysContentTouches:FALSE];

    // Le defino a mi control un borde de puntas redondeadas color gris
    self.layer.cornerRadius = 10;
    self.layer.borderColor = [UIColor grayColor].CGColor;
    self.layer.borderWidth = 1;

    dataArray = items;
    selectedIndex = 0;

    return self;
}

/**
  * Este metodo calcula el height de mi control dependiendo del texto de cada item,
  * O sea  que si el texto necesita de mas de un renglón el tamañao del height del control cambia
  */

-(CGFloat) getHeightTable{
    CGFloat fRet = 0;

    for ( int i = 0 ; i < [dataArray count] ;i++){
        NSString *cellText =[[dataArray objectAtIndex:i  ] objectAtIndex:0];
        UIFont *cellFont = [UIFont fontWithName:@"Helvetica" size:17.0];
        CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT);
        CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
        fRet += ( labelSize.height + 20 );
    }

    return fRet;
}

/**
    Al control le seteo un ID para hacerlo único con motivos de posterior uso.
    No hace falta incluirlo si no lo vas a usar.
  */
-(void) setIdFld:(int) iF {
    iIdFld = iF;
}

-(int) getIdFld{
    return iIdFld;
}

/**
    Los items que utilizo estan compuesto con un NSMutableArray debido a que tengo
    Un ID de registro y el texto a mostrar.
    Esto se puede modificar si solo contamos con un array de textos unicamente.
 */

-(NSString *) getSelectedId{
    return [[dataArray objectAtIndex:selectedIndex ] objectAtIndex:1];
}
-(NSString *) getSelectedText{
    return [[dataArray objectAtIndex:selectedIndex ] objectAtIndex:0];
}

// Metodos del UITableViewDelegate, UITableViewDataSource
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    NSInteger ret = [dataArray count];
    return ret;
}

-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    NSString *cellIdent = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdent];
    if(cell == nil)
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdent];

    [cell setExclusiveTouch:YES];

    cell.textLabel.text = [[dataArray objectAtIndex:[indexPath row] ] objectAtIndex:0];
    cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
    cell.textLabel.numberOfLines = 0;
    // Definir un tamaño y letra es importante para poder controlar despues el tamaño
    // que ocupa en pantalla.
    cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:17.0];
    cell.selectionStyle = UITableViewCellSelectionStyleDefault;

    if( selectedIndex == indexPath.row ){
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    }else{
        cell.accessoryType = UITableViewCellAccessoryNone;
    }

    return cell;
}

-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    NSLog(@"Seleccionando!!!!");

    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    if( cell.accessoryType == UITableViewCellAccessoryNone ){
        selectedIndex = indexPath.row;
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        [tableView reloadData];
    }
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellText =[[dataArray objectAtIndex:[indexPath row] ] objectAtIndex:0];
    UIFont *cellFont = [UIFont fontWithName:@"Helvetica" size:17.0];
    CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT);
    CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

    return labelSize.height + 20;
}
@end

Una vez creada la clase, su implementación es simple:

TableRadioButton *tblRadio = [[TableRadioButton alloc] initWithData:dataArray];
[tblRadio setFrame:CGRectMake(10,10,300 ,[tblRadio getHeightTable] )];
[view addSubview:tblRadio];

Hay un detalle más a tener en cuenta, y es que si estas haciendo uso del UITapGestureRecognizer sobre el View donde agregaste el TableRadioButton, el touch no funciona correctamente, es como que el UITapGestureRecognizer entra en conflicto con el UITableView.

La solución fue dura de conseguir, pero simple de solucionar debido a que solo tuve que indicarle al UITapGestureRecognizer la propiedad cancelsTouchesInView en NO

UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapDetected:)];
singleTap.numberOfTapsRequired = 1;
[singleTap setCancelsTouchesInView:NO];

Les dejo un Screenshot de como se debería ver el resultado en sus dispositivos.
Captura de pantalla de Simulador iOS 27.03.2014 11.47.29

Bueno, como siempre digo, espero les sea de utilidad y cualquier cosa comenten, pregunten y opinen!

Deja un comentario