I have used the interface builder to create the following UIButton
for different time slot and a UIButton
for Search
. I want the UIButton for different time slot to remain selected/highlighted when user tap on it. And the background color and font color will change as well (See pic for illustration). Moreover, user can only select one of the time slot at one time.
UIButton
with different time slot
What I am trying to achieve button
Code
#import "Search.h"
#import <QuartzCore/QuartzCore.h>
@interface Search(){
}
@end
@implementation Search
@synthesize btn1;
@synthesize btn2;
@synthesize btn3;
@synthesize btn4;
@synthesize btn5;
@synthesize btn6;
@synthesize btn7;
@synthesize btn8;
@synthesize btn9;
@synthesize btnSearch;
- (void)viewDidLoad
{
[super viewDidLoad];
_borderBox.layer.shadowRadius = 5;
_borderBox.layer.shadowColor = [UIColor colorWithRed:211.f/255.f green:211.f/255.f blue:211.f/255.f alpha:1.f].CGColor;
_borderBox.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
_borderBox.layer.shadowOpacity = 0.9f;
_borderBox.layer.masksToBounds = NO;
btn1.layer.borderColor = [UIColor lightGrayColor].CGColor;
btn1.layer.borderWidth =1.0f;
btn2.layer.borderColor = [UIColor lightGrayColor].CGColor;
btn2.layer.borderWidth =1.0f;
btn3.layer.borderColor = [UIColor lightGrayColor].CGColor;
btn3.layer.borderWidth =1.0f;
btn4.layer.borderColor = [UIColor lightGrayColor].CGColor;
btn4.layer.borderWidth =1.0f;
btn5.layer.borderColor = [UIColor lightGrayColor].CGColor;
btn5.layer.borderWidth =1.0f;
btn6.layer.borderColor = [UIColor lightGrayColor].CGColor;
btn6.layer.borderWidth =1.0f;
btn7.layer.borderColor = [UIColor lightGrayColor].CGColor;
btn7.layer.borderWidth =1.0f;
btn8.layer.borderColor = [UIColor lightGrayColor].CGColor;
btn8.layer.borderWidth =1.0f;
btn9.layer.borderColor = [UIColor lightGrayColor].CGColor;
btn9.layer.borderWidth =1.0f;
}
-(void)viewWillAppear:(BOOL)animated{
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
+(void)makeButtonColored:(UIButton*)button color1:(UIColor*) color
{
CALayer *layer = button.layer;
layer.cornerRadius = 8.0f;
layer.masksToBounds = YES;
layer.borderWidth = 4.0f;
layer.opacity = .3;//
layer.borderColor = [UIColor colorWithWhite:0.4f alpha:0.2f].CGColor;
CAGradientLayer *colorLayer = [CAGradientLayer layer];
colorLayer.cornerRadius = 8.0f;
colorLayer.frame = button.layer.bounds;
//set gradient colors
colorLayer.colors = [NSArray arrayWithObjects:
(id) color.CGColor,
(id) color.CGColor,
nil];
//set gradient locations
colorLayer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:1.0f],
nil];
[button.layer addSublayer:colorLayer];
}
Theoretically, you could do the following:
The constructor function of the button would like something like this:
-(UIButton *)newButtonWithTitle:(NSString *)title fontSize:(NSInteger)fontSize {
UIColor *selectedButtonColor = [UIColor colorWithRed:1.0 green:0.2 blue:0.2
alpha:0.5];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:title forState:UIControlStateNormal];
[button setTitleColor:selectedButtonColor forState:UIControlStateHighlighted];
[button setTitleColor:selectedButtonColor forState:UIControlStateSelected];
button.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightRegular];
button.layer.borderColor = [UIColor lightGrayColor].CGColor;
button.layer.borderWidth = 1.0;
[button addTarget:self action:@selector(scheduleButtonAction:) forControlEvents:UIControlEventTouchUpInside];
return button;
}
The button action function could be:
-(void)scheduleButtonAction:(UIButton *)button {
button.selected = YES;
[self.buttons enumerateObjectsUsingBlock:^(UIButton *aButton, NSUInteger idx, BOOL * _Nonnull stop) {
if (![aButton isEqual:button]) {
aButton.selected = NO;
}
}];
}
BUT I wouldn't do it this way. The problem with this solution is while it is possible, it's not the Apple way and it's definitely not an elegant solution.
There are multiple problems here:
How are you binding the data between each button and the value that it represents? You could do that by either using associative objects OR by subclassing UIButton and adding a property OR by using tags and a lookup table. All of which are not great solutions.
This design is hardcoded and not flexible. There is a lot of boilerplate code for the creation of the buttons and you have to keep track of all these properties.
What are you going to do if the requirement will change and you'll need a button for each hour of the day?
A better way to do this layout, which was hinted by user10277996 is to use a collection view. It will allow you to separate the concerns:
You should take a day or two and get really familiar with UICollectionView as it is one of the most powerful and useful classes in iOS.
Here is a tutorial to get you started: https://www.raywenderlich.com/975-uicollectionview-tutorial-getting-started
Apple's official documentation: https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40012334-CH1-SW1
If you want to dig deeper, check out the following resources (although not necessary for solving your specific issue): https://www.objc.io/issues/3-views/collection-view-layouts/ https://ashfurrow.com/uicollectionview-the-complete-guide/
I was able to achieve the function you are working on and below is how i did it.
I created the design via storyboard and connected all the 9 button's actions methods to a single Selector method, inside the action method with the help sender parameter we can get the selected buttons reference and use it.
- (IBAction)btnPressed:(UIButton*)sender {
/* Below for loop works as a reset for setting the default colour of button and to not select the same one twice*/
for (UIButton* button in buttons) {
[button setSelected:NO];
[button setBackgroundColor:[UIColor whiteColor]];
[button setUserInteractionEnabled:true];
// [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateSelected];
}
NSInteger tag = sender.tag; // Here we get the sender tag, which we can use for our needs. Also we can directly use the sender and get the title or whatsoever needed.
/*Now below line works as a toggle for the button where multiple buttons can't be selected at the same time.*/
sender.selected = ! sender.selected;
if(sender.selected)
{
/* Here we set the color for the button and handle the selected function*/
[sender setSelected:YES];
[sender setUserInteractionEnabled:false];
[sender setBackgroundColor:[UIColor magentaColor]];
}
}
You can also add custom layer for the button by using the "sender.Layer" property.
The Whole code is added below,
All the button's action needs to be connected to a single selector method, - (IBAction)btnPressed:(UIButton*)sender;
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *mainViewOL;
@property (weak, nonatomic) IBOutlet UIButton *btn1;
@property (weak, nonatomic) IBOutlet UIButton *btn2;
@property (weak, nonatomic) IBOutlet UIButton *btn3;
@property (weak, nonatomic) IBOutlet UIButton *btn4;
@property (weak, nonatomic) IBOutlet UIButton *btn5;
@property (weak, nonatomic) IBOutlet UIButton *btn6;
@property (weak, nonatomic) IBOutlet UIButton *btn7;
@property (weak, nonatomic) IBOutlet UIButton *btn8;
@property (weak, nonatomic) IBOutlet UIButton *btn9;
@end
@implementation ViewController
NSArray* buttons;
- (void)viewDidLoad {
[super viewDidLoad];
buttons = [NSArray arrayWithObjects:_btn1, _btn2, _btn3,_btn4,_btn5,_btn6,_btn7,_btn8,_btn9,nil];
self.mainViewOL.layer.shadowRadius = 5;
self.mainViewOL.layer.shadowColor = [UIColor colorWithRed:211.f/255.f green:211.f/255.f blue:211.f/255.f alpha:1.f].CGColor;
self.mainViewOL.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.mainViewOL.layer.shadowOpacity = 0.9f;
self.mainViewOL.layer.masksToBounds = NO;
/* I Have added the 9 button's in an array and used it to reduce the lines of code and for easy understanding as well*/
for (UIButton* button in buttons) {
button.layer.borderColor = [UIColor lightGrayColor].CGColor;
button.layer.borderWidth =1.0f;
}
}
- (IBAction)btnPressed:(UIButton*)sender {
for (UIButton* button in buttons) {
[button setSelected:NO];
[button setBackgroundColor:[UIColor whiteColor]];
[button setUserInteractionEnabled:true];
// [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; //Based on your needs and colour variant you cant add properties to the button for different control states.
[button setTitleColor:[UIColor blackColor] forState:UIControlStateSelected];
}
NSInteger tag = sender.tag;
sender.selected = ! sender.selected;
if(sender.selected)
{
[sender setSelected:YES];
[sender setUserInteractionEnabled:false];
[sender setBackgroundColor:[UIColor purpleColor]];
sender.backgroundColor = [UIColor magentaColor];
}
}
@end
And the Final Result
Ignore the delay in button selection, it is caused by the video to gif conversion.
Hope This helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With