cornerRadius causes long load time in iOS 10 - ios

I have a problem with my App. I have a View with 25 Buttons with changed cornerRadius. Since iOS 10 it takes about 5-6 seconds till this View loads.
Here is the relevant code:
- (void)setupTiles {
for (TileButton *btn in tiles_btn) {
btn.alpha = 0.0;
btn.layer.borderColor = [UIColor blackColor].CGColor;
btn.layer.borderWidth = 1.0f;
[btn layoutIfNeeded];
bin.layer.cornerRadius = btn.frame.size.width*0.3;
}
[self colorTilesWithArray:currentTileColors];
}
When i remove the following lines, is loads the view instantly:
[btn layoutIfNeeded];
bin.layer.cornerRadius = btn.frame.size.width*0.3;
The Buttons are grouped in an outlet collection, in case that's necessary to know.
Does anybody have a solution?
Thanks in advance!
Nico
Image 1: This takes about 5-6 seconds
Image 2: This is the View that will be loaded
TileButton.h-File:
#interface TileButton : UIButton
#property (nonatomic) int colorMode;
+ (UIColor *)blue;
+ (UIColor *)red;
+ (UIColor *)green;
+ (UIColor *)yellow;
+(UIColor *)colorForColorCode:(int)colorCode;
-(int)colorMode;
#end
TileButton.m-File:
#implementation TileButton
- (instancetype)init {
self = [super init];
if (self) {
self.layer.cornerRadius = self.frame.size.width*0.3;
}
return self;
}
+(UIColor *)blue {
return [UIColor colorWithRed:41.0/255.0 green:161.0/255.0 blue:255.0/255.0 alpha:1];
}
+(UIColor *)red {
return [UIColor colorWithRed:255.0/255.0 green:71.0/255.0 blue:109.0/255.0 alpha:1];
}
+(UIColor *)green {
return [UIColor colorWithRed:0.0/255.0 green:185.0/255.0 blue:30.0/255.0 alpha:1];
}
+(UIColor *)yellow {
return [UIColor colorWithRed:255.0/255.0 green:198.0/255.0 blue:26.0/255.0 alpha:1];
}
+(UIColor *)colorForColorCode:(int)colorCode {
switch (colorCode) {
case 1:
return [TileButton blue];
break;
case 2:
return [TileButton red];
break;
case 3:
return [TileButton green];
break;
case 4:
return [TileButton yellow];
break;
default:
return [UIColor blackColor];
break;
}
}
-(int)colorMode {
if (CGColorEqualToColor(self.backgroundColor.CGColor, [TileButton blue].CGColor))
return 1;
else if (CGColorEqualToColor(self.backgroundColor.CGColor, [TileButton red].CGColor))
return 2;
else if (CGColorEqualToColor(self.backgroundColor.CGColor, [TileButton green].CGColor))
return 3;
else if (CGColorEqualToColor(self.backgroundColor.CGColor, [TileButton yellow].CGColor))
return 4;
else
return -1;
}
#end

Since you are creating the buttons in Interface Builder you will need to set the cornerRadius in the awakeFromNib method. It should look something like this:
#implementation TileButton
-(void) awakeFromNib {
[super awakeFromNib];
self.layer.cornerRadius = self.frame.size.width*0.3;
}
https://developer.apple.com/reference/objectivec/nsobject/1402907-awakefromnib

Why can't you set the corner radius outside the for loop?
UIButton *btn = [UIButton alloc] initWithFrame:CGRectMake(0, 0, 0, 0)]; // set your frame here
bin.layer.cornerRadius = btn.frame.size.width*0.3; // width stays the same, set it here
for (TileButton *btn in tiles_btn) {
btn.alpha = 0.0;
btn.layer.borderColor = [UIColor blackColor].CGColor;
btn.layer.borderWidth = 1.0f;
// you need to set the button frame here
btn.frame = CGRectMake(0,0,0,0);
[btn layoutIfNeeded];
}
[self colorTilesWithArray:currentTileColors];

Assuming that TileButton is a subclass of UIButton, on initialization, you can set the TileButton to have your custom color, width, and corner radius. That way all of your buttons already have the specified customization on button creation.

Related

Change button's background colour

