UIButton not showing up in SKScene - ios

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

Related

Increase tap area of UIBarButtonitem to the left far side of navigation bar

I use the following code to add the back button bar item to the navigation bar.
CGSize size = CGSizeMake(40, 40);
UIImage *image = [mysdk.icon.backArrow make];
UIButton *back = [self.class buttonForBarItemByImage:image size:size];
back.backgroundColor = [UIColor grayColor];
UIBarButtonItem *backArrow = [[UIBarButtonItem alloc] initWithCustomView:back];
[back addTarget:backArrow action:#selector(backArrow_onClick) forControlEvents:UIControlEventTouchUpInside];
+ (UIButton *) buttonForBarItemByImage:(UIImage *)image size:(CGSize)size {
UIButton *back = [UIButton buttonWithType:UIButtonTypeCustom];
[back setImage:image forState:UIControlStateNormal];
CGSize buttonSize = size;
back.frame = CGRectMake(0, 0, buttonSize.width, buttonSize.height);
return back;
}
As you see in the following image the button is added. The image of the button is in the middle of the grey area. The grey area is the tap area.
As you see i circle with red color the area that is still with yellow. So i need to make this area tappable. The button image should not move just increase the tap area from the left. Is this somehow possible?
You can do this with a subclassed UINavigationBar:
MyNavBar.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface MyNavBar : UINavigationBar
#end
NS_ASSUME_NONNULL_END
MyNavBar.m
#import "MyNavBar.h"
#implementation MyNavBar
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// get the custom view (the UIButton)
UIView *v = self.topItem.leftBarButtonItem.customView;
if (v) {
// convert the button's frame to its superview coordinate space
CGRect r = [self convertRect:v.frame toView:v.superview];
// convert the point to the button's coordinate space
CGPoint p = [self convertPoint:point toView:v];
// if p is inside r
if (CGRectContainsPoint(r, p)) {
// return the custom view
return v;
}
}
return [super hitTest:point withEvent:event];
}
#end

Scrollview should start from where I left it last time

