preface
- Recently, I have encountered some problems in effect processing. Write an article to record the problems I met before. Here are two ways to deal with them
The first way: this idea is mainly to use Layer processing, using the offset way to achieve inside and outside shadow effect
Inner shadow: offset X and Y for inner glow: two layers offset for outer glow; four layers offset in four directions for outer shadow
1.KJShadowLayer has copy effect
- (instanceType)copyWithZone:(NSZone *)zone {KJShadowLayer *layer = [[KJShadowLayer allocWithZone:zone] init]; layer.frame = self.frame; layer.kj_path = self.kj_path; layer.kj_color = self.kj_color; layer.kj_offset = self.kj_offset; layer.kj_radius = self.kj_radius; layer.kj_opacity = self.kj_opacity; layer.kj_shadowType = self.kj_shadowType; return layer; }Copy the code
2. The initialization
- (instancetype)kj_initWithFrame:(CGRect)frame ShadowType:(KJShadowType)type{
if (self == [super init]) {
self.frame = frame;
self.drawsAsynchronously = YES;
self.contentsScale = [UIScreen mainScreen].scale;
self.kj_shadowType = type;
self.kj_shadowColor = UIColor.blackColor;
if (type == KJShadowTypeInnerShine) {
self.xxLayer = [self copy];
[self addSublayer:_xxLayer];
}else if (type == KJShadowTypeOuterShine || type == KJShadowTypeOuter) {
self.xLayer = [self copy];
self.xxLayer = [self copy];
self.xxxLayer = [self copy];
[self addSublayer:_xLayer];
[self addSublayer:_xxLayer];
[self addSublayer:_xxxLayer];
}
}
return self;
}
Copy the code
3. The paint Layer
- (void)drawInContext:(CGContextRef)context { CGRect rect = self.bounds; if (self.borderWidth ! = 0) rect = CGRectInset(rect, self.borderWidth, self.borderWidth); CGContextSaveGState(context); if (self.kj_shadowType == KJShadowTypeInner || self.kj_shadowType == KJShadowTypeInnerShine) { CGContextAddPath(context, self.kj_path.CGPath); CGContextClip(context); CGMutablePathRef outer = CGPathCreateMutable(); CGPathAddRect(outer, NULL, CGRectInset(rect, -1 * rect.size.width, -1 * rect.size.height)); CGPathAddPath(outer, NULL, self.kj_path.CGPath); CGPathCloseSubpath(outer); CGContextAddPath(context, outer); CGPathRelease(outer); }else{ CGContextAddPath(context, self.kj_path.CGPath); } UIColor *color = [self.kj_color colorWithAlphaComponent:self.kj_opacity]; CGContextSetShadowWithColor(context, self.kj_offset, self.kj_radius, color.CGColor); if (self.kj_shadowType == KJShadowTypeOuterShine || self.kj_shadowType == KJShadowTypeOuter) { CGContextDrawPath(context, kCGPathEOFill); }else{ CGContextDrawPath(context, kCGPathEOFillStroke); } CGContextRestoreGState(context); }Copy the code
4. Set properties
self.kj_path = self.kj_shadowPath;
self.kj_color = self.kj_shadowColor;
self.kj_radius = self.kj_shadowRadius;
self.kj_opacity = self.kj_shadowOpacity;
self.kj_offset = CGSizeMake(self.kj_shadowDiffuse, self.kj_shadowDiffuse);
[self setNeedsDisplay];
Copy the code
Second: the main operation of the image to process, create a new ImageView to bear the shadow, shadow and other effects
1. Projection – The core idea
1.1 – Archive and copy the view to be projected (because I only need the image above, I will archive and copy it and then take screenshots)
UIView - (UIView*)kj_copyView:(UIView*)view{NSData *tempArchive = [NSKeyedArchiver archivedDataWithRootObject:view]; return [NSKeyedUnarchiver unarchiveObjectWithData:tempArchive]; }Copy the code
1.2 – Take a screenshot and modify the color of the picture
/ / / get the screenshot - (UIImage *) kj_captureView: (UIView *) view {UIGraphicsBeginImageContext (view. Bounds. The size). CGContextRef ctx = UIGraphicsGetCurrentContext(); [view.layer renderInContext:ctx]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; Kj_changeImageColor :(UIColor*)color Image:(UIImage*) Image { UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextTranslateCTM(context, 0, image.size.height); CGContextScaleCTM (context, 1.0, 1.0); CGContextSetBlendMode(context, kCGBlendModeNormal); CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height); CGContextClipToMask(context, rect, image.CGImage); [color setFill]; CGContextFillRect(context, rect); UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; }Copy the code
1.3 – Achieve effect
Distance and Angle: use offset coordinates
CGFloat x = info.diffuse * sin(info.angle);
CGFloat y = info.diffuse * cos(info.angle);
self.frame = CGRectMake(self.originX+x, self.originY+y, self.width, self.height);
Copy the code
Blur: The blur filter from the Accelerate framework is used. The main function is box filter vImageBoxConvolve_ARGB8888 and swap pixel channel vImagePermuteChannels_ARGB8888. CGImageAlphaInfo need to use kCGImageAlphaPremultipliedLast enumeration, thus retain transparent area unchanged (black)
/// Box filter error = VImageBoxConvolve_ARGB8888 (& inBuffer, & outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend); if (error) NSLog(@"error from convolution %ld", error); /// swap pixel channel from BGRA to RGBA const Uint8_t permuteMap[] = {2, 1, 0, 3}; vImagePermuteChannels_ARGB8888(&outBuffer,&rgbOutBuffer,permuteMap,kvImageNoFlags); / / / kCGImageAlphaPremultipliedLast keep transparent areas CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB (); CGContextRef ctx = CGBitmapContextCreate(rgbOutBuffer.data, rgbOutBuffer.width, rgbOutBuffer.height, 8, rgbOutBuffer.rowBytes, colorSpace, kCGImageAlphaPremultipliedLast);Copy the code
2. Shadow (actually shadow and glow are the same basic principle)
2.1 – Generate path graph
- (UIImage*)kj_getImageWithColor:(UIColor*)color Extend:(CGFloat) Extend { UIGraphicsBeginImageContext(self.superview.size); UIBezierPath *path = self.outsidePath; path.lineWidth = extend; path.lineCapStyle = kCGLineCapRound; path.lineJoinStyle = kCGLineCapRound; [color set]; [path stroke]; UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return img; }Copy the code
2.2 – Fuzzy processing (processing method is the same as projection)
It should be noted that the generated path map contains inside and outside shadows, which need to be trimmed if used alone
2.3 – Cropping processing
Outside shadow: Crop out the inside of the path
// path clipping, Kj_outerCaptureWithImage :(UIImage*)image{ UIGraphicsBeginImageContextWithOptions(self.superview.bounds.size, NO, image.scale); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetBlendMode(context, kCGBlendModeClear); / / / kCGBlendModeClear cut partially transparent [image drawInRect: self. Superview. Bounds]; CGContextAddPath(context, self.outsidePath.CGPath); CGContextDrawPath(context, kCGPathEOFill); UIImage *newimage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newimage; }Copy the code
Inner shadow: Similarly, crop out the parts outside the path
- (UIImage*)kj_innerCaptureWithImage:(UIImage*)image{ UIGraphicsBeginImageContextWithOptions(self.superview.bounds.size, NO, image.scale); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetBlendMode(context, kCGBlendModeClear); [image drawInRect:self.superview.bounds]; UIBezierPath * path = ({/ / / hollow-out UIBezierPath * path = [UIBezierPath bezierPathWithRect: self. Superview. Bounds]; path.usesEvenOddFillRule = YES; [path appendPath:self.outsidePath]; path; }); CGContextAddPath(context, path.CGPath); CGContextDrawPath(context, kCGPathEOFill); UIImage *newimage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newimage; }Copy the code
Shadow handling code snippet:
#import "kjeffectModel. h" NS_ASSUME_NONNULL_BEGIN /// Shadow type typedef NS_OPTIONS(NSInteger, KJEffectImageShadowType) {KJEffectImageShadowTypeOuter KJEffectImageShadowTypeInner, / / /, / / / KJEffectImageShadowTypeMeanwhile, / / / inside and outside has}; @interface KJShadowImageView : UIImageView /// initialize - (instancetype)kj_initWithView:(UIView*)view ExtendParameterBlock:(void(^_Nullable)(KJShadowImageView *obj))paramblock; - (void)kj_changeShadowInfo:(KJEffectShineModel*)info ShadowType:(KJEffectImageShadowType)type; # pragma mark - ExtendParameterBlock extension parameter @ property (nonatomic, strong, readonly) KJShadowImageView * (^ kAddView) (UIView *); @property(nonatomic,strong,readonly) KJShadowImageView *(^kFrame)(CGRect); / / / the district path @ property (nonatomic, strong, readonly) KJShadowImageView * (^ kOutsidePath) (UIBezierPath *); @end NS_ASSUME_NONNULL_ENDCopy the code