I am working on a project in which I create a subclass for UIButton for setting the gradient background colour.Its working fine but now there are two tabs in a ViewController there is two buttons suppose A and B. I want to change colour of button A when I clicked on Button B same like tabs functionality:
Sub class of UIButton:
// .h_file
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface grediantButton : UIButton
#property(nonatomic)IBInspectable UIColor*topColor;
#property(nonatomic)IBInspectable UIColor*bottomColor;
#property(nonatomic)IBInspectable CGFloat cornerRadius;
- (void)customInit;
#end
// .m file
#import "grediantButton.h"
#import "UIColor+myColor.h"
#implementation grediantButton
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.topColor = [UIColor clearColor];
self.bottomColor = [UIColor clearColor];
self.cornerRadius = 1;
[self customInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self customInit];
}
return self;
}
- (void)drawRect:(CGRect)rect {
[self customInit];
}
- (void)setNeedsLayout {
[super setNeedsLayout];
[self setNeedsDisplay];
}
- (void)prepareForInterfaceBuilder {
[self customInit];
}
- (void)customInit {
self.layer.cornerRadius = self.cornerRadius;
CAGradientLayer *gradientLayer = [[CAGradientLayer alloc] init];
gradientLayer.frame = self.bounds;
gradientLayer.cornerRadius = self.cornerRadius;
gradientLayer.colors = [NSArray arrayWithObjects:(id)[self.topColor CGColor], (id)[self.bottomColor CGColor],nil];
[self.layer setMasksToBounds:YES];
[self.layer insertSublayer:gradientLayer atIndex:0];
}
My VC Code:
#pragma mark:AddnewTownshipVC
- (IBAction)addnewTownShip:(id)sender {
[self.addnewTwnBtn setTopColor:[UIColor getTabTopColor]];
[self.addnewTwnBtn setBottomColor:[UIColor getTabBotColor]];
[self.viewalltownBtn setTopColor:[UIColor blackColor]];
[self.viewalltownBtn setBottomColor:[UIColor blackColor]];
}
#pragma mark:viewalltownshipVC
- (IBAction)viewAllTownship:(id)sender {
[self.addnewTwnBtn setTopColor:[UIColor blackColor]];
[self.addnewTwnBtn setBottomColor:[UIColor blackColor]];
[self.viewalltownBtn setTopColor:[UIColor getTabTopColor]];
[self.viewalltownBtn setBottomColor:[UIColor getTabBotColor]];
}
Tabs is like:
what happen when I click on black one tab
You should set state for UIButton multiple states.
For example, set different background images for different states.
[button setBackgroundImage:naomalImage forState:UIControlStateNormal];
[button setBackgroundImage:selectedImage forState:UIControlStateSelected];
And then, just control states.
- (IBAction)addnewTownShip:(id)sender {
button.selected = YES;
}
...
Thank you all for response.
I solved my issue by change some code in my - (void)customInit method
- (void)customInit {
self.layer.cornerRadius = self.cornerRadius;
CAGradientLayer *gradientLayer = [[CAGradientLayer alloc] init];
gradientLayer.frame = self.bounds;
gradientLayer.cornerRadius = self.cornerRadius;
gradientLayer.colors = [NSArray arrayWithObjects:(id)[self.topColor CGColor], (id)[self.bottomColor CGColor],nil];
[self.layer setMasksToBounds:YES];
if([[self.layer sublayers] objectAtIndex:0]){
[self.layer replaceSublayer:[[self.layer sublayers] objectAtIndex:0] with:gradientLayer];
}else{
[self.layer insertSublayer:gradientLayer atIndex:0];
}
}
and call this method in
#pragma mark:AddnewTownshipVC
- (IBAction)addnewTownShip:(grediantButton*)sender {
[sender setTopColor:[UIColor getTabTopColor]];
[sender setBottomColor:[UIColor getTabBotColor]];
[sender customInit];
[self.viewalltownBtn setTopColor:[UIColor blackColor]];
[self.viewalltownBtn setBottomColor:[UIColor blackColor]];
[self.viewalltownBtn customInit];
// sender.isSelected = true;
// self.viewalltownBtn.isSelected = false;
}
#pragma mark:viewalltownshipVC
- (IBAction)viewAllTownship:(grediantButton*)sender {
[self.addnewTwnBtn setTopColor:[UIColor blackColor]];
[self.addnewTwnBtn setBottomColor:[UIColor blackColor]];
[self.addnewTwnBtn customInit];
[sender setTopColor:[UIColor getTabTopColor]];
[sender setBottomColor:[UIColor getTabBotColor]];
[sender customInit];
}
simply go to the attribute inspector in storyboard see in bottom in it there has background colour you can change with it.
see here :-
https://i.stack.imgur.com/A6ecY.png

