I couldn't draw fully gradient background on iPad - ios

I drew gradient background layer on UICollectionView with following code.
CAGradientLayer* collectionRadient = [CAGradientLayer layer];
collectionRadient.bounds = self.collectionView.bounds;
collectionRadient.anchorPoint = CGPointZero;
collectionRadient.colors = [NSArray arrayWithObjects:(id)[startColor CGColor],(id)[endColor CGColor], nil];
UIView* vv = [[UIView alloc] init];
self.collectionView.backgroundView = vv;
[self.collectionView.backgroundView.layer insertSublayer:collectionRadient atIndex:0];
It works on iPhone simulator. but not iPad.
Below Image is that. the background layer is taking up only a part of UICollectionView.

I would suggest creating a subclass of UIView named GradientView or something similar which you would use as your background view.
In your implementation of GradientView you just need to overload the following class method:
+ (Class)layerClass
{
return [CAGradientLayer class];
}
Then, change your code above to the following:
UIView* backgroundView = [[GradientView alloc] initWithFrame:self.collectionView.bounds];
backgroundView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
self.collectionView.backgroundView = backgroundView;
CAGradientLayer* collectionGradient = (CAGradientLayer *)backgroundView.layer;
collectionGradient.anchorPoint = CGPointZero;
collectionGradient.colors = [NSArray arrayWithObjects:(id)[startColor CGColor],(id) [endColor CGColor], nil];
I'm hoping that your custom GradientView's layer will automatically resize. If not, you may need to implement the layoutSubviews method in your custom GradientView to resize your gradient layer.

Related

code not running when remove full view and again add UIslider background color

The first time it works nice but second time when changes view constraint and again going to this function it crashes in below code.
anyone know please let me know thank you.
below code running first time perfect second time in the same view not running perfectly.
UIView *view = (UIView*)[slider.subviews objectAtIndex:0];
-(void)setGradientToSlider:(UISlider *)slider WithColors:(NSArray*)colorArray
{
UIView *view;
UIImageView *max_trackImageView;
view = (UIView*)[slider.subviews objectAtIndex:0];
max_trackImageView = (UIImageView*)[view.subviews objectAtIndex:0];
//setting gradient to max track image view.
CAGradientLayer* max_trackGradient = [CAGradientLayer layer];
CGRect rect=max_trackImageView.frame;
rect.origin.x=view.frame.origin.x;
max_trackGradient.frame=rect;
max_trackGradient.colors = colorArray;
[max_trackGradient setStartPoint:CGPointMake(0.0, 0.5)];
[max_trackGradient setEndPoint:CGPointMake(1.0, 0.5)];
[view.layer setCornerRadius:5.0];
[max_trackImageView.layer insertSublayer:max_trackGradient atIndex:0];
//Setting gradient to min track ImageView.
CAGradientLayer* min_trackGradient = [CAGradientLayer layer];
UIImageView *min_trackImageView;
min_trackImageView = (UIImageView*)[slider.subviews objectAtIndex:1];
rect=min_trackImageView.frame;
rect.size.width=max_trackImageView.frame.size.width;
rect.origin.y=0;
rect.origin.x=0;
min_trackGradient.frame=rect;
min_trackGradient.colors = colorArray;
[min_trackGradient setStartPoint:CGPointMake(0.0, 0.5)];
[min_trackGradient setEndPoint:CGPointMake(1.0, 0.5)];
[min_trackImageView.layer setCornerRadius:5.0];
[min_trackImageView.layer insertSublayer:min_trackGradient atIndex:0];
}

How do I create a growing iOS Button?

