Subclassed UISegmentedControl titleTextAttributes not always applied - ios

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;
}

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

In Objective-C, how can I change the property of an instance inside of class via UIControl?

This question is about the exercise 6.9 of iOS Programming 4th Edition.
I have a class that can draw concentric circles and respond to touches by changing its color randomly.
Here is the implementation of the view:
#implementation LTHypnosisView
- (instancetype) initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self){
self.backgroundColor = [UIColor clearColor];
self.circleColor = [UIColor lightGrayColor];
}
return self;
}
- (void)drawRect:(CGRect)rect {
//Draw the circles
}
- (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
float red = (arc4random() % 100) /100.0;
float green = (arc4random() % 100) /100.0;
float blue = (arc4random() % 100) /100.0;
UIColor *randomColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
self.circleColor = randomColor;
}
- (void) setCircleColor:(UIColor *)circleColor{
_circleColor = circleColor;
[self setNeedsDisplay];
}
#end
Here is the implementation of the viewController:
#implementation LTHypnosisViewController
- (void) viewDidLoad{
CGRect screenRect = self.view.bounds;
LTHypnosisView *mainView = [[LTHypnosisView alloc] initWithFrame:screenRect];
[self addSubview:mainView]
NSArray *items = #[#"Red",#"Green",#"Blue"];
UISegmentedControl *segControl = [[UISegmentedControl alloc] initWithItems:items];
[segControl addTarget:self
action:#selector(change:)
forControlEvents:UIControlEventValueChanged];
segControl.frame = CGRectMake(10, 50, self.view.bounds.size.width-20, 60);
segControl.selectedSegmentIndex = 0;
segControl.backgroundColor = [UIColor whiteColor];
[self.view addSubview:segControl];
}
- (void) change:(UISegmentedControl*)sender{
switch (sender.selectedSegmentIndex) {
//How should I write here??
}
}
#end
I want to change the color of the concentric circles with the UISegmentedControl. Consequently I need a method within the viewController to change the _circleColor property of the LTHypnosisView instance. How can I do that?
A simple way, I'd make LTHypnosisView *mainView a property of the view controller, make setCircleColor public, and then in change: method call [self.mainView setCircleColor:myColor];
You could also do something more involved, like define a protocol on LTHypnosisView and set the view controller as the datasource. Whenever the change: method is called, call something like [mainView updateColor] and within updateColor have it read the color from it's datasource.

UIBezierPath Animation in UIView

