iosobjective-ciphoneuiimageviewcgcolor

How to create an overlay color with a gradient in iOS


Background info: I have a UIImageView. I have added an overlay colour on its image in the following way:

UIGraphicsBeginImageContext(initialImage.size);
[initialImage drawInRect:CGRectMake(0, 0, initialImage.size.width, initialImage.size.height) blendMode:kCGBlendModeNormal alpha:alphaValue];
UIBezierPath * path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, initialImage.size.width, initialImage.size.height)];
[overlayColor setFill];
[path fillWithBlendMode:kCGBlendModeMultiply alpha:1];
finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

[self setImage:finalImage];

I still want to add this as an overlay colour but I want it to have a gradient. I have been trying to figure out a way to do this but haven't really been successful. I guess, the approach of adding an overlay colour with a gradient is wrong? I'm not sure about how to do this. I have tried to add a CGGradientLayer as a sublayer to the UIImageView but it doesn't work.

I thought about adding a UIView and setting it's backgroundColor as the overlayColorand then adding a CGGradientLayer as sublayer of the UIView which is added as a subview to the UIImageView but, we are not supposed to add subviews to UIImageViews.

Can someone please help me with this? Maybe I should change my approach?

Pointing me in the right direction will be great as well!

I look forward to your responses and apologies if this post hasn't been entirely clear!

Thanks in advance for your help!

Edit: Code for the CGGradientLayer

CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.frame;

UIColor *colorOne = [UIColor colorFromHex:self.feedColor withAlpha:(alphaValue * 0.7)];
UIColor *colorTwo = [UIColor colorFromHex:self.feedColor withAlpha:(alphaValue * 1.0)];

gradient.colors = [NSArray arrayWithObjects:(id)colorOne.CGColor, (id)colorTwo.CGColor, nil];

[self.layer insertSublayer:gradient atIndex:0];

Solution

  • If you're looking for a more dynamic approach, then I would subclass CALayer instead of UIImageView. Therefore you'd want something like this:

    @interface gradientImageLayer : CALayer
    
    - (instancetype)initWithFrame:(CGRect)frame;
    
    @end
    
    @implementation gradientImageLayer
    
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super init]) {
    
            self.opaque = YES; // Best for performance, but you if you want the layer to have transparency, then remove.
    
            UIImage *i = [UIImage imageNamed:@"foo2.png"]; // Replace with your image
    
            self.frame = frame;
    
            self.contentsScale = [UIScreen mainScreen].nativeScale;
            self.contents = (__bridge id _Nullable)(i.CGImage);
    
            // Your code for the CAGradientLayer was indeed correct.
            CAGradientLayer *gradient = [CAGradientLayer layer];
            gradient.frame = frame;
    
            // Add whatever colors you want here.
            UIColor *colorOne = [UIColor colorWithRed:1 green:1 blue:0 alpha:0.1];
            UIColor *colorTwo = [UIColor colorWithRed:0 green:1 blue:1 alpha:0.2];
    
            gradient.colors = @[(id)colorOne.CGColor, (id)colorTwo.CGColor]; // Literals read far nicer than a clunky [NSArray arrayWith.... ]
    
            [self addSublayer:gradient];        
        }
    
        return self;
    }
    
    @end
    

    The downside to this approach is you are unable to apply different blend modes. The only solutions I've seen to applying a blend mode on a CALayer is through Core Graphics, but then you'd be better off with my original answer.