UIImage resizableImageWithCapInsets - can this be specified using a nib? - ios

I have a UIImage resizing perfectly using resizableImageWithCapInsets to make a badge containing a text label. I'm interested to know is there a way to do this in a storyboard, or in a view created with a nib?
There's no technical reason to require this. Most of the UI is done in the nib and I would like to keep as much there as possible rather than loading the image, making it resizable, finding the text dimensions, resizing the UIImageView and then applying the label in code.
I can't make this project iOS6-only yet (deployment target ios5+) so using layout constraints isn't an option.

Yes, there is.
The trick is to override -initWithCoder: and the image setters (setImage:forState: and setBackgroundImage:forState:) to create the resizable versions of the same image you set in the xib. Obviously, they still won't resize properly in xib itself, but it will be resized properly after initialization. Don't forget to set NAResizableButton as the custom class of UIButton.
Sure beats doing it programmatically. :)
Interface:
#import <UIKit/UIKit.h>
#interface NAResizableButton : UIButton
#end
Implementation:
#import "NAResizableButton.h"
#implementation NAResizableButton
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self setImage:[self imageForState:UIControlStateNormal] forState:UIControlStateNormal];
[self setImage:[self imageForState:UIControlStateHighlighted] forState:UIControlStateHighlighted];
[self setImage:[self imageForState:UIControlStateSelected] forState:UIControlStateSelected];
[self setImage:[self imageForState:UIControlStateDisabled] forState:UIControlStateDisabled];
[self setBackgroundImage:[self backgroundImageForState:UIControlStateNormal] forState:UIControlStateNormal];
[self setBackgroundImage:[self backgroundImageForState:UIControlStateHighlighted] forState:UIControlStateHighlighted];
[self setBackgroundImage:[self backgroundImageForState:UIControlStateSelected] forState:UIControlStateSelected];
[self setBackgroundImage:[self backgroundImageForState:UIControlStateDisabled] forState:UIControlStateDisabled];
}
return self;
}
- (void)setImage:(UIImage *)inImage forState:(UIControlState)inState
{
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(ceilf(inImage.size.height / 2), ceilf(inImage.size.width / 2), ceilf(inImage.size.height / 2), ceilf(inImage.size.width / 2));
if ([inImage respondsToSelector:#selector(resizableImageWithCapInsets:)])
{
// iOS 5
inImage = [inImage resizableImageWithCapInsets:edgeInsets];
}
else
inImage = [inImage stretchableImageWithLeftCapWidth:edgeInsets.left topCapHeight:edgeInsets.top];
[super setImage:inImage forState:inState];
}
- (void)setBackgroundImage:(UIImage *)inImage forState:(UIControlState)inState
{
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(ceilf(inImage.size.height / 2), ceilf(inImage.size.width / 2), ceilf(inImage.size.height / 2), ceilf(inImage.size.width / 2));
if ([inImage respondsToSelector:#selector(resizableImageWithCapInsets:)])
{
// iOS 5
inImage = [inImage resizableImageWithCapInsets:edgeInsets];
}
else
inImage = [inImage stretchableImageWithLeftCapWidth:edgeInsets.left topCapHeight:edgeInsets.top];
[super setBackgroundImage:inImage forState:inState];
}
#end

Related

In iOS7.1 UIButton set image not working?

[_firstPrincipleButton setBackgroundImage:[UIImage imageNamed:#"i_press.png"]
forState:UIControlStateNormal];
The above code is working fine in iOS7 but in iOS7.1 this is not changing the image of UIButton. I have also tried to do this with setImage but didn't work.
Thanks
Just in case that others have the same problem and need another hint on how to fix this:
I also had this problem and finally found out that setting a button's (background) image doesn't work on iOS 7.1, if the button is disabled.
Not sure if this fixes your problem, but it was mine. Calling setNeedsLayout didn't help in my case, unfortunately. What you can do as a workaround is either to override UIButton or to add a category that contains a method like the following to set an image:
- (void)setImage:(UIImage *)img forButtonState:(UIControlState)state
{
BOOL shouldEnable = self.enabled;
self.enabled = YES;
[self setImage:img forState:state];
self.enabled = shouldEnable;
}
Filed a bug report for this issue (16497031).
It is a bug in iOS 7.1 ,
you can create a category for UIButton and implement a method to set the image somewhat like this.
-(void)setImage:(UIImage *)image forButtonState:(UIControlState)state
{
CGFloat version = [[UIDevice currentDevice] systemVersion].floatValue ;
if( version <= 7.1)
{
BOOL isFirstTime = YES ;
for(UIView *aView in self.subviews)
{
if([aView isKindOfClass:[UIImageView class]])
{
[(UIImageView *)aView setImage:image] ;
isFirstTime = NO ;
break ;
}
}
if(isFirstTime)
{
CGRect frame = self.frame ;
frame.size = image.size ;
UIImageView * imageView = [[UIImageView alloc] initWithFrame:frame];
[imageView setImage:image];
[self addSubview:imageView];
}
}
else
[self setImage:image forState:state];
}
This will work.
enjoy coding :)

UIButton not showing up in SKScene

I'm trying to write some methods which can be used to generate labels, images, buttons etc.. in my spritekit project.
I call my setUPScene method in the initWithSize method and in the setUpScene method I call three methods to set label, image and button.
My label and image appears on the main scene but I can't succeed to appear UIButton type button. Actually I seek for some error but I think everything seems right. Here are my methods;
-(void) setUpMainScene {
self.backgroundColor = [SKColor colorWithRed:1.0 green:0.15 blue:0.3 alpha:1.0];
[self addChild:[self createLabel:#"Vecihi"
labelFontName:#"Chalkduster"
labelFontSize:20
labelPositionX:CGRectGetMidX(self.frame)
labelPositionY:CGRectGetMidY(self.frame)]];
[self addChild:[self createImage:#"Spaceship"
imagePositionX:CGRectGetMidX(self.frame)
imagePositionY:CGRectGetMidY(self.frame)+60
imageWidth:40.0
imageHeight:60.0]];
[self.view addSubview:[self createButton:#"ButtonSample.png"
setTitleColor:[UIColor whiteColor]
buttonPositionX:(CGRectGetMidX(self.frame))
buttonPositionY:(CGRectGetMidY(self.frame)-200.0)
buttonWidth:200.0
buttonHeight:70.0]];
}
//Create any label with given parameters
-(SKLabelNode *) createLabel:(NSString *) labelTitle labelFontName:(NSString *) fontName labelFontSize:(int)fontSize labelPositionX:(CGFloat) x labelPositionY:(CGFloat) y {
SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:fontName];
myLabel.text = labelTitle;
myLabel.fontSize = fontSize;
myLabel.position = CGPointMake(x,y);
return myLabel;
}
//Create any image with given parameters
-(SKSpriteNode *) createImage:(NSString *) imageName imagePositionX:(CGFloat) x imagePositionY:(CGFloat) y imageWidth:(CGFloat) width imageHeight:(CGFloat) height {
SKSpriteNode *myImage = [SKSpriteNode spriteNodeWithImageNamed:imageName];
myImage.position = CGPointMake(x, y);
myImage.size = CGSizeMake(width, height);
return myImage;
}
//Create any button wtih given parameters
-(UIButton *) createButton:(NSString *) buttonImage setTitleColor:(UIColor *) titleColor buttonPositionX:(CGFloat) x buttonPositionY:(CGFloat) y buttonWidth:(CGFloat) width buttonHeight:(CGFloat) height {
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
NSLog(#"%#", buttonImage);
myButton.frame = CGRectMake(x, y, width, height);
myButton.backgroundColor = [UIColor clearColor];
[myButton setTitleColor:titleColor forState:UIControlStateNormal];
[myButton setBackgroundImage:[UIImage imageNamed:buttonImage] forState:UIControlStateNormal];
return myButton;
}
The reason your code does not work, is because the scene's view property is set to nil, when initWithSize: is called.
You could handle this by moving the UI initialization code to the scene's didMoveToView: method. Then, in willMoveFromView:, you can perform cleanup (i.e. remove the UIButton from the view).
That way your scene-specific UI code stays in the scene, and doesn't clutter up the viewcontroller code. Especially, if you decide to add UI controls to multiple scenes - you only have one viewcontroller, and it could become messy fast.
You have to setup the button in the viewcontroller owning the scene, NOT the scene itself.
So, move
[self.view addSubview:[self createButton:#"ButtonSample.png"
setTitleColor:[UIColor whiteColor]
buttonPositionX:(CGRectGetMidX(self.frame))
buttonPositionY:(CGRectGetMidY(self.frame)-200.0)
buttonWidth:200.0
buttonHeight:70.0]];
in SampleScene.m
to SampleViewController.m like this
- (void) viewDidLoad {
[self.view addSubview:[self.scene createButton:#"ButtonSample.png"
setTitleColor:[UIColor whiteColor]
buttonPositionX:(CGRectGetMidX(self.view.frame))
buttonPositionY:(CGRectGetMidY(self.view.frame)-200.0)
buttonWidth:200.0
buttonHeight:70.0]];
//More commands not shown...
}
Please note the changes in the button positioning, and button creation
Replacing SamplesScene & SampleViewController to their corresponding names of course

how can I change the default values for UISlider in a custom subclass of UISlider so I can reuse it multiple times

At the moment I'm subclassing a UISlider and in the drawRect setting the default values. I'm getting a strange result.
The slider min image is at the correct position but the slider max image has white space on the left. The slider image in the middle is to far right and the colour for maximumTackTintColor is on both sides of the slider.
- (void)drawRect:(CGRect)rect
{
// Drawing code
self.minimumTrackTintColor = [ThemeManager minimumTrackTintColorForSlider];
self.maximumTrackTintColor = [ThemeManager maximumTrackTintColorForSlider];
self.minimumValueImage = [UIImage imageNamed:#"sliderLeftDot"];
self.maximumValueImage = [UIImage imageNamed:#"sliderRightDot"];
[self setThumbImage:[UIImage imageNamed:#"sliderCentrePiece"] forState:UIControlStateNormal];
}
You're not calling [super drawRect:rect] and this causes the slide to never be drawn properly, however you should never override drawRect: unless you need to perform custom drawing.
The right place where to initialize defaults is the designated initializer(s). Being UISlider a subclass of UIView, you need to override both -initWithFrame: and -initWithCoder:, to handle both the cases in which the slider is created programmatically or via a nib.
For instance
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self commonInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self commonInit];
}
return self;
}
- (void)commonInit {
self.minimumTrackTintColor = [ThemeManager minimumTrackTintColorForSlider];
self.maximumTrackTintColor = [ThemeManager maximumTrackTintColorForSlider];
self.minimumValueImage = [UIImage imageNamed:#"sliderLeftDot"];
self.maximumValueImage = [UIImage imageNamed:#"sliderRightDot"];
[self setThumbImage:[UIImage imageNamed:#"sliderCentrePiece"] forState:UIControlStateNormal];
}
Do not put that code in the drawRect: method. Put it in the appropriate init method or put it in another method that you call when you want the slider customized.
Even better is to make used of the UIAppearance feature to set the tint colors for all sliders:
[[UISlider appearance] setMinimumTrackTintColor:someColor];
[[UISlider appearance] setMaximumTrackTintColor:someColor];

UIButton's imageView property and hidden/alpha value

I have a UIButton that should display an activity indicator instead of an image in some situations. What I do at the moment is setting the hidden property of the button's imageView to YES and back. I also tried this with setting the alpha value to 0.0f and back to 1.0f.
This works until the state of the button changes. This resets the properties of the imageView and leads to hidden == NO and alpha == 1.0f.
Has anybody did something similar or has an idea how to hide the imageView of a button while the rest of it stays visible?
You can achieve this by playing with the transform property of view's layer i.e
To hide
swift code
button.imageView?.layer.transform = CATransform3DMakeScale(0.0, 0.0, 0.0)
objective-C code
button.imageView.layer.transform = CATransform3DMakeScale(0, 0, 0);
To unhide
button.imageView?.layer.transform = CATransform3DIdentity
objective-C code
button.imageView.layer.transform = CATransform3DIdentity
It is simple.
Button have different states. Just set zero image for selected state and your image for normal state. When you want to display activity indicator set button state as selected and add indicator as button subview.
- (void)initializeButton {
button = [[UIButton alloc] init];
[button addTarget:self action:#selector(buttonDidClick:) forControlEvents:UIControlEventTouchUpInside];
[button setImage:[[UIImage alloc] init] forState:UIControlStateSelected]; //zero image
[button setImage:yourImage forState:UIControlStateSelected]; //your image
}
- (void)buttonDidClick:(UIButton *)button {
button.selected = YES;
/* Add yor activity indicator with some view tag or save reference to it */
}
Call next method when your activity is finished
- (void)finishActivityWithButton:(UIButton *)button {
button.selected = NO;
/* Remove your activity indicator from button */
}
set the tint color to [UIColor clearColor] if you are using a template image (should maybe work on others too, didn't try)
[[button imageView] setTintColor:[UIColor clearColor]];
You can achieve this by removing the image from the button when you want to hide it and assigning the image back to button when you need to show the image.
When you want to hide the image write like:
[yourButton setImage:nil forState:UIControlStateNormal];
and when you want to show the image back:
[yourButton setImage:yourImage forState:UIControlStateNormal];
Here yourButton is your UIButton and yourImage is a UIImage which holds the button image.
change the transform or select state will cause another problem, the best way is using category:
const char kNormalImage;
const char kSelectImage;
const char kDisableImage;
#interface UIButton ()
#property (nonatomic, strong) UIImage *normalImage;
#property (nonatomic, strong) UIImage *selectImage;
#property (nonatomic, strong) UIImage *disableImage;
#end
#implementation UIButton (Hidden)
- (void)setImageViewHidden:(BOOL)hidden {
if (hidden) {
self.normalImage = [self imageForState:UIControlStateNormal];
self.selectImage = [self imageForState:UIControlStateSelected];
self.disableImage = [self imageForState:UIControlStateDisabled];
UIGraphicsBeginImageContextWithOptions(self.imageView.bounds.size, NO, 0.0);
UIImage *blank = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setImage:blank forState:UIControlStateNormal];
[self setImage:blank forState:UIControlStateSelected];
[self setImage:blank forState:UIControlStateDisabled];
} else {
[self setImage:self.normalImage forState:UIControlStateNormal];
[self setImage:self.selectImage forState:UIControlStateSelected];
[self setImage:self.disableImage forState:UIControlStateDisabled];
}
}
- (UIImage *)normalImage {
return objc_getAssociatedObject(self, &kNormalImage);
}
- (void)setNormalImage:(UIImage *)normalImage {
objc_setAssociatedObject(self, &kNormalImage, normalImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIImage *)selectImage {
return objc_getAssociatedObject(self, &kSelectImage);
}
- (void)setSelectImage:(UIImage *)selectImage {
objc_setAssociatedObject(self, &kSelectImage, selectImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIImage *)disableImage {
return objc_getAssociatedObject(self, &kDisableImage);
}
- (void)setDisableImage:(UIImage *)disableImage {
objc_setAssociatedObject(self, &kDisableImage, disableImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end

Checkbox in iOS application

I need to add checkbox controls to my form. I know that there is no such control in iOS SDK. How could I do this?
this has been driving me mad too and I found a different solution that works well for me and avoids having to use images.
Add a new label object to Interface Builder.
Create an IBOutlet property in Xcode and connect it up to it. In the code below I've called it 'fullyPaid' as I want to know if someone has fully paid a sum of money.
Add the 2 functions below.
The 'touchesBegan' function checks if you touched somewhere inside the 'fullyPaid' label object and if so, it calls the 'togglePaidStatus' function.
The 'togglePaidStatus' function sets up two strings which have the unicode characters representing an empty box (\u2610) and a checked box (\u2611) respectively. Then it compares what's currently in the 'fullyPaid' object and toggles it with the other string.
You might want to call the togglePaidStatus function in the viewDidLoad function to set it to an empty string initially.
Obviously you can add extra checks to prevent users toggling the checkbox if the label is not enabled, but that's not shown below.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if (CGRectContainsPoint([fullyPaid frame], [touch locationInView:self.view]))
{
[self togglePaidStatus];
}
}
-(void) togglePaidStatus
{
NSString *untickedBoxStr = [[NSString alloc] initWithString:#"\u2610"];
NSString *tickedBoxStr = [[NSString alloc] initWithString:#"\u2611"];
if ([fullyPaid.text isEqualToString:tickedBoxStr])
{
fullyPaid.text = untickedBoxStr;
}
else
{
fullyPaid.text = tickedBoxStr;
}
[tickedBoxStr release];
[untickedBoxStr release];
}
Generally, you would use the UISwitch for checkbox-like functionality.
You could roll your own though by using an image control with two images (checked/unchecked) and switching the images when they touch the control/
If you're showing a group of options and the user can select one of them, use a tableview with a checkmark accessory and a different text color on the selected row.
If you have a single option, your best bet is to use a switch. If you can't or don't want to, use a button, setting the normal image to an empty box and the selected image to a checked box. You'll have to make those two images yourself or find stock graphics to use for them.
Extending to Adrean's idea, i've achieved this using a very simple approach.
My idea is to change button (lets say checkBtn) text depending upon its state, and then change button's state in its IBAction.
Below is the code how i did this:
- (void)viewDidLoad
{
[super viewDidLoad];
[checkBtn setTitle:#"\u2610" forState:UIControlStateNormal]; // uncheck the button in normal state
[checkBtn setTitle:#"\u2611" forState:UIControlStateSelected]; // check the button in selected state
}
- (IBAction)checkButtonTapped:(UIButton*)sender {
sender.selected = !sender.selected; // toggle button's selected state
if (sender.state == UIControlStateSelected) {
// do something when button is checked
} else {
// do something when button is unchecked
}
}
I wanted to do this programmatically, and also solve the problem that the hit area was really too small. This is adapted from various sources, including Mike and Mike's commenter Agha.
In your header
#interface YourViewController : UIViewController {
BOOL checkboxSelected;
UIButton *checkboxButton;
}
#property BOOL checkboxSelected;;
#property (nonatomic, retain) UIButton *checkboxButton;
-(void)toggleButton:(id)sender;
And in your implementation
// put this in your viewDidLoad method. if you put it somewhere else, you'll probably have to change the self.view to something else
// create the checkbox. the width and height are larger than actual image, because we are creating the hit area which also covers the label
UIButton* checkBox = [[UIButton alloc] initWithFrame:CGRectMake(100, 60,120, 44)];
[checkBox setImage:[UIImage imageNamed:#"checkbox.png"] forState:UIControlStateNormal];
// uncomment below to see the hit area
// [checkBox setBackgroundColor:[UIColor redColor]];
[checkBox addTarget:self action:#selector(toggleButton:) forControlEvents: UIControlEventTouchUpInside];
// make the button's image flush left, and then push the image 20px left
[checkBox setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[checkBox setImageEdgeInsets:UIEdgeInsetsMake(0.0, 20.0, 0.0, 0.0)];
[self.view addSubview:checkBox];
// add checkbox text text
UILabel *checkBoxLabel = [[UILabel alloc] initWithFrame:CGRectMake(140, 74,200, 16)];
[checkBoxLabel setFont:[UIFont boldSystemFontOfSize:14]];
[checkBoxLabel setTextColor:[UIColor whiteColor]];
[checkBoxLabel setBackgroundColor:[UIColor clearColor]];
[checkBoxLabel setText:#"Checkbox"];
[self.view addSubview:checkBox];
// release the buttons
[checkBox release];
[checkBoxLabel release];
And put this method in too:
- (void)toggleButton: (id) sender
{
checkboxSelected = !checkboxSelected;
UIButton* check = (UIButton*) sender;
if (checkboxSelected == NO)
[check setImage:[UIImage imageNamed:#"checkbox.png"] forState:UIControlStateNormal];
else
[check setImage:[UIImage imageNamed:#"checkbox-checked.png"] forState:UIControlStateNormal];
}
Here is my version of checkbox for iphone.
It is single class which extends UIButton.
It is simple so i will paste it here.
CheckBoxButton.h file content
#import <UIKit/UIKit.h>
#interface CheckBoxButton : UIButton
#property(nonatomic,assign)IBInspectable BOOL isChecked;
#end
CheckBoxButton.m file content
#import "CheckBoxButton.h"
#interface CheckBoxButton()
#property(nonatomic,strong)IBInspectable UIImage* checkedStateImage;
#property(nonatomic,strong)IBInspectable UIImage* uncheckedStateImage;
#end
#implementation CheckBoxButton
-(id)init
{
self = [super init];
if(self)
{
[self addTarget:self action:#selector(switchState) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
[self addTarget:self action:#selector(switchState) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if(self)
{
[self addTarget:self action:#selector(switchState) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
-(void)setIsChecked:(BOOL)isChecked
{
_isChecked = isChecked;
if(isChecked)
{
[self setImage:self.checkedStateImage forState:UIControlStateNormal];
}
else
{
[self setImage:self.uncheckedStateImage forState:UIControlStateNormal];
}
}
-(void)switchState
{
self.isChecked = !self.isChecked;
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
#end
You can set images for checked/unchecked as well as isChecked property in the attribute inspector of visual studio.
To add CheckBoxButton in storyboard or xib, simple add UIButton and set custom class like on next image.
Button will send UIControlEventValueChanged event, every time when isChecked state is changed.
Everyones code here is very long, slightly messy, and could be done a lot simpler. I have a project on GitHub that subclass UIControl that you can download and check out and gives you an almost native checkbox UI element:
https://github.com/Brayden/UICheckbox
I like the idea of Adrian to use the characters rather than images. But I don't like the box, it need only the checkmark itself (#"\u2713"). I draw a box (a rounded box) programmatically and place an UILabel contains the checkmark inside it. This way of implementation makes it easy to use the custom view in any application without care about any dependent resource. You can also customize the color of the checkmark, the rounded box and the background with ease.
Here's the complete code:
#import <UIKit/UIKit.h>
#class CheckBoxView;
#protocol CheckBoxViewDelegate
- (void) checkBoxValueChanged:(CheckBoxView *) cview;
#end
#interface CheckBoxView : UIView {
UILabel *checkMark;
bool isOn;
UIColor *color;
NSObject<CheckBoxViewDelegate> *delegate;
}
#property(readonly) bool isOn;
#property(assign) NSObject<CheckBoxViewDelegate> *delegate;
- (void) drawRoundedRect:(CGRect) rect inContext:(CGContextRef) context;
#end
#import "CheckBoxView.h"
#define SIZE 30.0
#define STROKE_WIDTH 2.0
#define ALPHA .6
#define RADIUS 5.0
#implementation CheckBoxView
#synthesize isOn, delegate;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, SIZE, SIZE)])) {
// Initialization code
}
//UIColor *color = [UIColor blackColor];
color = [[UIColor alloc] initWithWhite:.0 alpha:ALPHA];
self.backgroundColor = [UIColor clearColor];
checkMark = [[UILabel alloc] initWithFrame:CGRectMake(STROKE_WIDTH, STROKE_WIDTH, SIZE - 2 * STROKE_WIDTH, SIZE - 2*STROKE_WIDTH)];
checkMark.font = [UIFont systemFontOfSize:25.];
checkMark.text = #"\u2713";
checkMark.backgroundColor = [UIColor clearColor];
checkMark.textAlignment = UITextAlignmentCenter;
//checkMark.textColor = [UIColor redColor];
[self addSubview:checkMark];
[checkMark setHidden:TRUE];
isOn = FALSE;
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
CGRect _rect = CGRectMake(STROKE_WIDTH, STROKE_WIDTH, SIZE - 2 * STROKE_WIDTH, SIZE - 2*STROKE_WIDTH);
[self drawRoundedRect:_rect inContext:UIGraphicsGetCurrentContext()];
[checkMark setHidden:!isOn];
}
- (void)dealloc {
[checkMark release];
[color release];
[super dealloc];
}
- (void) drawRoundedRect:(CGRect) rect inContext:(CGContextRef) context{
CGContextBeginPath(context);
CGContextSetLineWidth(context, STROKE_WIDTH);
CGContextSetStrokeColorWithColor(context, [color CGColor]);
CGContextMoveToPoint(context, CGRectGetMinX(rect) + RADIUS, CGRectGetMinY(rect));
CGContextAddArc(context, CGRectGetMaxX(rect) - RADIUS, CGRectGetMinY(rect) + RADIUS, RADIUS, 3 * M_PI / 2, 0, 0);
CGContextAddArc(context, CGRectGetMaxX(rect) - RADIUS, CGRectGetMaxY(rect) - RADIUS, RADIUS, 0, M_PI / 2, 0);
CGContextAddArc(context, CGRectGetMinX(rect) + RADIUS, CGRectGetMaxY(rect) - RADIUS, RADIUS, M_PI / 2, M_PI, 0);
CGContextAddArc(context, CGRectGetMinX(rect) + RADIUS, CGRectGetMinY(rect) + RADIUS, RADIUS, M_PI, 3 * M_PI / 2, 0);
CGContextClosePath(context);
CGContextStrokePath(context);
}
#pragma mark Touch
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint loc = [touch locationInView:self];
if(CGRectContainsPoint(self.bounds, loc)){
isOn = !isOn;
//[self setNeedsDisplay];
[checkMark setHidden:!isOn];
if([delegate respondsToSelector:#selector(checkBoxValueChanged:)]){
[delegate checkBoxValueChanged:self];
}
}
}
in .h file
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
{
BOOL isChecked;
UIImageView * checkBoxIV;
}
#end
And .m file
- (void)viewDidLoad
{
[super viewDidLoad];
isChecked = NO;
//change this property according to your need
checkBoxIV = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 15, 15)];
checkBoxIV.image =[UIImage imageNamed:#"checkbox_unchecked.png"];
checkBoxIV.userInteractionEnabled = YES;
UITapGestureRecognizer *checkBoxIVTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handlecheckBoxIVTapGestureTap:)];
checkBoxIVTapGesture.numberOfTapsRequired = 1;
[checkBoxIV addGestureRecognizer:checkBoxIVTapGesture];
}
- (void)handlecheckBoxIVTapGestureTap:(UITapGestureRecognizer *)recognizer {
if (isChecked) {
isChecked = NO;
checkBoxIV.image =[UIImage imageNamed:#"checkbox_unchecked.png"];
}else{
isChecked = YES;
checkBoxIV.image =[UIImage imageNamed:#"checkbox_checked.png"];
}
}
This will do the trick...
I made it with a UITextField to avoid drawing anything strange but I liked putting inside as text the tick unicode (Unicode Character 'CHECK MARK' (U+2713)) for the NSString: #"\u2713".
This way, in my .h file (implementing the protocol for the UITextField 'UITextFieldDelegate'):
UITextField * myCheckBox;
In my viewDidLoad or the function to prepare the UI:
...
myCheckBox = [[UITextField alloc] initWithFrame:aFrame];
myCheckBox.borderStyle = UITextBorderStyleRoundedRect; // System look like
myCheckBox.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
myCheckBox.textAlignment = NSTextAlignmentLeft;
myCheckBox.delegate = self;
myCheckBox.text = #" -"; // Initial text of the checkbox... editable!
...
Then, add an event selector for reating in the touch event and calling 'responseSelected' event:
...
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(checkboxSelected)];
[myCheckBox addGestureRecognizer:tapGesture];
...
Finally respond to that selector
-(void) checkboxSelected
{
if ([self isChecked])
{
// Uncheck the selection
myCheckBox.text = #" -";
}else{
//Check the selection
myCheckBox.text = #"\u2713";
}
}
The function 'isChecked' only checks if the text is the #"\u2713" check mark. To prevent showing the keyboard when the text field is selected use the event of the UITextField 'textFieldShouldBeginEditing' and add the event selector to manage the selection:
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
// Question selected form the checkbox
[self checkboxSelected];
// Hide both keyboard and blinking cursor.
return NO;
}
Subclass UIButton, drop a button to view controller, select it and change class name to CheckBox in the identity inspector.
#import "CheckBox.h"
#implementation CheckBox
#define checked_icon #"checked_box_icon.png"
#define empty_icon #"empty_box_icon.png"
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self setImage:[UIImage imageNamed:empty_icon] forState:UIControlStateNormal];
[self addTarget:self action:#selector(didTouchButton) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (void)didTouchButton {
selected = !selected;
if (selected)
[self setImage:[UIImage imageNamed:checked_icon] forState:UIControlStateNormal];
else
[self setImage:[UIImage imageNamed:empty_icon] forState:UIControlStateNormal];
}
#end
I made one recently. Free to acquire from GitHub. See if this will help. The effect is like
user Aruna Lakmal; FYI, when you add this code to IB as you describe initWithFrame is not called, initWithCoder is. Implement initWithCoder and it will work as you describe.
A simple UIButton subclass will able to behave like checkbox found in Android.
import UIKit
class CheckedUIButton: UIButton {
var checked: Bool = false {
didSet {
if checked {
setImage(UIImage(systemName: "checkmark.circle"), for: .normal)
} else {
setImage(UIImage(systemName: "circle"), for: .normal)
}
}
}
//initWithFrame to init view from code
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
//initWithCode to init view from xib or storyboard
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
addTarget(self, action: #selector(self.checkedTapped), for: .touchUpInside)
}
#objc
private func checkedTapped() {
self.checked.toggle()
}
}
Output
(Unchecked state)
(Checked state)
A simple UIButton subclass using SF symbols does the trick
class CheckBoxButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
func setup() {
setImage(UIImage(systemName: "square.fill"), for: .normal)
setImage(UIImage(systemName: "checkmark.square.fill"), for: .selected)
}
}
Storyboard
If you are using storyboard, ensure button type is set to custom as seen in the image below
Usage
All you have to do is toggle the isSelected variable in action/selector method
#objc func toggle(_ sender: UIButton) {
sender.isSelected.toggle()
}
Demo

Resources