My friend gave me the following designs for iOS buttons, and I'm not sure the best way to implement this.
I need to make the reusable button shown below (in Objective-C).
I've tried:
Subclassing the button, but read that I shouldn't do that
Animating the border (while subclassed), but the border only goes inwards, so it seems like I need to animate the frame too
So how do I approach this? I'm assuming making a CustomButtonView class which has a button (composition) as well as an inner and outer circle view? How would I then animate that to grow? Would I have to animate the frame change too, or could I use insets?
What is the simplest code to make this work? Thanks!
Here Is the approach I took to create this:
Subclass UIView to create your custom button
Use UITapGestureRecognizer or touchesBegan, touchesEnded... for your interaction
Add two CALayer's for your foreground and background layers
Add your icon layer (This can be an UIImageView or any other way of displaying an image)
- (id)initWithIcon:(UIImage *)icon backgroundColor:(UIColor *)backgroundColor foregroundColor:(UIColor *)foregroundColor {
if (self = [super init]) {
// Background Layer Setup
_backgroundLayer = [CALayer new];
[_backgroundLayer setBackgroundColor:backgroundColor.CGColor];
[self.layer addSublayer:_backgroundLayer];
// Foreground Layer Setup
_foregroundLayer = [CALayer new];
[_foregroundLayer setBackgroundColor:foregroundColor.CGColor];
[self.layer addSublayer:_foregroundLayer];
// Icon Setup
_icon = [[UIImageView alloc] initWithImage:icon];
[_icon setContentMode:UIViewContentModeCenter];
[self addSubview:_icon];
UIGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(buttonTapped:)];
[self addGestureRecognizer:tapGesture];
}
return self;
}
- (void)setFrame:(CGRect)frame {
// Make sure super is called
[super setFrame:frame];
// Build the layout of backgroundLayer
[self.backgroundLayer setFrame:CGRectMake(frame.size.width*0.1, frame.size.width*0.1, frame.size.width*0.8, frame.size.width*0.8)];
[self.backgroundLayer setCornerRadius:frame.size.width*0.8/2];
// Build the layout of forgroundLayer
[self.foregroundLayer setFrame:CGRectMake(frame.size.width*0.05, frame.size.width*0.05, frame.size.width*0.9, frame.size.width*0.9)];
[self.foregroundLayer setCornerRadius:frame.size.width*0.9/2];
// Build the frame of your icon
[self.icon setFrame:CGRectMake(0, 0, frame.size.width, frame.size.width)];
}
- (void)buttonTapped:(UIGestureRecognizer*)gesture {
// Animate the foreground getting smaller
CABasicAnimation *foregroundFrameChange = [CABasicAnimation animationWithKeyPath:#"frame"];
foregroundFrameChange.fromValue = [NSValue valueWithCGRect:_foregroundLayer.frame];
foregroundFrameChange.toValue = [NSValue valueWithCGRect:CGRectMake(self.frame.size.width*0.1,self.frame.size.width*0.1, self.frame.size.width*0.8, self.frame.size.width*0.8)];
self.foregroundLayer.frame = CGRectMake(self.frame.size.width*0.1,self.frame.size.width*0.1, self.frame.size.width*0.8, self.frame.size.width*0.8);
// Animate the forground cornerRadius to stay rounded
CABasicAnimation *foregroundRadiusChange = [CABasicAnimation animationWithKeyPath:#"cornerRadius"];
foregroundRadiusChange.fromValue = [NSNumber numberWithDouble:self.foregroundLayer.cornerRadius];
foregroundRadiusChange.toValue = [NSNumber numberWithDouble:self.frame.size.width*0.8/2];
[self.foregroundLayer setCornerRadius:self.frame.size.width*0.8/2];
// Animate the background getting larger
CABasicAnimation *backgroundFrameChange = [CABasicAnimation animationWithKeyPath:#"frame"];
backgroundFrameChange.fromValue = [NSValue valueWithCGRect:self.backgroundLayer.frame];
backgroundFrameChange.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.width)];
self.backgroundLayer.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.width);
// Animate the background cornerRadius to stay rounded
CABasicAnimation *backgroundRadiusChange = [CABasicAnimation animationWithKeyPath:#"cornerRadius"];
backgroundRadiusChange.fromValue = [NSNumber numberWithDouble:self.backgroundLayer.cornerRadius];
backgroundRadiusChange.toValue = [NSNumber numberWithDouble:self.frame.size.width/2];
[self.backgroundLayer setCornerRadius:self.frame.size.width/2];
// Group all the animations to run simultaneously
CAAnimationGroup *allAnimations = [CAAnimationGroup animation];
allAnimations.duration = 2;
allAnimations.animations = #[foregroundFrameChange, foregroundRadiusChange, backgroundFrameChange, backgroundRadiusChange];
allAnimations.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.layer addAnimation:allAnimations forKey:#"animate"];
// Create your button action callback here
}
This was a quick mock up and not a complete solution but it will give you something to play with.

Change UIButton gradient color

I use the following method to add some gradient effect to my IOS UIButton. I call it in viewController's viewDidLoad, works great, so far so good.
Now, I'd like to change the color of the button according to some user interaction. What I do is simply calling this method by different colors (fromColor, toColor) as input. The problem, the look of my button does not change. I tried to call setNeedsDisplay, but did not help.
Would you be so kind to help me? What I miss? There is should be some problem with layers, should be rested or something like that, but I could not find its proper way.
Update: By using setSubLayers instead of insertSubLayer color changes, but button title disappears.
+(void)setCustomButtonStyle:(UIButton *)button cornerRadius:(float) cornerRadius fromColor:(UIColor *)fromColor toColor:(UIColor *)toColor normalTitleColor:(UIColor *)normalTitleColor highlightedTitleColor:(UIColor *)highlightedTitleColor borderWidth:(float) borderWidth borderColor:(UIColor*)borderColor
{
[button setTitleColor:normalTitleColor forState:UIControlStateNormal];
[button setTitleColor:highlightedTitleColor forState:UIControlStateHighlighted];
CAGradientLayer *btnGradient = [CAGradientLayer layer];
btnGradient.frame = button.bounds;
btnGradient.colors = [NSArray arrayWithObjects:
(id)[fromColor CGColor],
(id)[toColor CGColor],
nil];
btnGradient.borderWidth = borderWidth;
btnGradient.borderColor = [borderColor CGColor];
[button.layer insertSublayer:btnGradient atIndex:0];
CALayer *btnLayer = [button layer];
[btnLayer setMasksToBounds:YES];
[btnLayer setCornerRadius:cornerRadius];
}
The problem you are having here is that 1. you are creating a new layer each time you are changing the colour and 2. you are then placing that new layer underneath everything else so you can't see it anyway. (Think of putting a playing card at the bottom of the deck).
What you need to do is keep hold of the gradient layer and then update then instead of creating a new one.
You are probably best doing this in a category on UIButton.
Something like... UIButton+GradientBackground or something.
In the .h file give it a single function...
// Use CGFloat not float. Also user NSInteger not int.
// Always.
// Also, I have used modern Obj-C syntax. Use this too.
- (void)setCornerRadius:(CGFloat)cornerRadius
fromColor:(UIColor *)fromColor
toColor:(UIColor *)toColor
normalTitleColor:(UIColor *)normalTitleColor
highlightedTitleColor:(UIColor *)highlightedTitleColor
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor;
Then in the .m file you need to also give it a property...
#property (nonatomic, strong) CAGradientLayer *gradientLayer;
Now in the function...
- (void)setCornerRadius:(CGFloat)cornerRadius
fromColor:(UIColor *)fromColor
toColor:(UIColor *)toColor
normalTitleColor:(UIColor *)normalTitleColor
highlightedTitleColor:(UIColor *)highlightedTitleColor
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor
{
[self setTitleColor:normalTitleColor forState:UIControlStateNormal];
[self setTitleColor:highlightedTitleColor forState:UIControlStateHighlighted];
if (!self.grandientLayer) {
// if it doesn't exist then create it and add it (only once)
self.gradientLayer = [CAGradientLayer layer];
[self.layer insertSublayer:self.gradientLayer atIndex:0];
}
self.gradientLayer.frame = self.bounds;
self.gradientLayer.colors = #[(id)fromColor.CGColor, (id)toColor.CGColor]; // use modern syntax
self.gradientLayer.borderWidth = borderWidth;
self.gradientLayer.borderColor = borderColor.CGColor;
[self.layer setMasksToBounds:YES];
[self.layer setCornerRadius:cornerRadius];
}
Notice that I have removed the button from the function name as this function will essentially be part of that UIButton's functions so self is the button you are changing.
You would call it like this...
[someButton setCornerRadius:5 fromColor:[UIColor redColor] toColor... and so on];
By doing this you only create that gradient layer once and you keep hold of it so that you can update it the next time round.
For first time add gradient layer, next time replace added gradient layer
Added Category as #Fogmeister thinks this can be used with category only
#interface UIButton (gradient)
+(void)setCustomButtonStyle:(UIButton *)button cornerRadius:(float) cornerRadius fromColor:(UIColor *)fromColor toColor:(UIColor *)toColor normalTitleColor:(UIColor *)normalTitleColor highlightedTitleColor:(UIColor *)highlightedTitleColor borderWidth:(float) borderWidth borderColor:(UIColor*)borderColor;
#end
#implementation UIButton (gradient)
+(void)setCustomButtonStyle:(UIButton *)button cornerRadius:(float) cornerRadius fromColor:(UIColor *)fromColor toColor:(UIColor *)toColor normalTitleColor:(UIColor *)normalTitleColor highlightedTitleColor:(UIColor *)highlightedTitleColor borderWidth:(float) borderWidth borderColor:(UIColor*)borderColor
{
//Set Title color as you want
[button setTitleColor:normalTitleColor forState:UIControlStateNormal];
[button setTitleColor:highlightedTitleColor forState:UIControlStateHighlighted];
//check gradientlayer exist
id layer = nil;
if (button.layer.sublayers.count > 0) {
layer = [button.layer.sublayers objectAtIndex:0];
}
//Find added gradient layer
CAGradientLayer *addedGradLayer = nil;
if (layer) {
if ([layer isKindOfClass:[CAGradientLayer class]]) {
addedGradLayer = (CAGradientLayer *)[button.layer.sublayers objectAtIndex:0];
}
}
//check gradient layer exists
if (!addedGradLayer) //first time
{
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = button.layer.bounds;
gradientLayer.colors = [NSArray arrayWithObjects:
(id)fromColor.CGColor,
(id)toColor.CGColor,
nil];
gradientLayer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:1.0f],
nil];
gradientLayer.cornerRadius = button.layer.cornerRadius;
[button.layer insertSublayer:gradientLayer atIndex:0];
button.clipsToBounds = YES;
}
else //next time
{
CAGradientLayer *newGradientLayer = [CAGradientLayer layer];
newGradientLayer.frame = button.layer.bounds;
newGradientLayer.colors = [NSArray arrayWithObjects:
(id)fromColor.CGColor,
(id)toColor.CGColor,
nil];
newGradientLayer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:1.0f],
nil];
newGradientLayer.cornerRadius = button.layer.cornerRadius;
[button.layer replaceSublayer:addedGradLayer with:newGradientLayer];
}
}
#end