I have a scrollview(timesScrollView) which is added as a subview on a view(dropDownView).The view is hidden until a particular button is pressed, when that button is pressed view will appear.
(IBAction)how_many_times_btn_click:(id)sender{
if(howMany==false){
for(UIView *view in dropDownView.subviews)
{
[view removeFromSuperview];
}
howMany=true;
duration=false;
how_many_times_btn.backgroundColor=[UIColor colorWithRed:130/255.0f green:189/255.0f blue:31/255.0f alpha:1.0f];
durationBtn.backgroundColor=[UIColor colorWithRed:62/255.0f green:67/255.0f blue:79/255.0f alpha:1.0f];
startBtn.backgroundColor=[UIColor colorWithRed:62/255.0f green:67/255.0f blue:79/255.0f alpha:1.0f];
dropDownView.hidden=NO;
dropDownView.frame=CGRectMake(0, 0, self.view.frame.size.width,70);
dropDownView.backgroundColor=[UIColor colorWithRed:37/255.0f green:42/255.0f blue:54/255.0f alpha:1.0f];
//dropDownView.backgroundColor=[UIColor whiteColor];
targetLbl=[[UILabel alloc]init];
targetLbl.frame=CGRectMake(0, 30, dropDownView.frame.size.width,30);
targetLbl.text=#"TARGET";
targetLbl.textColor=[UIColor whiteColor];
targetLbl.font=[UIFont boldSystemFontOfSize:22];
targetLbl.textAlignment=NSTextAlignmentCenter;
how_many_Lbl=[[UILabel alloc]init];
how_many_Lbl.frame=CGRectMake(0, targetLbl.frame.origin.y+targetLbl.frame.size.height, dropDownView.frame.size.width, 20);
how_many_Lbl.textAlignment=NSTextAlignmentCenter;
how_many_Lbl.text=#"HOW MANY TIMES WILL YOU DO IT?";
how_many_Lbl.textColor=[UIColor colorWithRed:65/255.0f green:71/255.0f blue:80/255.0f alpha:1.0f];
how_many_Lbl.font=[UIFont systemFontOfSize:10.0f];
hideViewBtn=[UIButton buttonWithType:UIButtonTypeCustom];
hideViewBtn.frame=CGRectMake(dropDownView.frame.size.width-30,20,20,20);
[hideViewBtn setImage:[UIImage imageNamed:#"Close Icon [ x ]"] forState:UIControlStateNormal];
[hideViewBtn addTarget:self action:#selector(hideView) forControlEvents:UIControlEventTouchUpInside];
//hideViewBtn.backgroundColor=[UIColor whiteColor];
self.timesScroll=[[LTInfiniteScrollView alloc] initWithFrame:CGRectMake(0, how_many_Lbl.frame.origin.y+how_many_Lbl.frame.size.height+16, dropDownView.frame.size.width, 102)];
//self.timesScroll.backgroundColor=[UIColor whiteColor];
self.timesScroll.verticalScroll=NO;
self.timesScroll.dataSource=self;
self.timesScroll.maxScrollDistance=5;
self.timesScroll.contentInset=UIEdgeInsetsMake(0, self.timesScroll.frame.size.width/2-31, 0,self.timesScroll.frame.size.width/2-31 );
self.timesScroll.userInteractionEnabled=YES;
self.timesScroll.exclusiveTouch=YES;
dropDownView.frame=CGRectMake(0, 0, self.view.frame.size.width,_timesScroll.frame.origin.y+_timesScroll.frame.size.height+20);
[self viewWillAppear:YES];
[dropDownView addSubview:targetLbl];
[dropDownView addSubview:how_many_Lbl];
[dropDownView addSubview:hideViewBtn];
[dropDownView addSubview:_timesScroll];
}
else
{
[self hideView];
}
}
The method above is what I am using to create view.
Now my problem is that when that particular button(how_many_times_btn) is pressed again all views are first removed then added as you can see and scrollview starts from initial position but I want it show from where I left it last time how_many_times_btn was clicked.
Hope you can understand What I am trying to say....if not I am happy to elaborate furthur.
you can get the last position by delegate methods
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
nslog(#"%f %f",scrollView.contentOffset.x,scrollView.contentOffset.y);
}
and store the x and y value and set
scrollView.contentOffset = CGPointMake(x,y);
You can save contentOffsetto one variable of CGPoint. And use this variable 's value later to scroll the UIScrollview.
Something like below line of code :
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
contentOffset = scrollView.contentOffset;
}
When button pressed write below line of code:
self.timesScroll .contentOffset = contentOffset;
I'm using LTInfiniteScrollview in which there is a method 'reloadDataWithInitialIndex'
-(void)reloadDataWithInitialIndex:(NSInteger)initialIndex
{
for (UIView *view in self.scrollView.subviews) {
[view removeFromSuperview];
}
self.views = [NSMutableDictionary dictionary];
self.visibleViewCount = [self.dataSource numberOfVisibleViews];
self.totalViewCount = [self.dataSource numberOfViews];
[self updateSize];
_currentIndex = initialIndex;
self.scrollView.contentOffset = [self contentOffsetForIndex:_currentIndex];
[self reArrangeViews];
[self updateProgress];}
This method is called in 'viewWillAppear'
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.viewSize = CGRectGetWidth(self.view.frame) / Number_of_visibleViews;
self.timesScroll.delegate=self;
[self.timesScroll reloadDataWithInitialIndex:howIndex];}
I just passed previous index value here.

How to pass a UIButton's frame and origin between ViewControllers

I want to do an animation that zooms in from a calendar, specifically with the origin and frame size being that of the button that represent's today's date. Here is the code for determining the todayButton inside CalendarMonthView.m:
NSDate *date = (weekdayOffset > 0) ? [_monthStartDate dateByAddingTimeInterval:-1 * weekdayOffset * D_DAY] : _monthStartDate;
BOOL bEnabled = (weekdayOffset == 0);
CGRect buttonFrame = CGRectMake (0, 0, 81, 61);
int idx = -1 * weekdayOffset;
for (int y = 0; y < 6; y++) {
buttonFrame.origin.x = 0;
for (int x = 0; x < 7; x++) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = idx++;
[button setFrame:buttonFrame];
[button setBackgroundImage:[UIImage imageNamed:#"calendarFlyout_dayContainer_today.png"] forState:UIControlStateSelected];
[button setBackgroundImage:[UIImage imageNamed:#"calendarFlyout_selected.png"] forState:UIControlStateHighlighted];
button.titleLabel.font = [UIFont fontWithName:#"TitilliumWeb-Regular" size:18.0];
button.titleLabel.textAlignment = NSTextAlignmentCenter;
// TODO: optimize performance
int dayOfMonth = (int)[_calendar component:NSCalendarUnitDay fromDate:date];
if (dayOfMonth < prevDayOfMonth) {
bEnabled = !bEnabled;
}
prevDayOfMonth = dayOfMonth;
[button setTitle:[NSString stringWithFormat:#"%d", dayOfMonth] forState:UIControlStateNormal];
[button setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
[button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
[button setBackgroundImage:[UIImage imageNamed:((bEnabled) ? #"calendarFlyout_dayContainer_insideMonth.png"
: #"calendarFlyout_dayContainer_outsideMonth.png")]
forState:UIControlStateNormal];
// button.enabled = bEnabled;
button.selected = [date isToday];
if (button.selected == NO) {
button.highlighted = (_currentDayDate) ? [date isEqualToDateIgnoringTime:_currentDayDate] : NO;
} else {
// Set buttonHolder to today
}
[button addTarget:self action:#selector (dayButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
[_dayButtonsHolderView addSubview:button];
buttonFrame.origin.x += buttonFrame.size.width;
date = [date dateByAddingTimeInterval:D_DAY];
}
buttonFrame.origin.y += buttonFrame.size.height;
}
- (IBAction)dayButtonTapped:(id)sender
{
if (_delegate) {
UIButton *button = (UIButton *)sender;
NSDate *selectedDate = [_monthStartDate dateByAddingDays:button.tag];
[_delegate performSelector:#selector (calendarMonthView:dateSelected:) withObject:self withObject:selectedDate];
}
}
I want to get the frame of button and use it in this animation used in CalendarFlyoutView.m.
I'm new to iOS programming and I read up on some delegate information and passing information through segues, but it doesn't seem to help me here.
Any help would be greatly appreciated.
Use the transform feature.
// animate in year calendar
_yearCalendarView.hidden = NO;
self.curMonthView.monthLabel.hidden = YES;
CalendarMonthView *monthView = [CalendarMonthView new];
monthView.delegate = self;
CGRect buttonFrame = _currentDayButtonFrame;
_yearCalendarView.frame = CGRectMake(buttonFrame.origin.x, buttonFrame.origin.y + buttonFrame.size.height, buttonFrame.size.width, buttonFrame.size.height);
NSLog(#"_yearCalendarView frame: %#", NSStringFromCGRect(_yearCalendarView.frame));
_yearCalendarView.transform = CGAffineTransformMakeScale(0.01, 0.01);
[UIView animateWithDuration:kAnimation_ExpandCalendarDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
_yearCalendarView.transform = CGAffineTransformIdentity;
_yearCalendarView.frame = CGRectMake(_monthSwiperScrollView.frame.origin.x, _monthBarImageView.frame.size.height + 20, _monthSwiperScrollView.frame.size.width + 2, _yearFlyoutHeight);
} completion:^(BOOL finished) {
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector (yearCalendarAnimationDidStop:finished:context:)];
}];
If you are making a segue from the CalendarMonthView to the CalendarFlyoutView then you can just add this method to the CalendarMonthView.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
CalendarFlyoutView * view = segue.destinationViewController;
view.buttonFrame = button.frame;
}
and in your CalendarFlyoutView.h
#interface CalendarFlyoutView : UIViewController
#property CGRect buttonFrame;
#end
A couple of questions that need to be answered to best help you:
1) Are CalendarMonthView and CalendarFlyoutView UIView or UIViewController? You say view controller, but the class name says otherwise.
2) What is happening in the method 'dayButtonTapped'? Are you performing a segue there? Are you somehow creating or loading an instance of CalendarFlyoutView?
3) If you are not using a segue, are you somehow creating and add a child view controller for CalendarFlyoutView to CalendarMonthView?
4) What is the desired transition (your animation)? Does the CalendarFlyoutView start at the same size as the button, then fill the whole screen?
5) Is there a specific reason that you are setting the button's frame instead of constraining it in the correct place?
Once we know a little more about these questions we should be able to provide some more concrete suggestions.
It's still not very clear where the actual animation code is being run. In your question, you state "I want to get the frame of button and use it in this animation used in CalendarFlyoutView.m." which seems to imply the animation code is in CalendarFlyoutView, which seems like an odd place for it.
There are a few things I could recommend, given my incomplete understanding of the whole of the code:
1) Move the animation code to the dayButtonTapped method, where you already have a reference to the button, and therefore it's frame. It also seems like a more logical place for it.
2) Where ever you are instantiating the CalendarFlyoutView (not shown in the code you posted), assign the button frame there.
3) If there is only a single instance of CalendarFlyoutView and you have a reference to it in CalendarMonthView, you could assign a property of the flyout view that is the frame of the button you already have a reference to in the dayButtonTapped method.
I have to add though, that these are just work arounds to what seems like a structural issue. Is this a reusable control you are trying to write? Why did you decide to not use the storyboard or view controllers? Why are you using frames and absolute sizes and positions at all instead of letting auto layout do it's job (you will bang your head against the wall for hours trying to tweak everything to run correctly in all orientations and all possible device screen sizes).

Creating a level select screen with circular buttons

I'm trying to create a level select screen for a game I'm making which consists of a grid of circular buttons with level numbers in them. At any one time one button should be selected and displayed with a filled background instead of just an outline.
My initial implementation was to create a view which looked something like this:
#implementation LevelView
- (void)drawRect:(CGRect)rect {
int i = 1;
for (int row = 0; row < NUM_ROWS; row++) {
for (int col = 0; col < NUM_COLS; col++) {
// Calculate frame of the circle for this level.
if (i == selected) {
// Use CoreGraphics to draw filled circle with text.
}
else {
// Use CoreGraphics to draw outlined circle with text.
}
i++;
}
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for (UITouch *touch in touches) {
// Get the level that the touch is in the circle of.
int level = [self levelForPoint:[touch locationInView:self]];
selected = level;
[self setNeedsDisplay];
}
}
#end
The problem with this is that the selecting and unselecting of the various buttons are not animated (fade in, fade out) like the circular buttons in the iOS7 lock screen or Phone app.
Is there a better/easier way that I can accomplish this?
In response to your request to fade button state: we subclassed UIButton and overrode the setHighlighted: method like so:
- (void) setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
if (highlighted) {
[self setBackgroundColor:[UIColor whiteColor]];
} else {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.4f];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[self setBackgroundColor:nil];
[UIView commitAnimations];
}
}
As promised here is my way of tackling the problem. Let's first create a grid of UIButtons in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
for (NSUInteger i = 0; i < 10; ++i)
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = i;
[button setImage:[UIImage imageNamed:#"normal_image"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"selected_image"] forState:UIControlStateSelected];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
//You can assign a button frame here or in viewWillLayoutSubviews or viewDidLayoutSubviews
button.frame = CGRectMake(your...frame);
[self.view addSubview:button];
[buttons addObject:button]; //NSMutableArray ivar containing our buttons
}
And that takes care of placing our buttons. You can use 2 for loops to make a grid of your choice. As for the image I'm assuming that you're using pre-baked images (you can pre-bake for retina and non retina separately). If you want to render it programatically let me know and I will edit the post.
As for the selection logic we implement the selector buttonTapped:
- (void)buttonTapped:(UIButton *)sender
{
for (UIButton *button in buttons) //Resets all selected states if there were any
button.state = UIControlStateNormal;
sender.state = UIControlStateSelected;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Hey" message:[NSString stringWithFormat:#"You touches the %ldth button.", (long)sender.tag + 1] delegate:nil cancelButtonTitle:#"Cool" otherButtonTitles:nil]
[alert show];
}
EDIT as for the animation purposes you can take a look at the link here or you can use an array of imageViews with attached UITapGestureRecognizers.
EDIT 2 A basic way on how to draw images for your buttons:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(buttonWidth, buttonHeight), NO, 0.0f); //Makes a graphics context that is not opaque and uses the screen scale (so you do not need to worry about retina or non retina)
[[UIColor redColor] set];
UIRectFill(CGRectMake(0.0f, 0.0f, buttonWidth, buttonHeight);
NSDictionary *drawingAttributes = #{NSFontAttributeName: [UIFont systemFontOfSize:15.0f], NSForegroundColorAttributeName: [UIColor blueColor]};
[#"Normal state" drawAtPoint:CGPointZero withAttributes:drawingAttributes];
UIImage *normalStateImate = UIGraphicsGetImageFromCurrentImageContext();
[[UIColor greenColor] set];
UIRectFill(CGRectMake(0.0f, 0.0f, buttonWidth, buttonHeight);
[#"Selected state" drawAtPoint:CGPointZero withAttributes:drawingAttributes];
UIImage *selectedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
And then you would apply the images in the same way as I have written above. How to draw pretty images and whatnot is another topic and if you want help on that I suggest googling or asking another question. If you ever feel like changing the images you would redraw them and apply them again to your buttons.

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