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
Related
I have a UIButton inside a UIView, when the UIButton is selected I would like have a background colour of dark gray...is this possible?
UIView *toolbar = [[UIView alloc]initWithFrame:CGRectMake(10, 50, 40, 160)];
[toolbar setBackgroundColor:[UIColor colorWithWhite: 0.70 alpha:0.5]];
toolbar.layer.cornerRadius = 5;
UIButton *penTool = [UIButton buttonWithType:UIButtonTypeCustom];
penTool.frame = CGRectMake(5, 0, 30, 30);
[penTool setImage:[UIImage imageNamed:#"pen-but" inBundle:currentBundle compatibleWithTraitCollection:nil] forState:UIControlStateNormal];
[penTool addTarget:self action:#selector(drawButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
penTool.autoresizingMask = UIViewAutoresizingNone;
penTool.exclusiveTouch = YES;
penTool.tag = 1;
[toolbar addSubview:penTool];
What if you can do this?
[button setBackgroundColor:[UIColor greenColor]
forState:UIControlStateSelected];
Let's give a great API for every state, not just selected, just like UIButton default API's ability to change image and title for every state.
BGButton.h
#import <UIKit/UIKit.h>
#interface BGButton : UIButton
- (void)setBackgroundColor:(UIColor *)backgroundColor
forState:(UIControlState)state;
#end
BGButton.m
#import "BGButton.h"
#implementation BGButton {
NSMutableDictionary *_stateBackgroundColor;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_stateBackgroundColor = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:backgroundColor];
/*
* If user set button.backgroundColor we will set it to
* normal state's backgroundColor.
* Check if state is on normal state to prevent background being
* changed for incorrect state when updateBackgroundColor method is called.
*/
if (self.state == UIControlStateNormal) {
[self setBackgroundColor:backgroundColor forState:UIControlStateNormal];
}
}
- (void)setBackgroundColor:(UIColor *)backgroundColor
forState:(UIControlState)state {
NSNumber *key = [NSNumber numberWithInt:state];
[_stateBackgroundColor setObject:backgroundColor forKey:key];
}
/*
* state is synthesized from other flags. So we can't use KVO
* to listen for state changes.
*/
- (void)setSelected:(BOOL)selected {
[super setSelected:selected];
[self stateDidChange];
}
- (void)setEnabled:(BOOL)enabled {
[super setEnabled:enabled];
[self stateDidChange];
}
- (void)setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
[self stateDidChange];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self stateDidChange];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
[self stateDidChange];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self stateDidChange];
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
[self stateDidChange];
}
- (void)stateDidChange {
[self updateBackgroundColor];
}
- (void)updateBackgroundColor {
NSNumber *key = [NSNumber numberWithInt:self.state];
self.backgroundColor = [_stateBackgroundColor objectForKey:key];
}
#end
You can use it like the default UIButton API
BGButton *button = [[BGButton alloc] init];
[button setBackgroundColor:[UIColor greenColor]
forState:UIControlStateSelected];
[button setBackgroundColor:[UIColor blueColor]
forState:UIControlStateHighlighted];
[button setBackgroundColor:[UIColor orangeColor]
forState:UIControlStateDisabled];
Even if you use
button.backgroundColor = [UIColor redColor];
You will stay safe, it will be translated into
[button setBackgroundColor:[UIColor redColor]
forState:UIControlStateNormal];
if you have this code
[self.view addSubview:toolbar];
then just implement your button selector
-(void)drawButtonTapped:(UIButton*)button{
if (button.selected) {
button.backgroundColor = [UIColor clearColor];
button.selected = NO;
}else{
button.backgroundColor = [UIColor darkGrayColor];
button.selected = YES;
}
}
I don't know why guys would go through such trouble when we have this functionality built-in inside UIButton. All you need to do is use UIButtonTypeSystem That's all.
[UIButton buttonWithType:UIButtonTypeSystem]
Seriously, there's nothing more to it. Have a look at it's behavior in the screenshots.
INTERFACE BUILDER
RUN TIME
Want to change color, set tintColor to color of your choice.
ADVANTAGES
Covers titles with dynamic lengths. Not applied on all button width.
When selected, title color is transparent, your background can be seen through, try to use an image as a background.
Let's hope you have at least iOS7 as deployment target. UIButtonTypeSystem was introduce with revised UI design guidelines of iOS7 and has been there for almost 3 years.
Good Luck!
I used a UIButton subclass here:
#import "CustomButton.h"
#interface CustomButton()
#property (nonatomic,strong) UIColor *originalColor;
#end
#implementation CustomButton
- (void)didMoveToSuperview
{
self.originalColor = self.superview.backgroundColor;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.superview.backgroundColor = [UIColor grayColor];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
if ([self hitTest:location withEvent:nil]) {
self.superview.backgroundColor = [UIColor grayColor];
self.originalColor = self.superview.backgroundColor;
}
else
{
self.superview.backgroundColor = self.originalColor;
}
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.superview.backgroundColor = self.originalColor;
}
#end
If you subclass your button from this class, It should give you the desired effect. This also gives you the highlight effect. If you wish you can disable it by removing the line on touchesBegan.
Just try to Override the UIButton with the following Method...
- (void)setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
if (highlighted) {
self.backgroundColor = [UIColor darkGray];
}
}
I think you'll be want to change the selectedImage for flexibility not the background.
Add another image (e.g. pen-but-selected) in the project. Add this 2nd line for setImage:
[penTool setImage:[UIImage imageNamed:#"pen-but-selected" inBundle:currentBundle compatibleWithTraitCollection:nil] forState:UIControlStateSelected];
- (void)drawButtonTapped:(UIButton *)button {
button.selected = !button.selected;
}
If you still want to change background color. #jamil's answer would work. Make sure the buttonImage has transparent part so you can see the buttonBackground through the buttonImage.
Try using a boolean, or some other reference to the selection state of the button. You could also make use of a 'highlightColour' as well as the 'selectedColour' if you wanted closer control (should probably do this for better UX).
bool buttonIsSelected = false;
UIColor * selectedColour = [UIColor redColor];
UIColor * defaultColour = [UIColor blueColor];
UIButton * button = [UIButton new];
button.frame = CGRectMake(0,0,100,100);
button.backgroundColor = [UIColor redColor];
[self.view addSubview:button];
[button addTarget:self action:#selector(buttonTouchUp:) forControlEvents:UIControlEventTouchUpInside];
[button addTarget:self action:#selector(buttonTouchCancel:) forControlEvents:UIControlEventTouchCancel];
[button addTarget:self action:#selector(buttonTouchDown:) forControlEvents:UIControlEventTouchDown];
-(void)buttonTouchUp:(UIButton *)button {
if (buttonIsSelected){
buttonIsSelected = false;
button.backgroundColor = defaultColour;
} else {
buttonIsSelected = true;
button.backgroundColor = selectedColour;
}
}
-(void)buttonTouchCancel:(UIButton *)button {
button.backgroundColor = defaultColour;
if (buttonIsSelected){
button.backgroundColor = selectedColour;
}
}
-(void)buttonTouchDown:(UIButton *)button {
button.backgroundColor = selectedColour;
}
-(void)OnSelect:(UIButton*)button{
if (button.selected) {
button.backgroundColor = [UIColor blue];
button.selected = NO;
}else{
button.backgroundColor = [UIColor darkGrayColor];
button.selected = YES;
}
}
use the above this will work..
-(void)buttonTapped:(UIButton*)button{
button.selected =! button.selected;
if (button.selected)
button.backgroundColor = [UIColor grayColor];
else
button.backgroundColor = [UIColor clearColor];
}
I'm having a hard time finding the right documentation for how to handle touch events in order to support similar behavior to the keyboard.
What I want is a button that when I long press it, it shows a custom view controller above the button, but I want the user to be able to drag their finger to one of the other buttons (without taking their finger off the screen).
I have the button with a long press and it's custom view controller all setup and working. What I can't figure is how to support dragging from the first button over to the other button in the view controller to be able to select it.
I've tried using a subclassed UIButton where I tried this:
[self addTarget:self action:#selector(onDragOver:) forControlEvents:UIControlEventTouchDragEnter];
But that doesn't work.
I also found this question How to track button selection after long press? which is precisely the functionality I'm trying to duplicate. But there are no answers.
Here's my solution. The trick is you have to use hitTest:.
First you add a gesture recognizer to the button that is a normal button - the button that you want to open a context menu / custom view controller.
Then in your gesture recognizer callback, you use hitTest: to figure out if the user is over a custom button of yours and update it's state manually.
- (id) init {
//add a long press gesture recognizer
UILongPressureGestureRecognizer * gesture = [[UILongPressureGestureRecognizer alloc] initWithTarget:self action:#selector(onLongTap:)];
[self.myButton addGestureRecognizer:gesture];
}
- (void) onLongTap:(UIGestureRecognizer *) gesture {
if(gesture.state == UIGestureRecognizerStateBegan) {
//display your view controller / context menu over the button
}
if(gesture.state == UIGestureRecognizerStateEnded) {
//gesture stopped, use hitTest to find if their finger was over a context button
CGPoint location = [gesture locationInView:self.view];
CGPoint superviewLocation = [self.view.superview convertPoint:location fromView:self.view];
UIView * view = [self.view.superview hitTest:superviewLocation withEvent:nil];
if([view isKindOfClass:[MMContextMenuButton class]]) {
//their finger was over my custom button, tell the button to send actions
MMContextMenuButton * button = (MMContextMenuButton *) view;
[self hideAndSendControlEvents:UIControlEventTouchUpInside];
if(self.draggedContextMenuButton == button) {
self.draggedContextMenuButton = nil;
}
}
if(self.draggedContextMenuButton) {
[self sendActionsForControlEvents:UIControlEventTouchUpInside];
}
self.draggedContextMenuButton = nil;
}
if(gesture.state == UIGestureRecognizerStateChanged) {
//gesture changed, use hitTest to see if their finger
//is over a button. Manually have to tell the button
//that it should update it's state.
CGPoint location = [gesture locationInView:self.view];
CGPoint superviewLocation = [self.view.superview convertPoint:location fromView:self.view];
UIView * view = [self.view.superview hitTest:superviewLocation withEvent:nil];
if([view isKindOfClass[MMContextMenuButton class]]) {
MMContextMenuButton * button = (MMContextMenuButton *) view;
if(self.draggedContextMenuButton != button) {
[self.draggedContextMenuButton dragOut];
}
self.draggedContextMenuButton = button;
[button dragOver];
}
}
}
//////////////
#import "MMContextMenuButton.h"
#import "MMContextMenus.h"
#implementation MMContextMenuButton
- (id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
self.layer.cornerRadius = 4;
self.adjustsImageWhenHighlighted = FALSE;
self.adjustsImageWhenDisabled = FALSE;
self.backgroundColor = [UIColor clearColor];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
[self setTitleColor:[UIColor colorWithRed:0.435 green:0.745 blue:0.867 alpha:1] forState:UIControlStateNormal];
[self addTarget:self action:#selector(onHighlight:) forControlEvents:UIControlEventTouchDown];
[self addTarget:self action:#selector(onRelease:) forControlEvents:UIControlEventTouchUpOutside&UIControlEventTouchUpOutside];
return self;
}
- (void) onHighlight:(id) sender {
self.backgroundColor = [UIColor colorWithRed:0.435 green:0.745 blue:0.867 alpha:1];
}
- (void) onRelease:(id) sender {
self.backgroundColor = [UIColor clearColor];
}
- (void) hideAndSendControlEvents:(UIControlEvents) events {
[self dragOut];
[self sendActionsForControlEvents:events];
[[MMContextMenus instance] hideContextMenus];
}
- (void) dragOver {
self.highlighted = TRUE;
self.backgroundColor = [UIColor colorWithRed:0.435 green:0.745 blue:0.867 alpha:1];
}
- (void) dragOut {
self.highlighted = FALSE;
self.backgroundColor = [UIColor clearColor];
}
#end
In my app I want to programmatically add textfield below another if required on click of a button. I already had provided two textFields. if a user want to add another textfield he can do so by clicking a button. I have already written code to obtain the textfield but the problem is that it overlaps the already designed textFields. How can I do it?
Is there any way through which I can get the x and Y co-ordinates of already designed textfield so that I can place new textField relative to those co-ordinates.
This code add textField to view dynamically when every click action on button
ExampleViewController.h
#import <UIKit/UIKit.h>
#interface ExampleViewController :UIViewController<UITextFieldDelegate>
#property int positionY;
#property int fieldCount;
#property (strong,nonatomic) UIScrollView *scroll;
#end
ExampleViewController.m
#import "ExampleViewController.h"
#interface ExampleViewController ()
#end
#implementation ExampleViewController
#synthesize positionY;
#synthesize fieldCount;
#synthesize scroll;
- (void)viewDidLoad {
[super viewDidLoad];
scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scroll.backgroundColor = [UIColor whiteColor];
[self.view addSubview:scroll];
UIButton *clickToCreateTextField = [[UIButton alloc] initWithFrame:CGRectMake(40, 80, self.view.frame.size.width-80, 75)];
[clickToCreateTextField setTitle:#"Create Text Field" forState:UIControlStateNormal];
[clickToCreateTextField addTarget:self action:#selector(clickedButton) forControlEvents:UIControlEventTouchUpInside];
[clickToCreateTextField setBackgroundColor:[UIColor blackColor]];
[scroll addSubview:clickToCreateTextField];
positionY = clickToCreateTextField.center.y;
fieldCount = 0;
// Do any additional setup after loading the view.
}
-(void) clickedButton{
//add text field programmitacally
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(40, positionY, self.view.frame.size.width-80, 75)];
textField.delegate = self;
//give a tag to determine the which textField tapped
textField.tag = fieldCount;
textField.placeholder = [NSString stringWithFormat:#"Your dynamically created textField: %d", fieldCount ];
[scroll addSubview:textField];
//check if the textFields bigger than view size set scroll size and offset
if (positionY>= self.view.frame.size.height) {
scroll.contentOffset = CGPointMake(0, positionY);
scroll.contentSize = CGSizeMake(scroll.frame.size.width, scroll.frame.size.height+positionY);
}
fieldCount++;
//increase the position with a blank place
positionY = positionY+textField.frame.size.height+20;
}
#pragma mark TextField Delegate Methods
-(void) textFieldDidBeginEditing:(UITextField *)textField{
//Do what ever you want
}
-(void) textFieldDidEndEditing:(UITextField *)textField{
[textField resignFirstResponder];
//do anything
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
-(void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
You can do any additional changes on this code.
I think this example explain your answer.
Hope it helps.
Use a counter and calculate y like this counter*texfield.frame.size.height.
I Place the imgaes as below.
In ListView.m
-(void)viewDidLoad()
{
int j=5;
for (int i=0; i<[po_id_array count]; i++) {
ViewForTickImage *tickimage=[[ViewForTickImage alloc]initWithFrame:CGRectMake(5, j, 35, 35)];
NSLog(#"----------%d",j);
j=j+17;
[tickimage setTick:FALSE];
[tickimage setTag:i];
// [tickimage setDelegate:self];
[self.view addSubview:tickimage];
}
}
In ViewForTickImage.h file the code is as follows:
#interface ViewForTickImage : UIView
{
BOOL ticked;
NSMutableArray *list_selected;
id delegate;
UIImageView *currentImg;
AppDelegate *appdelegate;
}
//#property(nonatomic,readwrite)BOOL ticked;
#property(nonatomic,readwrite,getter=isTicked,setter=setTick:)BOOL ticked;
#property(nonatomic,retain) UIImageView *currentImg;
-(void)checkTickVisiblilityForListAllView;
In ViewForTickImage.m file the code is::
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
currentImg = [[UIImageView alloc]initWithFrame:frame];
[currentImg setContentMode:UIViewContentModeScaleAspectFit];
UIImage *img=[UIImage imageNamed:#"image_unselect.png"];
[currentImg setImage:img];
[self addSubview:currentImg];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"image selected");
}
It displays the column of images.Touch event is working only for first image.How to fire the touch event for all images.Please give the solution.
Instead of using imageView try using UIbutton and add image to the image property of the button. yoU can identify the click in a simple way.
Using tag property of the button you can identify which button was clicked.
UIButton *imageButton1;
imageButton1.tag=buttonIdentifier;
[imageButton1 addTarget:self action:#selector(imageSelected:) forControlEvents:UIControlEventTouchUpInside];
After my user clicks a button, I'd like that button to stay pushed during the time that I perform a network operation. When the network operation is complete, I want the button to return to its default state.
I've tried calling -[UIButton setSelected:YES] right after the button push (with a corresponding call to -[UIButton setSelected:NO] after my network op finishes) but it doesn't seem to do anything. Same thing if I call setHighlighted:.
I suppose I could try swapping out the background image to denote a selected state for the duration of the network op, but that seems like a hack. Any better suggestions?
Here's what my code looks like:
- (IBAction)checkInButtonPushed
{
self.checkInButton.enabled = NO;
self.checkInButton.selected = YES;
self.checkInButton.highlighted = YES;
[self.checkInActivityIndicatorView startAnimating];
[CheckInOperation startWithPlace:self.place delegate:self];
}
- (void)checkInCompletedWithNewFeedItem:(FeedItem*)newFeedItem wasNewPlace:(BOOL)newPlace possibleError:(NSError*)error;
{
[self.checkInActivityIndicatorView stopAnimating];
self.checkInButton.enabled = YES;
self.checkInButton.selected = NO;
self.checkInButton.highlighted = NO;
}
How are you setting the images for the different UIControlStates on the button? Are you setting a background image for UIControlStateHighlighted as well as UIControlStateSelected?
UIImage *someImage = [UIImage imageNamed:#"SomeResource.png"];
[button setBackgroundImage:someImage forState:UIControlStateHighlighted];
[button setBackgroundImage:someImage forState:UIControlStateSelected];
If you're setting the selected state on the button touch down event rather than touch up inside, your button will actually be in a highlighted+selected state, so you'll want to set that too.
[button setBackgroundImage:someImage forState:(UIControlStateHighlighted|UIControlStateSelected)];
Edit:
To sum up my remarks in the comments and to address the code you posted...you need to set your background images for the full UIControl state that you're in. According to your code snippet, this control state would be disabled + selected + highlighted for the duration of the network operation. This means that you would need to do this:
[button setBackgroundImage:someImage forState:(UIControlStateDisabled|UIControlStateHighlighted|UIControlStateSelected)];
If you remove the highlighted = YES, then you would need this:
[button setBackgroundImage:someImage forState:(UIControlStateDisabled|UIControlStateSelected)];
Get the picture?
I have an easier way. Just use "performSelector" with 0 delay to perform [button setHighlighted:YES] . This will perform re-highlighting after the current runloop ends.
- (IBAction)buttonSelected:(UIButton*)sender {
NSLog(#"selected %#",sender.titleLabel.text);
[self performSelector:#selector(doHighlight:) withObject:sender afterDelay:0];
}
- (void)doHighlight:(UIButton*)b {
[b setHighlighted:YES];
}
"Everything gets better when you turn power on"
button.selected = !button.selected;
works perfectly... after I connected the outlet to the button in the Interface Builder.
You do not need to setBackgroundImage:forState:, the builder allows you to specify the background (gets resized if necessary) or/and foreground (not resizing) images.
Try using NSOperationQueue to achieve this. Try out code as follows:
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
theButton.highlighted = YES;
}];
Hope this helps.
Use a block so you don't have to build a whole separate method:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), ^{
theButton.highlighted = YES;
});
Update
To be clear you still need to set the background (or normal image) for the combination states as well as the regular ones like sbrocket says in the accepted answer. At some point your button will be both selected and highlighted, and you won't have an image for that unless you do something like this:
[button setBackgroundImage:someImage forState (UIControlStateHighlighted|UIControlStateSelected)];
Otherwise your button can fall back to the UIControlStateNormal image for the brief selected+highlighted state and you'll see a flash.
In swift I'm doing it like the following.
I create a Subclass of UIButton and implemented a custom property state
class ActiveButton: UIButton {
private var _active = false
var active:Bool {
set{
_active = newValue
updateState()
}
get{
return _active
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addTarget(self, action: #selector(ActiveButton.touchUpInside(_:)), forControlEvents: .TouchUpInside)
}
func touchUpInside(sender:UIButton) {
active = !active
}
private func updateState() {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.highlighted = self.active
}
}
}
Works perfectly for me.
I had a similar problem where I wanted a button to keep it's highlight after click.
The problem is if you try to use setHighlighted:YES inside of you click action it will reset right after you click action, - (IBAction)checkInButtonPushed
I solved this by using a NSTimer like this
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval: 0.01
target: self
selector: #selector(selectCurrentIndex)
userInfo: nil
repeats: NO];
and then call setHighlighted:YES from my selectCurrentIndex method. I use regular UIButtonTypeRoundedRect buttons.
I have another way ...if you don't want to use images, and you want the effect of a pressed button, You can subclass the Button and here's my code:
in the .h File:
#interface reservasButton : UIButton {
BOOL isPressed;
}
#end
In the .m File:
#import <QuartzCore/QuartzCore.h>
#implementation reservasButton
-(void)setupView { //This is for Shadow
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOpacity = 0.5;
self.layer.shadowRadius = 1;
self.layer.shadowOffset = CGSizeMake(2.0f, 2.0f); //comment
// self.layer.borderWidth = 1;
self.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
// [self setBackgroundColor:[UIColor whiteColor]];
// self.opaque = YES;
}
-(id)initWithFrame:(CGRect)frame{
if((self = [super initWithFrame:frame])){
[self setupView];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if((self = [super initWithCoder:aDecoder])){
[self setupView];
}
return self;
}
//Here is the important code
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
if (isPressed == FALSE) {
self.contentEdgeInsets = UIEdgeInsetsMake(1.0,1.0,-1.0,-1.0);
self.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
self.layer.shadowOpacity = 0.8;
[super touchesBegan:touches withEvent:event];
isPressed = TRUE;
}
else {
self.contentEdgeInsets = UIEdgeInsetsMake(0.0,0.0,0.0,0.0);
self.layer.shadowOffset = CGSizeMake(2.0f, 2.0f);
self.layer.shadowOpacity = 0.5;
[super touchesEnded:touches withEvent:event];
isPressed = FALSE;
}
} `
Here is a C# / MonoTouch (Xamarin.iOS) implementation using approaches presented above. It assumes you have set the Highlighted image state already, and configures the selected and selected|highlighted states to use the same image.
var selected = button.BackgroundImageForState(UIControlState.Highlighted);
button.SetBackgroundImage(selected, UIControlState.Selected);
button.SetBackgroundImage(selected, UIControlState.Selected | UIControlState.Highlighted);
button.TouchUpInside += delegate
{
NSTimer.CreateScheduledTimer(TimeSpan.FromMilliseconds(0), delegate
{
button.Highlighted = true;
NSTimer.CreateScheduledTimer(TimeSpan.FromMilliseconds(200), delegate
{
button.Highlighted = false;
});
});
};