Friday, 15 January 2010

a UISegmentedControl subclass that simulates checkboxes

I would like to share a small , possible, implementation of checkboxes on the iPhone using a UISegmentedControl subclass.

The UISegmentedControl is in fact a radiobutton over multiple possibilities, now in certain circumstances it could be usefull that you can select multiple items.
One possibility is to use a tableview, however in some cases this is overkill.

Suppose you have the following selection list :

In this list you want to be able to select multiple values like this (for example) :

Now to be able to do this , I wrote a small subclass of UISegmentedControl :

#import



@interface UIMultipleSelectionSegmentControl : UISegmentedControl  {


NSMutableSet  *indices ;

}

 @property( nonatomic,retain)   NSMutableSet *indices ;

  

@end



Now the trick is that you cannot use the private variables of the UISegmentedControl class, so the only possibility that one can use is to override the setSelectedSegmentIndex: and their do the actual implementation of multiple selections .

#import "UIMultipleSelectionSegmentControl.h"



@implementation UIMultipleSelectionSegmentControl


 

@synthesize indices ;

 

 


- (NSSet *) selectedSegmentIndices

{

return self.indices ;

}


- (void) setSelectedSegmentIndices: (NSSet *) aSet

{

NSEnumerator *enumerator = [aSet objectEnumerator];

NSNumber *value;

while ((value = [enumerator nextObject])) 

{

[self setSelectedSegmentIndex: [value integerValue]] ;

}

 

}




- (void) setSelectedSegmentIndex: (NSInteger) anIndex

{

 

if ( self.indices == nil ) self.indices = [NSMutableSet set] ;

 

if ( anIndex >= 0 )

{

NSNumber *indexNumber ;

UIImage *myImage ;

indexNumber = [NSNumber numberWithInt: anIndex] ;

 

if ( ! [self.indices containsObject: indexNumber] )

{

[self.indices addObject: indexNumber] ;

switch( anIndex )

{

case 0 :myImage = [UIImage imageNamed: @"segment-5-sel.png"] ; break ;

case 1 :myImage = [UIImage imageNamed: @"segment-10-sel.png"] ; break ;

case 2 :myImage = [UIImage imageNamed: @"segment-20-sel.png"] ; break ;

case 3 :myImage = [UIImage imageNamed: @"segment-50-sel.png"] ; break ;

case 4 :myImage = [UIImage imageNamed: @"segment-100-sel.png"] ; break ;

case 5: myImage = [UIImage imageNamed: @"segment-200-sel.png"] ; break ;

}

[super setSelectedSegmentIndex: anIndex] ;


}

else

{

  [self.indices removeObject: indexNumber] ;

[super setSelectedSegmentIndex: -indexNumber] ;

switch( anIndex )

{

case 0 :myImage = [UIImage imageNamed: @"segment-5.png"] ; break ;

case 1 :myImage = [UIImage imageNamed: @"segment-10.png"] ; break ;

case 2 :myImage = [UIImage imageNamed: @"segment-20.png"] ; break ;

case 3 :myImage = [UIImage imageNamed: @"segment-50.png"] ; break ;

case 4 :myImage = [UIImage imageNamed: @"segment-100.png"] ; break ;

case 5: myImage = [UIImage imageNamed: @"segment-200.png"] ; break ;

}

}


[self setImage: myImage forSegmentAtIndex: anIndex] ;

 

    }

}


- (void)dealloc {

    [indices release] ;

    [super dealloc];

}



@end



In this case I made images of each segment, one that represent the unselected image and one the represent the selected image.
(the images in my case are 24x24 pixels in size).

This subclass will still send UIControlChangeEvents, so it acts as a normal UIControl class.
To get the indices you have to call the method selectedSegmentIndices and to set them you use setSelectedSegmentIndices:.
(note that the indices are a NSSet).


 






7 comments:

  1. Oh man, this sounds a little hacky. Do you find using a table view that much more effort?

    ReplyDelete
  2. Yes you do need stuff this when you have a question like click all that applies without requiring a navigation

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete
  6. Hey... I did exactly as you said and implemented the segementedcontrol like this:

    JHMultipleSegmentedControl *segDia = [[JHMultipleSegmentedControl alloc] initWithFrame:CGRectMake(20, 160, 270, 30)];
    [segDia setSelectedSegmentIndex:-1];
    [segDia insertSegmentWithImage:[UIImage imageNamed: @"domingo.png"] atIndex:0 animated:YES];
    [segDia insertSegmentWithImage:[UIImage imageNamed: @"segunda.png"] atIndex:1 animated:YES];
    [segDia insertSegmentWithImage:[UIImage imageNamed: @"terca.png"] atIndex:2 animated:YES];
    [segDia insertSegmentWithImage:[UIImage imageNamed: @"quarta.png"] atIndex:3 animated:YES];
    [segDia insertSegmentWithImage:[UIImage imageNamed: @"quinta.png"] atIndex:4 animated:YES];
    [segDia insertSegmentWithImage:[UIImage imageNamed: @"sexta.png"] atIndex:5 animated:YES];
    [segDia insertSegmentWithImage:[UIImage imageNamed: @"sabado.png"] atIndex:6 animated:YES];
    [self.view addSubview:segDia];

    But it looks like there is another segmentedcontrole behinded, blue.

    Any ideias/!

    ReplyDelete