multiple punch-out style mask?

I've done simple CALayer masks before but I think I'm getting confused on what they do. I'm trying to have a punch out effect with several (2) views.
Here's what I have so far. I'm looking to have a white square with punched out label and image (so you can see the brown background through it. Where am I going wrong?
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor brownColor];
self.viewToPunch = [[UIView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.viewToPunch];
self.viewToPunch.backgroundColor = [UIColor whiteColor];
self.punchLabel = [[UILabel alloc] initWithFrame:CGRectZero];
self.punchLabel.textColor = [UIColor blackColor];
self.punchLabel.font = [UIFont boldSystemFontOfSize:20.0];
self.punchLabel.text = #"punch";
self.punchLabel.textAlignment = NSTextAlignmentCenter;
self.punchImage = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:#"plus"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
[self.punchImage setContentMode:UIViewContentModeCenter];
self.viewsToPunch = #[self.punchLabel,self.punchImage];
[self punch:self.viewToPunch withUIViews:self.viewsToPunch];
}
- (void)punch:(UIView *) viewToPunch withUIViews:(NSArray *)viewsToPunch
{
CALayer *punchMask = [CALayer layer];
punchMask.frame = viewToPunch.frame;
NSMutableArray *sublayers = [[NSMutableArray alloc] init];
for (UIView *views in viewsToPunch){
[sublayers addObject:views.layer];
}
punchMask.sublayers = sublayers;
punchMask.masksToBounds = YES;
viewToPunch.layer.mask = punchMask;
}
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
self.viewToPunch.frame = CGRectMake(50, 50, 100, 100);
self.punchLabel.frame = CGRectMake(0, 0, 100, 100);
self.punchImage.frame = CGRectMake(0, 0, self.viewToPunch.frame.size.width, 40.);
[self punch:self.viewToPunch withUIViews:self.viewsToPunch];
}
So not only do the frames seem to be off, it seems to be the opposite of a punch out. How do I invert the mask and fix up the frames?
Thanks a lot for any help! I put it in a method punch:withUIViews: so I can hopefully reuse it in other areas.
When you apply a mask to a CALayer, it only gets drawn in the parts where the mask is not transparent. But you're simply applying an empty (transparent) mask, with the wrong coordinates (which is why your view isn't completely transparent: the mask isn't covering the view completely; it should be punchMask.frame = viewToPunch.bounds;)
You might want to look into CAShapeLayer and assign it a path. Use that as mask layer.
For example, see CAShapeLayer mask view or Getting Creative with CALayer Masks (cached blog post).
I tried to combine mask of CAGradationLayer and CAShapeLayer, and It is possible.
https://stackoverflow.com/a/32220792/3276863

