首次提交

This commit is contained in:
启星
2025-08-08 11:05:33 +08:00
parent 1b3bb91b4a
commit adc1a2a25d
8803 changed files with 708874 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
//
// CALayer+QQCorner.h
// QQCorner
//
// Created by 秦琦 on 2018/10/24.
// Copyright © 2018 QinQi. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
@class QQCorner;
@interface CALayer (QQCorner)
@property (nonatomic, strong) QQCorner *qq_corner;
/**
Add corner to a CALayer instance
给一个CALayer对象添加圆角
@param handler Deal the properities of corner, see QQCorner.
corner的属性看QQCorner的介绍
@warning If you pass nil or clearColor to both 'fillColor' and 'borderColor' params in corner, this method will do nothing.
如果在corner对象中fillColor 和 borderColor 都被设置为 nil 或者 clearColor这个方法什么都不会做。
*/
- (void)updateCornerRadius:(void(^)(QQCorner *corner))handler;
@end

View File

@@ -0,0 +1,109 @@
//
// CALayer+QQCorner.m
// QQCorner
//
// Created by on 2018/10/24.
// Copyright © 2018 QinQi. All rights reserved.
//
#import "CALayer+QQCorner.h"
#import "QQCornerModel.h"
#import <objc/runtime.h>
@implementation CALayer (QQCorner)
static const char *qq_layer_key = "qq_layer_key";
static const char *qq_corner_key = "qq_corner_key";
- (CAShapeLayer *)qq_layer {
CAShapeLayer *layer = objc_getAssociatedObject(self, &qq_layer_key);
if (!layer) {
layer = [CAShapeLayer layer];
layer.frame = self.bounds;
[self insertSublayer:layer atIndex:0];
self.qq_layer = layer;
}
return layer;
}
- (void)setQq_layer:(CAShapeLayer *)qq_layer {
objc_setAssociatedObject(self, &qq_layer_key, qq_layer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (QQCorner *)qq_corner {
QQCorner *corner = objc_getAssociatedObject(self, &qq_corner_key);
if (!corner) {
corner = [[QQCorner alloc] init];
self.qq_corner = corner;
}
return corner;
}
- (void)setQq_corner:(QQCorner *)qq_corner {
objc_setAssociatedObject(self, &qq_corner_key, qq_corner, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)updateCornerRadius:(void (^)(QQCorner *))handler {
if (handler) {
handler(self.qq_corner);
}
CGColorRef fill = self.qq_corner.fillColor.CGColor;
if (!fill || CGColorEqualToColor(fill, [UIColor clearColor].CGColor)) {
if (!self.backgroundColor || CGColorEqualToColor(self.backgroundColor, [UIColor clearColor].CGColor)) {
if (!self.qq_corner.borderColor || CGColorEqualToColor(self.qq_corner.borderColor.CGColor, [UIColor clearColor].CGColor)) {
return;
}
}
fill = self.backgroundColor;
self.backgroundColor = [UIColor clearColor].CGColor;
}
QQRadius radius = self.qq_corner.radius;
if (radius.upLeft < 0) {
radius.upLeft = 0;
}
if (radius.upRight < 0) {
radius.upRight = 0;
}
if (radius.downLeft < 0) {
radius.downLeft = 0;
}
if (radius.downRight < 0) {
radius.downRight = 0;
}
if (self.qq_corner.borderWidth < 0) {
self.qq_corner.borderWidth = 0;
}
CGRect qq_frame = self.bounds;
qq_frame.origin.x = self.qq_corner.borderWidth * 0.5;
qq_frame.origin.y = self.qq_corner.borderWidth * 0.5;
qq_frame.size.width -= self.qq_corner.borderWidth;
qq_frame.size.height -= self.qq_corner.borderWidth;
self.qq_layer.frame = qq_frame;
UIBezierPath *path = [UIBezierPath bezierPath];
CGFloat height = qq_frame.size.height;
CGFloat width = qq_frame.size.width;
//
[path moveToPoint:CGPointMake(radius.upLeft, 0)];
[path addQuadCurveToPoint:CGPointMake(0, radius.upLeft) controlPoint:CGPointZero];
//
[path addLineToPoint:CGPointMake(0, height - radius.downLeft)];
[path addQuadCurveToPoint:CGPointMake(radius.downLeft, height) controlPoint:CGPointMake(0, height)];
//
[path addLineToPoint:CGPointMake(width - radius.downRight, height)];
[path addQuadCurveToPoint:CGPointMake(width, height - radius.downRight) controlPoint:CGPointMake(width, height)];
//
[path addLineToPoint:CGPointMake(width, radius.upRight)];
[path addQuadCurveToPoint:CGPointMake(width - radius.upRight, 0) controlPoint:CGPointMake(width, 0)];
[path closePath];
[path addClip];
self.qq_layer.fillColor = fill;
self.qq_layer.strokeColor = self.qq_corner.borderColor.CGColor;
self.qq_layer.lineWidth = self.qq_corner.borderWidth;
self.qq_layer.path = path.CGPath;
}
@end

View File

@@ -0,0 +1,16 @@
//
// QQCorner.h
// QQCorner
//
// Created by 秦琦 on 2018/10/25.
// Copyright © 2018 QinQi. All rights reserved.
//
#ifndef QQCorner_h
#define QQCorner_h
#import "UIImage+QQCorner.h"
#import "UIView+QQCorner.h"
#import "CALayer+QQCorner.h"
#endif /* QQCorner_h */

View File

@@ -0,0 +1,72 @@
//
// QQCornerModel.h
// QQCorner
//
// Created by 秦琦 on 2018/10/24.
// Copyright © 2018 QinQi. All rights reserved.
//
#import <Foundation/Foundation.h>
struct QQRadius {
CGFloat upLeft; //The radius of upLeft. 左上半径
CGFloat upRight; //The radius of upRight. 右上半径
CGFloat downLeft; //The radius of downLeft. 左下半径
CGFloat downRight; //The radius of downRight. 右下半径
};
typedef struct QQRadius QQRadius;
static QQRadius const QQRadiusZero = (QQRadius){0, 0, 0, 0};
NS_INLINE bool QQRadiusIsEqual(QQRadius radius1, QQRadius radius2) {
return radius1.upLeft == radius2.upLeft && radius1.upRight == radius2.upRight && radius1.downLeft == radius2.downLeft && radius1.downRight == radius2.downRight;
}
NS_INLINE QQRadius QQRadiusMake(CGFloat upLeft, CGFloat upRight, CGFloat downLeft, CGFloat downRight) {
QQRadius radius;
radius.upLeft = upLeft;
radius.upRight = upRight;
radius.downLeft = downLeft;
radius.downRight = downRight;
return radius;
}
NS_INLINE QQRadius QQRadiusMakeSame(CGFloat radius) {
QQRadius result;
result.upLeft = radius;
result.upRight = radius;
result.downLeft = radius;
result.downRight = radius;
return result;
}
typedef NS_ENUM(NSUInteger, QQGradualChangeType) {
QQGradualChangeTypeUpLeftToDownRight = 0,
QQGradualChangeTypeUpToDown,
QQGradualChangeTypeLeftToRight,
QQGradualChangeTypeUpRightToDownLeft
};
@interface QQGradualChangingColor : NSObject
@property (nonatomic, strong) UIColor *fromColor;
@property (nonatomic, strong) UIColor *toColor;
@property (nonatomic, assign) QQGradualChangeType type;
@end
@interface QQCorner : NSObject
/**The radiuses of 4 corners. 4个圆角的半径*/
@property (nonatomic, assign) QQRadius radius;
/**The color that will fill the layer/view. 将要填充layer/view的颜色*/
@property (nonatomic, strong) UIColor *fillColor;
/**The color of the border. 边框颜色*/
@property (nonatomic, strong) UIColor *borderColor;
/**The lineWidth of the border. 边框宽度*/
@property (nonatomic, assign) CGFloat borderWidth;
@end

View File

@@ -0,0 +1,53 @@
//
// QQCornerModel.m
// QQCorner
//
// Created by on 2018/10/24.
// Copyright © 2018 QinQi. All rights reserved.
//
#import "QQCornerModel.h"
@implementation QQGradualChangingColor
- (instancetype)initWithColorFrom:(UIColor *)from to:(UIColor *)to type:(QQGradualChangeType)type {
if (self = [super init]) {
_fromColor = from;
_toColor = to;
_type = type;
}
return self;
}
+ (instancetype)gradualChangingColorFrom:(UIColor *)from to:(UIColor *)to {
return [[self alloc] initWithColorFrom:from to:to type:QQGradualChangeTypeUpLeftToDownRight];
}
+ (instancetype)gradualChangingColorFrom:(UIColor *)from to:(UIColor *)to type:(QQGradualChangeType)type {
return [[self alloc] initWithColorFrom:from to:to type:type];
}
@end
@implementation QQCorner
- (instancetype)initWithRadius:(QQRadius)radius fillColor:(UIColor *)fillColor borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth {
if (self = [super init]) {
_radius = radius;
_fillColor = fillColor;
_borderColor = borderColor;
_borderWidth = borderWidth;
}
return self;
}
+ (instancetype)cornerWithRadius:(QQRadius)radius fillColor:(UIColor *)fillColor {
return [[self alloc] initWithRadius:radius fillColor:fillColor borderColor:nil borderWidth:0];
}
+ (instancetype)cornerWithRadius:(QQRadius)radius fillColor:(UIColor *)fillColor borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth {
return [[self alloc] initWithRadius:radius fillColor:fillColor borderColor:borderColor borderWidth:borderWidth];
}
@end

View File

@@ -0,0 +1,72 @@
//
// UIImage+QQCorner.h
// QQCorner
//
// Created by 秦琦 on 2018/10/24.
// Copyright © 2018 QinQi. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "QQCornerModel.h"
@interface UIImage (QQCorner)
/**
Add corner to a UIImage instance.
给一个UIImage对象添加圆角
@param radius The radiuses of 4 corners.
4个圆角的半径
*/
- (UIImage *)imageByAddingCornerRadius:(QQRadius)radius;
/**
Create a UIImage by UIColor.
通过颜色创建图片,大小为 1 x 1
*/
+ (UIImage *)imageWithColor:(UIColor *)color;
/**
Create a UIImage with corner by UIColor.
通过颜色创建带圆角的图片
@param color The color of the image.
图片的颜色
@param size The size of the image.
图片的尺寸
@param radius The radiuses of 4 corners.
4个圆角的半径
*/
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size cornerRadius:(QQRadius)radius;
/**
Create a UIImage with the contents of a layer.
将layer的内容渲染为图片
@param layer Whose contents will be rendered in the image.
将要被渲染到图片的layer
@param radius The radiuses of 4 corners. If you pass QQRadiusZero, the final image will not add corner
4个圆角的半径如果传入QQRadiusZero最终的图片将不添加圆角
*/
+ (UIImage *)imageWithLayer:(CALayer *)layer cornerRadius:(QQRadius)radius;
/**
Create a UIImage with gradual changing color.
创建一个渐变色的图片
@param graColor gradual changing color properties.
渐变色的属性
@param size The size of the image.
图片的尺寸
@param radius The radiuses of 4 corners. If you pass QQRadiusZero, the final image will not add corner
4个圆角的半径如果传入QQRadiusZero最终的图片将不添加圆角
*/
+ (UIImage *)imageWithGradualChangingColor:(void(^)(QQGradualChangingColor *graColor))handler size:(CGSize)size cornerRadius:(QQRadius)radius;
/**
Create a UIImage with border and corner. Always uses in UIButton
创建一个边框图片可以带圆角。通常在UIButton使用
@param corner The properities of corner, see QQCorner.
corner的属性看QQCorner的介绍
@param size The size of the image.
图片的尺寸
*/
+ (UIImage *)imageWithQQCorner:(void(^)(QQCorner *corner))handler size:(CGSize)size;
@end

View File

@@ -0,0 +1,190 @@
//
// UIImage+QQCorner.m
// QQCorner
//
// Created by on 2018/10/24.
// Copyright © 2018 QinQi. All rights reserved.
//
#import "UIImage+QQCorner.h"
#import "QQCornerModel.h"
@implementation UIImage (QQCorner)
static UIBezierPath * qq_pathWithCornerRadius(QQRadius radius, CGSize size) {
CGFloat imgW = size.width;
CGFloat imgH = size.height;
UIBezierPath *path = [UIBezierPath bezierPath];
//
[path addArcWithCenter:CGPointMake(radius.downLeft, imgH - radius.downLeft) radius:radius.downLeft startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
//
[path addArcWithCenter:CGPointMake(radius.upLeft, radius.upLeft) radius:radius.upLeft startAngle:M_PI endAngle:M_PI_2 * 3 clockwise:YES];
//
[path addArcWithCenter:CGPointMake(imgW - radius.upRight, radius.upRight) radius:radius.upRight startAngle:M_PI_2 * 3 endAngle:0 clockwise:YES];
//
[path addArcWithCenter:CGPointMake(imgW - radius.downRight, imgH - radius.downRight) radius:radius.downRight startAngle:0 endAngle:M_PI_2 clockwise:YES];
[path closePath];
return path;
}
+ (UIImage *)imageWithGradualChangingColor:(void (^)(QQGradualChangingColor *))handler size:(CGSize)size cornerRadius:(QQRadius)radius {
QQGradualChangingColor *graColor = [[QQGradualChangingColor alloc] init];
if (handler) {
handler(graColor);
}
CAGradientLayer *graLayer = [CAGradientLayer layer];
graLayer.frame = (CGRect){CGPointZero, size};
CGFloat startX = 0, startY = 0, endX = 0, endY = 0;
switch (graColor.type) {
case QQGradualChangeTypeUpLeftToDownRight: {
startX = 0;
startY = 0;
endX = 1;
endY = 1;
}
break;
case QQGradualChangeTypeUpToDown: {
startX = 0;
startY = 0;
endX = 0;
endY = 1;
}
break;
case QQGradualChangeTypeLeftToRight: {
startX = 0;
startY = 0;
endX = 1;
endY = 0;
}
break;
case QQGradualChangeTypeUpRightToDownLeft: {
startX = 0;
startY = 1;
endX = 1;
endY = 0;
}
break;
}
graLayer.startPoint = CGPointMake(startX, startY);
graLayer.endPoint = CGPointMake(endX, endY);
graLayer.colors = @[(__bridge id)graColor.fromColor.CGColor, (__bridge id)graColor.toColor.CGColor];
graLayer.locations = @[@0.0, @1.0];
return [self imageWithLayer:graLayer cornerRadius:radius];
}
+ (UIImage *)imageWithQQCorner:(void (^)(QQCorner *))handler size:(CGSize)size {
QQCorner *corner = [[QQCorner alloc] init];
if (handler) {
handler(corner);
}
if (!corner.fillColor) {
corner.fillColor = [UIColor clearColor];
}
UIBezierPath *path = qq_pathWithCornerRadius(corner.radius, size);
if (@available(iOS 10.0, *)) {
UIGraphicsImageRenderer *render = [[UIGraphicsImageRenderer alloc] initWithSize:size];
return [render imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
CGContextSetStrokeColorWithColor(rendererContext.CGContext, corner.borderColor.CGColor);
CGContextSetFillColorWithColor(rendererContext.CGContext, corner.fillColor.CGColor);
CGContextSetLineWidth(rendererContext.CGContext, corner.borderWidth);
[path addClip];
CGContextAddPath(rendererContext.CGContext, path.CGPath);
CGContextDrawPath(rendererContext.CGContext, kCGPathFillStroke);
}];
} else {
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, corner.borderColor.CGColor);
CGContextSetFillColorWithColor(context, corner.fillColor.CGColor);
CGContextSetLineWidth(context, corner.borderWidth);
CGContextAddPath(context, path.CGPath);
[path addClip];
CGContextDrawPath(context, kCGPathFillStroke);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
}
- (UIImage *)imageByAddingCornerRadius:(QQRadius)radius {
UIBezierPath *path = qq_pathWithCornerRadius(radius, self.size);
if (@available(iOS 10.0, *)) {
UIGraphicsImageRenderer *render = [[UIGraphicsImageRenderer alloc] initWithSize:self.size];
return [render imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
[path addClip];
CGContextAddPath(rendererContext.CGContext, path.CGPath);
[self drawInRect:(CGRect){CGPointZero, self.size}];
}];
} else {
UIGraphicsBeginImageContext(self.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[path addClip];
CGContextAddPath(context, path.CGPath);
[self drawInRect:(CGRect){CGPointZero, self.size}];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
}
+ (UIImage *)imageWithColor:(UIColor *)color {
return [self imageWithColor:color size:CGSizeMake(1, 1) cornerRadius:QQRadiusZero];
}
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size cornerRadius:(QQRadius)radius {
if (@available(iOS 10.0, *)) {
UIGraphicsImageRenderer *render = [[UIGraphicsImageRenderer alloc] initWithSize:size];
return [render imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
if (!QQRadiusIsEqual(radius, QQRadiusZero)) {
UIBezierPath *path = qq_pathWithCornerRadius(radius, size);
[path addClip];
CGContextAddPath(rendererContext.CGContext, path.CGPath);
}
CGContextSetFillColorWithColor(rendererContext.CGContext, color.CGColor);
CGContextFillRect(rendererContext.CGContext, (CGRect){CGPointZero, size});
}];
} else {
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
if (!QQRadiusIsEqual(radius, QQRadiusZero)) {
UIBezierPath *path = qq_pathWithCornerRadius(radius, size);
[path addClip];
CGContextAddPath(context, path.CGPath);
}
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, (CGRect){CGPointZero, size});
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
}
+ (UIImage *)imageWithLayer:(CALayer *)layer cornerRadius:(QQRadius)radius {
if (@available(iOS 10.0, *)) {
UIGraphicsImageRenderer *render = [[UIGraphicsImageRenderer alloc] initWithSize:layer.bounds.size];
return [render imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
if (!QQRadiusIsEqual(radius, QQRadiusZero)) {
UIBezierPath *path = qq_pathWithCornerRadius(radius, layer.bounds.size);
[path addClip];
CGContextAddPath(rendererContext.CGContext, path.CGPath);
}
[layer renderInContext:rendererContext.CGContext];
}];
} else {
UIGraphicsBeginImageContext(layer.bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
if (!QQRadiusIsEqual(radius, QQRadiusZero)) {
UIBezierPath *path = qq_pathWithCornerRadius(radius, layer.bounds.size);
[path addClip];
CGContextAddPath(context, path.CGPath);
}
[layer renderInContext:context];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
}
@end

View File

@@ -0,0 +1,24 @@
//
// UIView+QQCorner.h
// QQCorner
//
// Created by 秦琦 on 2018/10/24.
// Copyright © 2018 QinQi. All rights reserved.
//
#import <UIKit/UIKit.h>
@class QQCorner;
@interface UIView (QQCorner)
/**
Add corner to a UIView instance
给一个UIView对象添加圆角
@param handler Deal the properities of corner, see QQCorner.
corner的属性看QQCorner的介绍
@warning If you pass nil or clearColor to both 'fillColor' and 'borderColor' params in corner, this method will do nothing.
如果在corner对象中fillColor 和 borderColor 都被设置为 nil 或者 clearColor这个方法什么都不会做。
*/
- (void)updateCornerRadius:(void(^)(QQCorner *corner))handler;
@end

View File

@@ -0,0 +1,31 @@
//
// UIView+QQCorner.m
// QQCorner
//
// Created by on 2018/10/24.
// Copyright © 2018 QinQi. All rights reserved.
//
#import "UIView+QQCorner.h"
#import "CALayer+QQCorner.h"
#import "QQCornerModel.h"
@implementation UIView (QQCorner)
- (void)updateCornerRadius:(void (^)(QQCorner *))handler {
if (handler) {
handler(self.layer.qq_corner);
}
if (!self.layer.qq_corner.fillColor || CGColorEqualToColor(self.layer.qq_corner.fillColor.CGColor, [UIColor clearColor].CGColor)) {
if (CGColorEqualToColor(self.backgroundColor.CGColor, [UIColor clearColor].CGColor)) {
if (!self.layer.qq_corner.borderColor || CGColorEqualToColor(self.layer.qq_corner.borderColor.CGColor, [UIColor clearColor].CGColor)) {
return;
}
}
self.layer.qq_corner.fillColor = self.backgroundColor;
self.backgroundColor = [UIColor clearColor];
}
[self.layer updateCornerRadius:handler];
}
@end