Subclassed UISegmentedControl titleTextAttributes not always applied

Scenario
I have a peculiar bug in my subclassed UISegmentedControl. What I have simply done is subclassed a UISegmentedControl to remove the default indicators of selection/deselection and used a custom CALayer as an indicator for the same. At the same time, I have modified the titleTextAttributes to complement the UI.
Problem
When the view first appears and default selected index is 0, it looks like this:
When I try to change the selectedSegmentIndex to 1 it becomes:
Where it should look like this (corrected once you change the selectedSegmentIndex a few times):
This behaviour of titleTextAttributes not changing is random, however 2 cases when this happens 100% of the time are:
View appears, and you select segment at index 1
Select last segment, and then select segment 0
I have not found subclassing notes in the UISegmentedControl official docs.
Sample project HERE.
NOTE: If you use only 2 segments, you will never the see the title of the segment at index 1 on selection.
Indeed it's a weird behavior.
You can workaround it adding an explicit call to [self setNeedsLayout] after changing the indication layer frame.
//
// RBLSegmentedControl.m
// Rabbler
//
// Created by Utkarsh Singh on 05/11/15.
// Copyright © 2015 Neeraj SIngh. All rights reserved.
//
#import "NPSegmentedControl.h"
#interface NPSegmentedControl ()
#property (nonatomic, retain) CALayer* selectedIndicationLayer;
#end
#implementation NPSegmentedControl
- (instancetype)initWithItems:(NSArray *)items {
if (self = [super initWithItems:items]) {
self.selectedIndicationLayer.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width / items.count, self.frame.size.height);
[self addTarget:self action:#selector(segmentedControlDidChange) forControlEvents:UIControlEventValueChanged];
NSDictionary* selectedAttributes = #{NSFontAttributeName:[UIFont fontWithName:#"HelveticaNeue-Light" size:15],
NSForegroundColorAttributeName:[UIColor whiteColor]};
NSDictionary* deselectedAttributes = #{NSFontAttributeName:[UIFont fontWithName:#"HelveticaNeue-Light" size:15],
NSForegroundColorAttributeName:[UIColor redColor]};
NSDictionary* highlightedAttributes = #{NSFontAttributeName:[UIFont fontWithName:#"HelveticaNeue-Light" size:15],
NSForegroundColorAttributeName:[[UIColor redColor] colorWithAlphaComponent:0.6f]};
[self setTitleTextAttributes:deselectedAttributes forState:UIControlStateNormal];
[self setTitleTextAttributes:selectedAttributes forState:UIControlStateSelected];
[self setTitleTextAttributes:highlightedAttributes forState:UIControlStateHighlighted];
}
return self;
}
- (void) segmentedControlDidChange {
[self animateSegmentSelectionChange];
if (self.delegate && [self.delegate respondsToSelector:#selector(segmentedControlDidChangeSelectedSegment:)]) {
[self.delegate segmentedControlDidChangeSelectedSegment:self.selectedSegmentIndex];
}
}
- (void) animateSegmentSelectionChange {
[UIView animateWithDuration:0.25
delay:0
options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionBeginFromCurrentState)
animations:^{
CGRect frame = self.selectedIndicationLayer.frame;
frame.origin.x = self.selectedSegmentIndex * [UIScreen mainScreen].bounds.size.width / self.numberOfSegments;
self.selectedIndicationLayer.frame = frame;
[self setNeedsLayout]; // <--
} completion:nil];
}
- (CALayer *) selectedIndicationLayer {
if (!_selectedIndicationLayer) {
_selectedIndicationLayer = [CALayer layer];
_selectedIndicationLayer.backgroundColor = [[UIColor redColor] CGColor];
_selectedIndicationLayer.cornerRadius = self.frame.size.height /2;
_selectedIndicationLayer.borderColor = [UIColor whiteColor].CGColor;
_selectedIndicationLayer.borderWidth = 1.5f;
[self.layer insertSublayer:_selectedIndicationLayer atIndex:0];
}
return _selectedIndicationLayer;
}
- (void) setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex {
[super setSelectedSegmentIndex:selectedSegmentIndex];
[self animateSegmentSelectionChange];
}
- (void) setIndicatorColor:(UIColor *)indicatorColor {
self.selectedIndicationLayer.backgroundColor = indicatorColor.CGColor;
}
#end
I change your selectedIndicationLayer to this and it fix the problem
- (CALayer *) selectedIndicationLayer {
if (!_selectedIndicationLayer) {
_selectedIndicationLayer = [CALayer layer];
_selectedIndicationLayer.backgroundColor = [[UIColor redColor] CGColor];
_selectedIndicationLayer.cornerRadius = self.frame.size.height /2;
_selectedIndicationLayer.borderColor = [UIColor whiteColor].CGColor;
_selectedIndicationLayer.borderWidth = 1.5f;
}
if ([self.layer.sublayers containsObject:_selectedIndicationLayer]) {
[_selectedIndicationLayer removeFromSuperlayer];
}
[self.layer insertSublayer:_selectedIndicationLayer atIndex:0];
return _selectedIndicationLayer;
}
Just edit your following method.
- (CALayer *) selectedIndicationLayer {
if (!_selectedIndicationLayer) {
_selectedIndicationLayer = [CALayer layer];
_selectedIndicationLayer.backgroundColor = [[UIColor redColor] CGColor];
_selectedIndicationLayer.cornerRadius = self.frame.size.height /2;
_selectedIndicationLayer.borderColor = [UIColor whiteColor].CGColor;
_selectedIndicationLayer.borderWidth = 1.5f;
}
[self.layer insertSublayer:_selectedIndicationLayer atIndex:0];
return _selectedIndicationLayer;
}