I'm trying to make a animation that is done in Skype iOS application. The animation can be seen in this link Animation Section 3:Add a Contact
I tried with UIBezierPath but I can't do it. Any ideas?
Thanks!
My code
#implementation CustomView
#pragma mark - Lifecycle and initialize components
- (instancetype) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if ( self ) {
self.alpha = 0;
[self configureCancelButton];
[self configureCollectionView];
[self addSubview:self.cancelButton];
[self addSubview:self.collectionView];
}
return self;
}
- (void) configureCancelButton {
self.cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.cancelButton setTitle:#"Cancel" forState:UIControlStateNormal];
self.cancelButton.titleLabel.font = [UIFont systemFontOfSize:20];
[self.cancelButton addTarget:self action:#selector (cancelView) forControlEvents:UIControlEventTouchUpInside];
self.cancelButton.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.8];
self.cancelButton.layer.cornerRadius = 7.f;
self.frameForCancelButton = CGRectMake (self.bounds.origin.x+5, self.bounds.size.height - kCancelButtonHeight - 5, self.bounds.size.width-10, kCancelButtonHeight);
self.cancelButton.frame = CGRectMake (self.frameForCancelButton.origin.x, self.frameForCancelButton.origin.y + kAnimationOffset, self.frameForCancelButton.size.width, self.frameForCancelButton.size.height);
}
- (void) configureCollectionView {
UICollectionViewFlowLayout *layout= [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(kItemSize, kItemSize);
layout.minimumInteritemSpacing = 3.0;
layout.sectionInset = UIEdgeInsetsMake(0, 7, 7, 10);
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.frameForCollectionView = CGRectMake(self.cancelButton.frame.origin.x, self.frame.size.height - (self.cancelButton.frame.size.height + 10 + kCollectionHeight), self.cancelButton.bounds.size.width, kCollectionHeight);
self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake (self.frameForCollectionView.origin.x, self.frameForCollectionView.origin.y + kAnimationOffset, self.frameForCollectionView.size.width, self.frameForCollectionView.size.height)
collectionViewLayout:layout];
self.collectionView.backgroundColor = [UIColor colorWithWhite:1 alpha:.8];
[self.collectionView registerClass:[RAShareCell class] forCellWithReuseIdentifier:#"Cell"];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
self.collectionView.layer.cornerRadius = 7.f;
}
#pragma mark - Public Methods
- (void) present {
[UIView animateWithDuration:0.35 animations:^{
self.alpha = 1;
self.darkView.alpha = .5;
}];
[UIView animateWithDuration:1.0 delay:0.45 options:(UIViewAnimationOptions) UIViewAnimationCurveEaseInOut animations:^{
[self setupAnimation];
} completion:nil];
}
- (void) cancelView {
[UIView animateWithDuration:0.35 delay:0 options:UIViewAnimationCurveEaseInOut animations:^{
self.cancelButton.frame = CGRectMake (self.frameForCancelButton.origin.x, self.frameForCancelButton.origin.y + kAnimationOffset, self.frameForCancelButton.size.width, self.frameForCancelButton.size.height);
self.collectionView.frame= CGRectMake(self.frameForCollectionView.origin.x, self.frameForCollectionView.origin.y + kAnimationOffset, self.frameForCollectionView.size.width, self.frameForCollectionView.size.height);
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
;
}
- (void) setupAnimation {
self.cancelButton.alpha = 1;
self.collectionView.alpha = 1;
self.cancelButton.frame = self.frameForCancelButton;
self.collectionView.frame = self.frameForCollectionView;
[self addShapeLayer];
}
- (void)addShapeLayer
{
self.shapeLayer = [CAShapeLayer layer];
self.shapeLayer.path = [[self pathAtInterval:0.0] CGPath];
self.shapeLayer.fillColor = [[UIColor redColor] CGColor];
self.shapeLayer.lineWidth = 3.0;
self.shapeLayer.strokeColor = [[UIColor redColor] CGColor];
[self.collectionView.layer insertSublayer:self.shapeLayer atIndex:0];
}
- (UIBezierPath *) pathAtInterval:(NSTimeInterval) interval {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(self.collectionView.frame.origin.x,self.collectionView.frame.origin.y)];
[path addQuadCurveToPoint:CGPointMake(self.collectionView.frame.size.width, self.collectionView.frame.origin.y) controlPoint:CGPointMake(self.collectionView.frame.size.width/2, self.collectionView.frame.origin.y-30)];
return path;
}
Your animation block does not contain any animations. You should move your setup outside of the animation block and then animate within it.
[self setupAnimation];
[UIView animateWithDuration:1.0 delay:0.45 options:(UIViewAnimationOptions) UIViewAnimationCurveEaseInOut animations:^{
// animate something
} completion:nil];
In addition to this, your intended animation does not have a linear progression, meaning that you'l want to use animateKeyframesWithDuration instead.
[UIView animateKeyframesWithDuration:1.0 delay:0.45 options:UIViewAnimationCurveEaseInOut animations:^{
for (NSInteger i = 0; i < 10; i++) {
[UIView addKeyframeWithRelativeStartTime:i/10.0
relativeDuration:1.0/10.0
animations:^{
self.shapeLayer = [self pathAtInterval:i/10.0];
}];
}
} completion:nil];
1, add the path you created to CAShaperLayer object;
2, see the "strokeStart" and "strokeEnd" properties in CAShapeLayer class;
3, add a animation which called "CABasicAnimation" to your Layer obj.

iOS - change gradient background of UIButton or custom UIButton

I created my own class derived from UIButton with .h file:
#interface MenuButton : UIButton
- (void)changeBackground;
#end
In implementation I tried to add some methods for changing background but without success so far:
#import "MenuButton.h"
#import "GradientLayers.h"
#implementation MenuButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder;
{
self = [super initWithCoder:aDecoder];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit;
{
// Gradient background
CAGradientLayer *gradient = [GradientLayers darkBlueBackground];
gradient.frame = [self layer].bounds;
// Insert background
[[self layer] insertSublayer:gradient atIndex:0];
[[self layer] setCornerRadius:10.0f];
[[self layer] setMasksToBounds:YES];
[[self layer] setBorderWidth:2];
[[self layer] setBorderColor:[[UIColor blackColor] CGColor]];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// handle touch
[super touchesEnded:touches withEvent:event];
if ([self state] != UIControlStateSelected)
{
CAGradientLayer *gradient = [GradientLayers blackBackground];
gradient.frame = [self layer].bounds;
[[self layer] insertSublayer:gradient atIndex:0];
}
else
{
CAGradientLayer *gradient = [GradientLayers darkBlueBackground];
gradient.frame = [self layer].bounds;
[[self layer] insertSublayer:gradient atIndex:0];
}
}
- (void)changeBackground
{
CAGradientLayer *gradient = [[[self layer] sublayers] firstObject];
gradient = [GradientLayers blackBackground];
[[self layer] insertSublayer:gradient atIndex:0];
[self setNeedsDisplay];
}
#end
Part of GradientLayer.m file:
#import "GradientLayers.h"
#implementation GradientLayers
+ (CAGradientLayer*) blackBackground {
// similar to darkBlueBackground
}
+ (CAGradientLayer*) darkBlueBackground {
// Create colors
UIColor *darkOp = [UIColor colorWithRed:0.039f green:0.106f blue:0.278f alpha:1.0];
UIColor *lightOp = [UIColor colorWithRed:0.133f green:0.267f blue:0.65f alpha:1.0];
// Create gradient
CAGradientLayer *gradient = [CAGradientLayer layer];
// Set colors
gradient.colors = [NSArray arrayWithObjects:
(id)lightOp.CGColor,
(id)darkOp.CGColor,
nil];
// Shadow
gradient.shadowOffset = CGSizeMake(3.0f, 3.0f);
gradient.shadowColor = [UIColor blackColor].CGColor;
gradient.shadowOpacity = 0.6;
gradient.shadowRadius = 10;
// Other options
gradient.borderWidth = 0.5f;
gradient.borderColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0].CGColor;
gradient.cornerRadius = 10;
return gradient;
}
#end
I have my buttons with darkBlueBackground as set in commonInit. It's not a problem. But I want to change it after user taps on button and so far without luck. If I set breakpoint I am in touchesEnded or changeBackground but after execution the button has same background as before. So how can I change background to another gradient background? Thanks
Because the [self layer].sublayers array is listed in back to front order (https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/CALayer_class/Introduction/Introduction.html#//apple_ref/occ/instp/CALayer/sublayers), by inserting your replacement layer at index 0, you're always putting it behind the old gradient.
You should remove the old gradient layer and then add the new one.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// handle touch
[super touchesEnded:touches withEvent:event];
// Remove old gradient here
[[[[self layer] sublayers] objectAtIndex:0] removeFromSuperlayer];
if ([self state] != UIControlStateSelected)
{
CAGradientLayer *gradient = [GradientLayers blackBackground];
gradient.frame = [self layer].bounds;
[[self layer] insertSublayer:gradient atIndex:0];
}
else
{
CAGradientLayer *gradient = [GradientLayers darkBlueBackground];
gradient.frame = [self layer].bounds;
[[self layer] insertSublayer:gradient atIndex:0];
}
}
- (void)changeBackground
{
// Remove old gradient here
[[[[self layer] sublayers] objectAtIndex:0] removeFromSuperlayer];
CAGradientLayer *gradient = [[[self layer] sublayers] firstObject];
gradient = [GradientLayers blackBackground];
[[self layer] insertSublayer:gradient atIndex:0];
[self setNeedsDisplay];
}
You really should keep track of the gradient better, so you're not constantly removing and replacing it with the same gradient. Perhaps maybe just showing and hiding the selected state, which would always just sit above the unhighlighted version.

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