Recently I needed custom UI control for iOS that had rounded corners and a gradient background. To make the user experience as rich as possible I wanted the background of the control to flatten when the user tapped it. There were plenty of examples of gradient buttons and rounded corner views but nothing did exactly what I needed.

After a bit of experimentation I found the easiest solution was to add a gradient layer to a custom UIControl and swap it with a different layer when the control became highlighted.

The first step was to was to create a custom UIControl class and add a gradient background layer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#import <QuartzCore/QuartzCore.h>

@interface GradientControl : UIControl
{
    CAGradientLayer *normalBackground;
}
@end

@implementation GradientControl
- (id) initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder])
    {
        self.backgroundColor = [UIColor clearColor];

        normalBackground = [CAGradientLayer layer];
        normalBackground.frame = self.bounds;
        normalBackground.cornerRadius = 10;
        normalBackground.borderWidth = 1;
        normalBackground.borderColor = [[UIColor whiteColor] CGColor];

        normalBackground.colors = [NSArray arrayWithObjects:
                                   (id)[[UIColor colorWithHexString:@"a9aeba"] CGColor],
                                   (id)[[UIColor colorWithHexString:@"7e8790"] CGColor],
                                   (id)[[UIColor colorWithHexString:@"6f778b"] CGColor],
                                   (id)[[UIColor colorWithHexString:@"5b657d"] CGColor],
                                   nil];
        normalBackground.locations = [NSArray arrayWithObjects:
                                      [NSNumber numberWithFloat:0],
                                      [NSNumber numberWithFloat:0.5],
                                      [NSNumber numberWithFloat:0.51],
                                      [NSNumber numberWithFloat:1],
                                      nil];
    }

    return self;
}
@end

Next we need to add a UIView to our main view in Interface Builder and set the class property to our new custom GradientControl:

Now if we run it in the simulator we can see our new shiny rounded gradient control:

Although this new component responds to events and triggers any actions that you assign it doesn’t give any visual feedback to taps. This can be solved by creating a second gradient layer and swapping the layers when the highlight value changes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#import <QuartzCore/QuartzCore.h>

@interface GradientControl : UIControl
{
    CAGradientLayer *normalBackground;
    CAGradientLayer *highlightBackground;
}
@end

@implementation GradientControl
- (id) initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder])
    {
        self.backgroundColor = [UIColor clearColor];

        normalBackground = [CAGradientLayer layer];
        // setup normal background...

        highlightBackground = [CAGradientLayer layer];
        // setup highlight background...
    }

    return self;
}

- (void) setHighlighted:(BOOL)aHighlighted
{
    BOOL oldHighlighted = self.highlighted;
    [super setHighlighted:aHighlighted];

    if (oldHighlighted == aHighlighted)
    {
        return;
    }

    if (aHighlighted)
    {
        [self.layer replaceSublayer:normalBackground with:highlightBackground];
    }
    else
    {
        [self.layer replaceSublayer:highlightBackground with:normalBackground];
    }
}
@end

Now when we tap the control the background seamlessly switches to the highlighted background:

The last thing to do is to add some setters to change the background colours and the border. With these we can easily create a range of gradient controls:

The full code with examples can be found on GitHub.