Create a rating view in IOS objective c

I want to create following rating views shown below. How is it possible to do it
I have got 3 conditions.I have got label in top and below that are stars and at the end there is TextField. Good,Amazing,Horrific is set according to number of stars. When there is 1 Star i.e horrific condition we have to present four button Order issues, timing, friendliness,Cleanliness.And UITextField is shift below it
I have implemented following code
#pragma mark - Btn Action on start click
- (IBAction)Btn_FirstHorrific:(id)sender {
[self ReviewHorrific];
[self NumOfStarWorstCase];
str_deliveryBoyRating=#"1";
str_customerComment=#"Horrific";
}
- (IBAction)Btn_SecondNah:(id)sender {
[self ReviewSecondNah];
[self NumOfStarOthercase];
str_deliveryBoyRating=#"2";
str_customerComment=#"nah!";
}
- (IBAction)Btn_ThirdItsOk:(id)sender {
[self ReviewItsOk];
[self NumOfStarOthercase];
str_deliveryBoyRating=#"3";
str_customerComment=#"Its ok";
}
- (IBAction)Btn_FourthGood:(id)sender {
[self ReviewGood];
[self NumOfStarOthercase];
str_deliveryBoyRating=#"4";
str_customerComment=#"good";
}
- (IBAction)Btn_FifthAmazing:(id)sender {
[self ReviewAmazing];
[self NumOfStarOthercase];
str_deliveryBoyRating=#"5";
str_customerComment=#"Amazing";
}
#pragma mark - Change star colour in Number of star changes
- (void)ReviewHorrific{
Btn_FirstHorrific.tintColor = UIColorFromRGB(0xff8133);
Btn_SecondNah.tintColor = [UIColor whiteColor];
Btn_ThirdItsOk.tintColor = [UIColor whiteColor];
Btn_FourthGood.tintColor = [UIColor whiteColor];
Btn_FifthAmazing.tintColor = [UIColor whiteColor];
lbl_report.text=#"Please report your issue";
lbl_RateType.text=#"Horrific";
self.View_WorstCase.hidden = FALSE;
}
- (void)ReviewSecondNah{
Btn_FirstHorrific.tintColor = UIColorFromRGB(0xff8133);
Btn_SecondNah.tintColor = UIColorFromRGB(0xff8133);
Btn_ThirdItsOk.tintColor = [UIColor whiteColor];
Btn_FourthGood.tintColor = [UIColor whiteColor];
Btn_FifthAmazing.tintColor = [UIColor whiteColor];
lbl_report.text=#"Please report your issue";
lbl_RateType.text=#"Nah!!";
}
- (void)ReviewItsOk{
Btn_FirstHorrific.tintColor = UIColorFromRGB(0xff8133);
Btn_SecondNah.tintColor = UIColorFromRGB(0xff8133);
Btn_ThirdItsOk.tintColor = UIColorFromRGB(0xff8133);
Btn_FourthGood.tintColor = [UIColor whiteColor];
Btn_FifthAmazing.tintColor = [UIColor whiteColor];
lbl_report.text=#"Please report your issue";
lbl_RateType.text=#"It's OK";
}
- (void)ReviewGood{
Btn_FirstHorrific.tintColor = UIColorFromRGB(0xff8133);
Btn_SecondNah.tintColor = UIColorFromRGB(0xff8133);
Btn_ThirdItsOk.tintColor = UIColorFromRGB(0xff8133);
Btn_FourthGood.tintColor = UIColorFromRGB(0xff8133);
Btn_FifthAmazing.tintColor = [UIColor whiteColor];
lbl_report.text=#"Heap praise if you would like";
lbl_RateType.text=#"Good";
}
- (void)ReviewAmazing{
Btn_FirstHorrific.tintColor = UIColorFromRGB(0xff8133);
Btn_SecondNah.tintColor = UIColorFromRGB(0xff8133);
Btn_ThirdItsOk.tintColor = UIColorFromRGB(0xff8133);
Btn_FourthGood.tintColor = UIColorFromRGB(0xff8133);
Btn_FifthAmazing.tintColor = UIColorFromRGB(0xff8133);
lbl_report.text=#"Glad to hear it. Any comments or feedback?";
lbl_RateType.text=#"Amazing";
}
Step 1)
Add 5 UIButton to your UI file. Then in your header (.h) code, declare the UIButton like so:
IBOutlet UIButton *starOne;
IBOutlet UIButton *starTwo;
IBOutlet UIButton *starThree;
IBOutlet UIButton *starFour;
IBOutlet UIButton *starFive;
Then back in your UI file, link the star button views to your code. Then add the default (non-selected) star images to your buttons (just drag and drop to do this).
Step 2)
Back in your header code, add 5 IBAction methods. These will detect when the star buttons are pressed:
-(IBAction)press_1;
-(IBAction)press_2;
-(IBAction)press_3;
-(IBAction)press_4;
-(IBAction)press_5;
Then add a method as well. This method will handle the star images.
-(void)set_stars:(int)num :(NSString *)desc
Then declare in integer in your header file. This will tell you what the rating is, like so:
int rating;
Also declare an array (this will contain pointers to your star buttons):
NSArray *_starButtons;
Lastly, declare a UILabel to show the name for each rating (and like it to your UI), like so:
IBOutlet UILabel *rating_name;
Step 3)
In your implementation (.m) code, add in the following:
-(IBAction)press_1 {
[self set_stars:1 :#"Horrific"];
}
-(IBAction)press_2 {
[self set_stars:2 :#"nah!"];
}
-(IBAction)press_3 {
[self set_stars:3 :#"Its ok"];
}
-(IBAction)press_4 {
[self set_stars:4 :#"good"];
}
-(IBAction)press_5 {
[self set_stars:5 :#"Amazing"];
}
-(void)set_stars:(int)num :(NSString *)desc {
rating_name.text = [NSString stringWithFormat:#"%#", desc];
for (int loop = 0; loop < 5; loop++) {
if ((loop + 1) <= num) {
[((UIButton*)_starButtons[loop]) setBackgroundImage:[UIImage imageNamed:#"star_ON.png"] forState:UIControlStateNormal];
}
else {
[((UIButton*)_starButtons[loop]) setBackgroundImage:[UIImage imageNamed:#"star_OFF.png"] forState:UIControlStateNormal];
}
}
rating = num;
}
Finally, dont forget to populate the array with your buttons in the viewDidLoad method like so:
_starButtons = #[starOne, starTwo, starThree, starFour, starFive];

UIPageControl + circular (Infinite) scrolling

I am working on app in which i have to make welcome screen. In that UIPageControl i need to display different 9 subviews. Also need infinite circular scrolling in that. It means after page-9 if i slide than page-1 should appear.
I have tried so much to implement it but never got expected results.
Can anyone help me out to get the best solution for it.
Thanks.
I recommended to use iCarousel for doing this task. I do following changes in to there demo Buttons Demo
.h class
#property (nonatomic, assign) BOOL wrap;
.m class
- (void)viewDidLoad
{
[super viewDidLoad];
_wrap = YES;
//configure carousel
carousel.type = iCarouselTypeLinear;
}
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
return 9;
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
UILabel *label = nil;
//create new view if no view is available for recycling
if (view == nil)
{
view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 400)];
view.backgroundColor=[UIColor redColor];
((UIImageView *)view).image = [UIImage imageNamed:#"page.png"];
view.contentMode = UIViewContentModeCenter;
label = [[UILabel alloc] initWithFrame:view.bounds];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = UITextAlignmentCenter;
label.font = [label.font fontWithSize:50];
label.tag = 1;
[view addSubview:label];
}
else
{
//get a reference to the label in the recycled view
label = (UILabel *)[view viewWithTag:1];
}
label.text = [NSString stringWithFormat:#"%i", index];
return view;
}
- (UIView *)carousel:(iCarousel *)carousel placeholderViewAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
UILabel *label = nil;
//create new view if no view is available for recycling
if (view == nil)
{
//don't do anything specific to the index within
//this `if (view == nil) {...}` statement because the view will be
//recycled and used with other index values later
view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 400.0f)];
view.backgroundColor=[UIColor redColor];
((UIImageView *)view).image = [UIImage imageNamed:#"page.png"];
view.contentMode = UIViewContentModeCenter;
label = [[UILabel alloc] initWithFrame:view.bounds];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = UITextAlignmentCenter;
label.font = [label.font fontWithSize:50.0f];
label.tag = 1;
[view addSubview:label];
}
else
{
//get a reference to the label in the recycled view
label = (UILabel *)[view viewWithTag:1];
}
label.text = (index == 0)? #"[": #"]";
return view;
}
- (CGFloat)carousel:(iCarousel *)_carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value
{
//customize carousel display
switch (option)
{
case iCarouselOptionWrap:
{
//normally you would hard-code this to YES or NO
return _wrap;
}
case iCarouselOptionSpacing:
{
//add a bit of spacing between the item views
return value * 1.05f;
}
case iCarouselOptionFadeMax:
{
if (carousel.type == iCarouselTypeCustom)
{
//set opacity based on distance from camera
return 0.0f;
}
return value;
}
default:
{
return value;
}
}
}
- (CATransform3D)carousel:(iCarousel *)_carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
{
//implement 'flip3D' style carousel
transform = CATransform3DRotate(transform, M_PI / 8.0f, 0.0f, 1.0f, 0.0f);
return CATransform3DTranslate(transform, 0.0f, 0.0f, offset * carousel.itemWidth);
}
or getting tapped Button index:-
- (void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index
{
NSLog(#"Tapped view number: %d", index);
}
it's Output is :-
Here is Attached Demo

Set Selected State of CAGradient UIButton in iOS

Hi I have made a custom button in code using corner radius CAGradientLayer and border colour in one of my view controllers like the below:
phoneButton = [CustomButton buttonWithType:UIButtonTypeCustom];
phoneButton.frame = CGRectMake(6, 363, 99, 48);
phoneButton.titleLabel.font = [UIFont fontWithName:#"Futura-Medium" size:14];
phoneButton.titleLabel.shadowColor = [UIColor colorWithWhite:0.0 alpha:1.0];
phoneButton.titleLabel.shadowOffset = CGSizeMake(0, 1);
[phoneButton setTitle:#"Phone" forState:UIControlStateNormal];
[phoneButton addTarget:self action:#selector(phone) forControlEvents:UIControlEventTouchUpInside];
gradient = [CAGradientLayer layer];
gradient.frame = phoneButton.bounds;
gradient.cornerRadius = 8;
gradient.borderColor = [[UIColor whiteColor]CGColor];
gradient.borderWidth = 2.0;
gradient.colors = [NSArray arrayWithObjects:(id)[[sharedManager cellGradientEnd] CGColor], (id)[[sharedManager cellGradientStart] CGColor], nil];
[phoneButton.layer insertSublayer:gradient atIndex:0];
[self.view addSubview:phoneButton];
Now I would like to set the selected/highlighted color of the button on selection. How do I do this. I read make a UIbutton subclass and override setSelected but I dont have a clue how to do it. Here is customButton subclass.m
#import "CustomButton.h"
#implementation CustomButton
#synthesize sharedManager;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
sharedManager = [[MySingleton alloc]init];
}
return self;
}
-(void) setHighlighted:(BOOL)highlighted {
if(highlighted) {
NSLog(#"Highlighted");
} else {
NSLog(#"Not Highlighted");
}
[super setHighlighted:highlighted];
}
-(void) setSelected:(BOOL)selected {
if(selected) {
NSLog(#"Selected");
} else {
NSLog(#"Not Selected");
}
[super setSelected:selected];
}
#end
Or just dim the button on selection would be good? I should add that the button is not in a Xib.
I figured it out just by creating a selected Gradient and unselected Gradient state of the button in subclass.m now it is all working great!
- (CustomButton *)buttonWithType:(UIButtonType)type
{
return [self buttonWithType:UIButtonTypeCustom];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
//Call the parent implementation of initWithCoder
self = [super initWithCoder:coder];
//Custom drawing methods
if (self)
{
[self drawBackgroundLayer];
[self drawHighlightBackgroundLayer];
highlightBackgroundLayer.hidden = YES;
}
return self;
}
-(void)loadSingleton{
sharedManager = [[MySingleton alloc]init];
}
- (void)layoutSubviews
{
// Set gradient frame (fill the whole button))
backgroundLayer.frame = self.bounds;
// Set inverted gradient frame
highlightBackgroundLayer.frame = self.bounds;
[super layoutSubviews];
}
- (void)drawBackgroundLayer
{
[self loadSingleton];
// Check if the property has been set already
if (!backgroundLayer)
{
backgroundLayer = [CAGradientLayer layer];
backgroundLayer.cornerRadius = 8;
backgroundLayer.borderWidth = 1.5;
backgroundLayer.borderColor = [UIColor whiteColor].CGColor;
backgroundLayer.colors = [NSArray arrayWithObjects:(id)[[sharedManager cellGradientEnd] CGColor], (id)[[sharedManager cellGradientStart] CGColor], nil];
// Add the gradient to the layer hierarchy
[self.layer insertSublayer:backgroundLayer atIndex:0];
}
}
- (void)drawHighlightBackgroundLayer
{
[self loadSingleton];
if (!highlightBackgroundLayer)
{
highlightBackgroundLayer = [CAGradientLayer layer];
highlightBackgroundLayer.cornerRadius = 8;
highlightBackgroundLayer.borderWidth = 1.5;
highlightBackgroundLayer.borderColor = [UIColor whiteColor].CGColor;
highlightBackgroundLayer.colors = [NSArray arrayWithObjects:(id)[[sharedManager cellSelectedGradientEnd] CGColor], (id)[[sharedManager cellSelectedGradientStart] CGColor], nil];
[self.layer insertSublayer:highlightBackgroundLayer atIndex:1];
}
}
and setting the selected state on or off
- (void)setHighlighted:(BOOL)highlighted
{
NSLog(#"Selected");
// Disable implicit animation
[CATransaction begin];
[CATransaction setDisableActions:YES];
// Hide/show inverted gradient
highlightBackgroundLayer.hidden = !highlighted;
[CATransaction commit];
[super setHighlighted:highlighted];
}
Have you tried invoking super and then invoking setNeedsDisplay on self?
It should cause your display functionality to be invoked at the appropriate time, and in that code you should be checking for selected/highlighted.
If I understand correctly what you are trying to do, I would suggest the following approach:
move the CAGradientLayer inside of your CustomButton implementation (so it would become a CustomGradientButton);
when you want to set the selected state for the custom button, change the CAGradientLayer gradientColors by changing their saturation and brightness.
By doing this, your button will change its appearance in the selected state.
A way to modify saturation and brightness could be through this UIColor category of mine:
#interface UIColor (LighterDarkerColor)
- (UIColor*)colorWithSaturation:(float)saturationFactor
brightness:(float)brightnessFactor;
#end
#implementation UIColor (LighterDarkerColor)
- (UIColor*)colorWithSaturation:(float)saturationFactor
brightness:(float)brightnessFactor {
float hue = 0.0;
float saturation = 0.0;
float brightness = 0.0;
float alpha = 0.0;
if ([self getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha])
return [UIColor colorWithHue:hue saturation:saturation*saturationFactor
brightness:brightness*brightnessFactor alpha:alpha];
return self;
}
#end
You could do, e.g.:
UIColor* selectedColorStart = [[sharedManager cellGradientStart] colorWithSaturation:0.65 brightness:1.2];
to get a less saturated, brighter version of your cellGradientStart color. Then in your setSelected method you would modify your CAGradientLayer:
gradient.colors = [NSArray arrayWithObjects:(id)[selectedColorEnd CGColor], (id)[selectedColorStart CGColor], nil];
This approach works for me, although you need to fine-tune your selection of saturation and brightness working for your case.

Resources