How can I access the standard viewForHeaderInSection for a tableView?

I've got an indexed UITableView with individual sections. I would like to use a different background color for the header views in each section. I know I can completely roll my own view by implementing tableView:viewForHeaderInSection: (for example, see question # 2898361), but that seems to be "too much work" to me - the standard view looks fine, I would just have to change its background color.
But how do I access this standard view? I can't use [super tableView:viewForHeaderInSection:] because this is a question of implementing a protocol and not an issue of inheritance. Any other way I can get the standard view?
I'm almost certain you can't do this easily. I used one of my tech support request on my dev account recently asking about altering the background and borders of UITableView sections. The apple engineer told me that this really wasn't an easy thing to do, and even if you managed to do it, you would probably affect performance. He also pointed me to cocoawithlove and an article about editing uitableviews:
http://cocoawithlove.com/2009/08/adding-shadow-effects-to-uitableview.html
Really, creating your own header isn't that much effort. Below is some code I pulled out of one of my projects - it was commented out, so might not work straight away - but you can get the idea:
- (CAGradientLayer *) greyGradient {
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.startPoint = CGPointMake(0.5, 0.0);
gradient.endPoint = CGPointMake(0.5, 1.0);
UIColor *color1 = [UIColor colorWithRed:255.0f/255.0f green:255.0f/255.0f blue:255.0f/255.0f alpha:1.0];
UIColor *color2 = [UIColor colorWithRed:240.0f/255.0f green:240.0f/255.0f blue:240.0f/255.0f alpha:1.0];
[gradient setColors:[NSArray arrayWithObjects:(id)color1.CGColor, (id)color2.CGColor, nil]];
return gradient;
}
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
CGFloat width = CGRectGetWidth(tableView.bounds);
CGFloat height = [self tableView:tableView heightForHeaderInSection:section];
UIView *container = [[[UIView alloc] initWithFrame:CGRectMake(0,0,width,height)] autorelease];
container.layer.borderColor = [UIColor grayColor].CGColor;
container.layer.borderWidth = 1.0f;
CAGradientLayer *gradient = [self greyGradient];
gradient.frame = container.bounds;
[container.layer addSublayer:gradient];
UILabel *headerLabel = [[[UILabel alloc] initWithFrame:CGRectMake(12,0,width,height)] autorelease];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.font= [UIFont boldSystemFontOfSize:19.0f];
headerLabel.shadowOffset = CGSizeMake(1, 1);
headerLabel.textColor = [UIColor whiteColor];
headerLabel.shadowColor = [UIColor darkGrayColor];
NSString *title = [self tableView:tableView titleForHeaderInSection:section];
headerLabel.text = title;
return container;
}
Make sure to
#import <QuartzCore/QuartzCore.h>
By the way... this isn't supposed to mimic the look of the standard headers - its just an example. But I'm sure with a bit of trial and error you could alter this to mimic the standard ones and then change the colors slightly.
Although the other answers correctly point out you cannot access the default view to make simple modifications to it, if you have nothing to customize for a particular section header, you can return nil from tableView:viewForHeaderInSection: and the table view will use the default view.
This is helpful if you only need to customize some of your headers.
For whatever reason this is undocumented.
There is one problem with #bandejapalsa solution: the previous cell's separator is still visible with this implementation where as it is not on the default iOS sectionHeaderView. The solution I found was to use a CALayer and offset it by 1 pix. The image needs to be 1pix taller than the view frame itself.
// Create the view for the header
CGRect aFrame =CGRectMake(0, 0, tableView.contentSize.width, IMIUICustomisation.sectionHeaderViewHeight);
UIView * aView = [[UIView alloc] initWithFrame:aFrame];
aView.backgroundColor = UIColor.clearColor;
// Create a stretchable image for the background that emulates the default gradient, only in green
UIImage *viewBackgroundImage = [[UIImage imageNamed:#"greenheader.png"] stretchableImageWithLeftCapWidth:12 topCapHeight:0];
// Cannot set this image directly as the background of the cell because
// the background needs to be offset by 1pix at the top to cover the previous cell border (Alex Deplov's requirement ^_^)
CALayer *backgroungLayer = [CALayer layer];
backgroungLayer.frame = CGRectMake(0, -1, tableView.contentSize.width, IMIUICustomisation.sectionHeaderViewHeight+1);
backgroungLayer.contents = (id) viewBackgroundImage.CGImage;
backgroungLayer.masksToBounds = NO;
backgroungLayer.opacity = 0.9;
[aView.layer addSublayer:backgroungLayer];
// Take care of the section title now
UILabel *aTitle = [[UILabel alloc] initWithFrame: CGRectMake(10, 0, aView.bounds.size.width-10, aView.bounds.size.height)];
aTitle.text = [delegate tableView:tableView titleForHeaderInSection:section];
aTitle.backgroundColor = UIColor.clearColor;
aTitle.font = [UIFont boldSystemFontOfSize:18];
aTitle.textColor = UIColor.whiteColor;
// Text shadow
aTitle.layer.shadowOffset = CGSizeMake(0, 1);
aTitle.layer.shadowRadius = .2;
aTitle.layer.masksToBounds = NO;
aTitle.layer.shadowOpacity = 0.5;
aTitle.layer.shadowColor = IMIUICustomisation.selectedElementTextShadowColor.CGColor;
[aView addSubview:aTitle];
return aView;

Resources