Why accessibilityElementDidBecomeFocused is not firing for UIBarButtonItem - ios

I'd like to know when BarButton items gets the accessibility focus or loses the accessibility focus and so I implemented the informal protocol methods of UIAccessibilityFocus but it's not still firing.
extension UIBarButtonItem {
override open func accessibilityElementDidBecomeFocused() {
if self.accessibilityElementIsFocused() {
print("My element has become focused.")
}
}
override open func accessibilityElementDidLoseFocus() {
if self.accessibilityElementIsFocused() {
print("My element has lost focus.")
}
}
override open func accessibilityElementIsFocused() -> Bool {
if (self.accessibilityIdentifier == "hamburger") {
return true
} else {
return false
}
}
I imported the swift file into viewcontroller as well
#import "Sample-Swift.h"
then I tried subclassing and implemented the methods that also didn't work
.h header file
#import <UIKit/UIKit.h>
#import <UIKit/UIAccessibility.h>
NS_ASSUME_NONNULL_BEGIN
#interface HamburgerButton : UIBarButtonItem
#end
NS_ASSUME_NONNULL_END
.m implementation file
#implementation HamburgerButton
- (BOOL)isAccessibilityElement
{
return YES;
}
- (void)accessibilityElementDidBecomeFocused {
if ([self accessibilityElementIsFocused]) {
NSLog(#"My element has become focused.");
}
}
- (void)accessibilityElementDidLoseFocus {
if ([self accessibilityElementIsFocused]) {
NSLog(#"My element has lost focus.");
}
}
- (BOOL)accessibilityElementIsFocused {
if ([self.accessibilityIdentifier isEqualToString:#"hamburger"]) {
return YES;
} else {
return NO;
}
}
#end
Here is the implementation in view controller
HamburgerButton *leftButton = [[HamburgerButton alloc]
initWithTitle:#"Hamburger"
style:UIBarButtonItemStylePlain
target:self
action:#selector(flipView:)];
leftButton.accessibilityIdentifier=#"Hamburger";
leftButton.tag = 88;
leftButton.isAccessibilityElement = YES;
HamburgerButton *rightButton = [[HamburgerButton alloc]
initWithTitle:#"Chat"
style:UIBarButtonItemStylePlain
target:self
action:#selector(flipView:)];
rightButton.accessibilityIdentifier=#"Chat";
rightButton.tag = 89;
rightButton.isAccessibilityElement = YES;
self.navigationItem.leftBarButtonItem = leftButton;
self.navigationItem.rightBarButtonItem = rightButton;
Even though the focus comes and goes away from bar button , I'm not getting the call back on accessibilityElementDidBecomeFocused
Any ideas what could be done to get accessibilityElementDidBecomeFocused firing ?
Update 1:
I could achieve this functionality by the means of notification observer but it's not giving enough information about the receiver of focus so couldn't differentiate one bar button from the other.
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(hamburgerGotFocus:) name:UIAccessibilityElementFocusedNotification object:nil];
and find the selector method below
-(void)hamburgerGotFocus:(NSNotification *) notification{
NSLog(#"Focus:%#",notification);
UIView *receiver = notification.userInfo[#"UIAccessibilityFocusedElementKey"];
if(receiver!=nil){
NSString *strElement = [[NSString alloc]initWithFormat:#"%#",notification.userInfo[#"UIAccessibilityFocusedElementKey"]];
if([strElement containsString:#"UIButtonBarButton"]){
}
}
}
Here is the log of notification
2022-10-12 18:57:03.992859+0530 Sample[32427:1579550] Focus:NSConcreteNotification 0x280ac9980 {name = UIAccessibilityElementFocusedNotification; userInfo = {
UIAccessibilityAssistiveTechnologyKey = UIAccessibilityNotificationVoiceOverIdentifier;
UIAccessibilityFocusedElementKey = "<_UIButtonBarButton: 0x10690fce0; frame = (0 0; 49 44); tintColor = UIExtendedSRGBColorSpace 0 1 0 1; gestureRecognizers = <NSArray: 0x2804262e0>; layer = <CALayer: 0x280afa9e0>>";
}}
Update 2:
I tried doing this with UIlabel using Category and subclassing both worked
#interface SampleLabel : UILabel
#end
#implementation SampleLabel
- (void)accessibilityElementDidBecomeFocused {
NSLog(#"accessibilityIdentifier:%#",self.accessibilityIdentifier);
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, #"sample label from subclass");
}
- (void)accessibilityElementDidLoseFocus {
if ([self accessibilityElementIsFocused]) {
NSLog(#"My element has lost focus.subclass");
}
}
- (BOOL)accessibilityElementIsFocused {
return YES;
}
#end
By means of category
#interface UILabel (SampleLabel1)
#end
#implementation UILabel (SampleLabel1)
- (void)accessibilityElementDidBecomeFocused {
NSLog(#"accessibilityIdentifier:%#",self.accessibilityIdentifier);
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, #"sample label from category");
}
- (void)accessibilityElementDidLoseFocus {
if ([self accessibilityElementIsFocused]) {
NSLog(#"My element has lost focus.Category");
}
}
- (BOOL)accessibilityElementIsFocused {
return YES;
}
#end
I'm wondering whether accessibilityElementDidBecomeFocused is not compatible with UIBarButtonItem ?
FYI:
I'm following this tutorial to implement accessibilityElementDidLoseFocus.

accessibilityElementDidBecomeFocused
is not firing for bar button but it's firing for UIButton. So I just created a bar button using UIButton like below and the problem is fixed
// Image
UIImage *img1 = [UIImage imageNamed:#"img1"];
// Button
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
[btn1 setImage:img1 forState:UIControlStateNormal];
btn1.frame = CGRectMake(0.0, 0.0, img1.size.width, img1.size.height);
btn1.tintColor = [UIColor greenColor];
[btn1 addTarget:self action:#selector(barButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
// Bar button
UIBarButtonItem *barButton1 = [[UIBarButtonItem alloc]initWithCustomView:btn1];
// Accessibility element
btn1.isAccessibilityElement = true;
img1.isAccessibilityElement = false;
btn1.accessibilityLabel = #"some accessibility text";
btn1.accessibilityTraits = UIAccessibilityTraitNone;
self.navigationItem.rightBarButtonItem = barButton1;

Related

How to get custom TableViewCell value on view controller without using button and set it after reload table in objective c

I am new in iOS and I am facing problem regarding to get the value from custom table view cell to view controller. I am using rate view for rating and I am checking if value of rate is less then 3 then it show have to enter text in the text view and I want to get value in view controller
My code is like this
CustomTableviewcell.h
#interface NextTableview : UITableViewCell<RateViewDelegate,UITextViewDelegate>
{
NSString *StatusValue;
UILabel *lbl;
}
#property(nonatomic,strong) IBOutlet UILabel *staticlbl;
#property(nonatomic,strong) IBOutlet UITextView *commenttxtview;
#property(nonatomic,strong) IBOutlet UILabel *Kpiidlbl;
#property (weak, nonatomic) IBOutlet RateView *rateView;
#property (weak, nonatomic) IBOutlet UILabel *statusLabel;
#end
CustomTableviewcell.m
#synthesize rateView,staticlbl,statusLabel,commenttxtview,Kpiidlbl;
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
commenttxtview.layer.borderWidth = 0.70f;
commenttxtview.layer.borderColor = [[UIColor blackColor] CGColor];
commenttxtview.delegate=self;
UIToolbar* doneToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 50)];
doneToolbar.barStyle = UIBarStyleBlackTranslucent;
doneToolbar.items = [NSArray arrayWithObjects:
[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
[[UIBarButtonItem alloc]initWithTitle:#"Done" style:UIBarButtonItemStyleDone target:self action:#selector(doneButtonClickedDismissKeyboard)],
nil];
[doneToolbar sizeToFit];
commenttxtview.inputAccessoryView = doneToolbar;
lbl = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 0.0,90.0, 34.0)];
[lbl setText:#"Enter Text"];
[lbl setFont:[UIFont systemFontOfSize:12]];
[lbl setBackgroundColor:[UIColor clearColor]];
[lbl setTextColor:[UIColor lightGrayColor]];
commenttxtview.delegate = self;
[commenttxtview addSubview:lbl];
statusLabel.hidden=YES;
commenttxtview.hidden=YES;
// Do any additional setup after loading the view from its nib.
self.rateView.notSelectedImage = [UIImage imageNamed:#"not_selected_star#2x.png"];
self.rateView.halfSelectedImage = [UIImage imageNamed:#"half_selected_star#2x.png"];
self.rateView.fullSelectedImage = [UIImage imageNamed:#"selected_star#2x.png"];
self.rateView.rating = 0;
self.rateView.editable = YES;
self.rateView.maxRating = 5;
self.rateView.delegate = self;
Kpiidlbl.hidden=YES;
}
-(void)doneButtonClickedDismissKeyboard
{
[commenttxtview resignFirstResponder];
// commenttxtview.hidden=YES;
}
- (void)textViewDidEndEditing:(UITextView *)theTextView
{
if (![commenttxtview hasText]) {
lbl.hidden = NO;
}
}
- (void) textViewDidChange:(UITextView *)textView
{
if(![commenttxtview hasText]) {
lbl.hidden = NO;
}
else{
lbl.hidden = YES;
}
}
- (void)rateView:(RateView *)rateView ratingDidChange:(int)rating {
self.statusLabel.text = [NSString stringWithFormat:#"%d", rating];
NSLog(#"Rating value =%#",self.statusLabel.text);
StatusValue=statusLabel.text;
NSLog(#"Status Value String =%#",StatusValue);
// Hear I am getting value of rating..in StatusValue..
int status=[StatusValue intValue];
if(status<=3)
{
commenttxtview.hidden=NO;
}
else{
commenttxtview.hidden=YES;
}
}
How can I get label and textview value in viewcontroller and I want to set rate view value to rate view after reload table.
Hear in the Image i am getting five star and If I click on less then 3 star it should have to write comment.I am taking rate value in the label.How can I get both label and textvalue in view controller.Please tell me to update question if you want more data.Thanks in Advance!
So write this in your CustomTableViewCell.h
#property(nonatomic,assign)NSInteger selectedRating;
everytime when the rating is updated in the Cell, you have to change the value of this variable.
In the viewController it depends on when you want to get the value of this cell. For example in this method:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
CustomTableViewCell *yourCell = (CustomerTableViewCell*)[tableView cellForRowAtIndexPath:indexPath];
NSLog(#"%lu",yourCell.selectedRating);
}
Let me know if this was helpful!
Another way without a variable is to read the NSString in the statusLabel and change the string to a NSInteger variable...
If you are using your delegate...
Then implement the delegate RateViewDelegatein the viewController. The delegate is called in the class to which it is assigned...
Try this
You have to add the #property (nonatomic, weak) NSObject<RateViewDelegate>* delegate;
to the header file of your CustomTableViewCell.h in the method
-(UITableViewCell) cellForRowAtIndexPath....
you have to assign the delegate like this
cell.delegate = self;
Now modify your cell delegate method to this
- (void)rateView:(RateView *)rateView ratingDidChange:(int)rating {
self.statusLabel.text = [NSString stringWithFormat:#"%d", rating];
NSLog(#"Rating value =%#",self.statusLabel.text);
StatusValue=statusLabel.text;
NSLog(#"Status Value String =%#",StatusValue);
// Hear I am getting value of rating..in StatusValue..
int status=[StatusValue intValue];
if(status<=3)
{
commenttxtview.hidden=NO;
}
else{
commenttxtview.hidden=YES;
}
if([self.delegate respondsToSelector:#selector(rateView:ratingDidChange:)]){
[self.delegate rateView:rateView ratingDidChange:rating];
}
}

UITextField, hided Keyboard, TopBar still there

i have a VC with a UITextField. Thats my Code:
.h
#interface GameOverViewController : UIViewController <UITextFieldDelegate> {
}
.m
#implementation UITextField (DisableCopyPaste)
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
return NO;
return [super canPerformAction:action withSender:sender];
}
#end
#implementation GameOverViewController
NSString *text1;
text1 = #"";
UIView* testView = [[UIView alloc] init];
_nameField.inputView = testView;
[_nameField becomeFirstResponder];
_nameField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
This is for hiding the Keyboard:
UIView* testView = [[UIView alloc] init];
_nameField.inputView = testView;
But now there is still the TopBar of the Keyboard on my View:
If i take out this two lines of code, the whole Keyboard appears:
i can't get it hided. i don't want this bar.
if i disable the UITextField, the Bar is gone.
Found the solution:
in iOS 9 there is an UITextInoutAssistantItem, and here is my Solution for this Bar:
if ([self respondsToSelector:#selector(inputAssistantItem)]) {
UITextInputAssistantItem *item = [self inputAssistantItem];
item.leadingBarButtonGroups = #[];
item.trailingBarButtonGroups = #[];
}

Switch selectedState between three UIButton on the same tab

I have 3 UIButton on the same tab.
The problem :
When I click on button1, button2 and button3 all of them got selected; however what I want is when I select (for example) button2, the other buttons be unselected (like radio buttons on html).
Any idea on how I can solve this issue ?
Here is my code :
- (IBAction)button1:(UIButton *)sender {
sender.selected = !sender.selected;
}
- (IBAction)button2:(UIButton *)sender {
sender.selected = !sender.selected;
}
- (IBAction)button3:(UIButton *)sender {
sender.selected = !sender.selected;
}
There are a couple of straightforward solutions. Maybe they are not optimal, but you can use them:
First solution:
Link your buttons with corresponding properties:
#property (weak, nonatomic) IBOutlet UIButton *button1;
#property (weak, nonatomic) IBOutlet UIButton *button2;
#property (weak, nonatomic) IBOutlet UIButton *button3;
In each action change the selected property for each button:
- (IBAction)button1Clicked:(UIButton *)sender {
_button1.selected = YES;
_button2.selected = NO;
_button3.selected = NO;
}
Second solution:
Add all your buttons in NSArray:
NSArray *buttonsArray = [[NSArray alloc] initWithObjects: button1, button2, button3, nil];
Then link all the buttons with one action:
- (IBAction)buttonClicked:(UIButton *)sender {
for (UIButton *btn in _buttonsArray) {
if (btn == sender)
btn.selected = YES;
else
btn.selected = NO;
}
}
Don't forget to make your NSArray accessible for all the methods of the class, for example by creating a corresponding property:
#property (weak, nonatomic) NSArray *buttonsArray;
Third solution:
You can change the tag property for each button (through Interface Builder or in code):
_button1.tag = 1;
_button2.tag = 2;
_button3.tag = 3;
Then link all the buttons with one action (just like in previous example):
- (IBAction)buttonClicked:(UIButton *)sender {
if (sender.tag == 1) {
_button1.selected = YES;
_button2.selected = NO;
_button3.selected = NO;
} else if (sender.tag == 2) {
_button1.selected = NO;
_button2.selected = YES;
_button3.selected = NO;
} else if (sender.tag == 3) {
_button1.selected = NO;
_button2.selected = NO;
_button3.selected = YES;
}
}
Fourth solution: (by #velmurugan-s)
Use one action for all the buttons and set their default state to selected = NO:
- (IBAction)buttonClicked:(UIButton *)sender {
_button1.selected = NO;
_button2.selected = NO;
_button3.selected = NO;
sender.selected = YES;
}
Summary:
I'd recommend you to use the second solution as the most optimal one.
What you can do is connect button1,button2,button3 to same button123 in interface section give them appropriate tags
- (IBAction)button123:(UIButton *)sender;
In its description use below code
- (IBAction)button123:(UIButton *)sender {
if (sender.tag == 111)
{
sender.selected = !sender.selected;
if (sender.selected)
{
self.button2.selected = NO;
self.button3.selected = NO;
}
}
else if (sender.tag == 222)
{
sender.selected = !sender.selected;
if (sender.selected)
{
self.button1.selected = NO;
self.button3.selected = NO;
}
}
else if (sender.tag == 333)
{
sender.selected = !sender.selected;
if (sender.selected)
{
self.button1.selected = NO;
self.button2.selected = NO;
}
}
- (IBAction)button1:(UIButton *)sender {
btn1.selected = YES;
btn2.selected = NO;
btn3.selected = NO;
}
- (IBAction)button2:(UIButton *)sender {
btn1.selected = NO;
btn2.selected = YES;
btn3.selected = NO;
}
- (IBAction)button3:(UIButton *)sender {
btn1.selected = NO;
btn2.selected = NO;
btn3.selected = YES;
}
You had change the selected sate for a button that you have selected,You need to change all other button state too.

toolbar previous and next button logic

I'm using the following code to move to the next field using the UITextField delegate and also I'm adding a toolbar to the keyboard with the previous, next and ok buttons. The code is working fine.
Like you see the keyboard return button logic is pretty generic, using the UITextField tags, and that's good because I'm gonna use the piece of code all around. Now I will need to write the previous and next buttons logic, and I'm lost. Any ideas?
UPDATE (complete code, with some modifications, thanks to #8vius that spent some time with me in the chat to make it work):
//
// SigninViewController.m
//
#import "SigninViewController.h"
#implementation SigninViewController
#synthesize firstResponder = _firstResponder;
#synthesize toolbar;
#synthesize email;
#synthesize password;
- (void)move:(UIBarButtonItem*)sender {
NSInteger tag = self.firstResponder.tag;
if ([sender.title isEqualToString:#"Anterior"]) {
tag -= 1;
} else if ([sender.title isEqualToString:#"Próximo"]) {
tag += 1;
}
UITextField *nextTextField = (UITextField*)[self.view viewWithTag:tag];
if (nextTextField && tag > 0) {
[nextTextField becomeFirstResponder];
} else {
[self.firstResponder resignFirstResponder];
self.firstResponder = nil;
}
}
- (void)ok:(id)sender {
[self.view endEditing:YES];
self.firstResponder = nil;
}
- (void)textFieldDidBeginEditing:(UITextField*)textField {
self.firstResponder = textField;
}
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
NSInteger tag = textField.tag + 1;
UITextField *nextTextField = (UITextField*)[self.view viewWithTag:tag];
if (nextTextField) {
[nextTextField becomeFirstResponder];
} else {
[textField resignFirstResponder];
self.firstResponder = nil;
}
return NO;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.email.delegate = self;
self.password.delegate = self;
if (self.toolbar == nil)
{
self.toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44)];
UIBarButtonItem* previous = [[UIBarButtonItem alloc] initWithTitle:#"Anterior" style:UIBarButtonItemStyleBordered target:self action:#selector(move:)];
UIBarButtonItem* next = [[UIBarButtonItem alloc] initWithTitle:#"Próximo" style:UIBarButtonItemStyleBordered target:self action:#selector(move:)];
UIBarButtonItem* space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:(UIBarButtonSystemItemFlexibleSpace) target:nil action:nil];
UIBarButtonItem* ok = [[UIBarButtonItem alloc] initWithTitle:#"Ok" style:UIBarButtonItemStyleBordered target:self action:#selector(ok:)];
[self.toolbar setItems:[[NSArray alloc] initWithObjects:previous, next, space, ok, nil]];
[self.toolbar setTranslucent:YES];
[self.toolbar setTintColor:[UIColor blackColor]];
}
for (UIView* view in self.view.subviews) {
if ([view isKindOfClass:[UITextField class]]) {
[(UITextField*)view setInputAccessoryView:toolbar];
}
}
}
- (void)viewDidUnload {
self.email = nil;
self.password = nil;
[super viewDidUnload];
}
#end
It's quite simple, when you load your view you set the tag property on your text fields depending on the order you want them in, then you have to just traverse the tag element on the fields:
- (void)toggleTextfield:(UIBarButtonItem *)sender {
NSInteger nextTag = self.firstResponder.tag;
if ([sender.title isEqualToString:#"Previous"] && nextTag > 1) {
nextTag -= 1
} else if ([sender.title isEqualToString:#"Next"]) {
nextTag += 1;
}
UITextField *nextTextField = (UITextField *)[self.view viewWithTag:nextTag];
if (nextTextField) {
[nextTextField becomeFirstResponder];
}
}
And keep track of who is the first responder:
-(void)textFieldDidBeginEditing:(UITextField *)textField {
self.firstResponder = textField;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
self.firstResponder = nil;
return YES;
}
And when you load your view, you bind the buttons to the toggle action:
UIBarButtonItem *previousButton = [[UIBarButtonItem alloc] initWithTitle:#"Previous"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(toggleTextfield:)];
UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithTitle:#"Next"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(toggleTextfield:)];
In my case, for instance, I set up my text fields inside a table view, so in my cellForRowAtIndexPath method I set the tag property to be the row of the indexPath.
EDIT: You have to set the firstResponder property for it to work.
In your .h file:
#property UIView *firstResponder
In your .m file:
#synthesize firstResponder = _firstResponder;

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