New file |
| | |
| | | // |
| | | // YYCGUtilities.h |
| | | // YYCategories <https://github.com/ibireme/YYCategories> |
| | | // |
| | | // Created by ibireme on 15/2/28. |
| | | // Copyright (c) 2015 ibireme. |
| | | // |
| | | // This source code is licensed under the MIT-style license found in the |
| | | // LICENSE file in the root directory of this source tree. |
| | | // |
| | | |
| | | #import <UIKit/UIKit.h> |
| | | #import <QuartzCore/QuartzCore.h> |
| | | |
| | | NS_ASSUME_NONNULL_BEGIN |
| | | |
| | | /// Create an `ARGB` Bitmap context. Returns NULL if an error occurs. |
| | | /// |
| | | /// @discussion The function is same as UIGraphicsBeginImageContextWithOptions(), |
| | | /// but it doesn't push the context to UIGraphic, so you can retain the context for reuse. |
| | | CGContextRef _Nullable YYCGContextCreateARGBBitmapContext(CGSize size, BOOL opaque, CGFloat scale); |
| | | |
| | | /// Create a `DeviceGray` Bitmap context. Returns NULL if an error occurs. |
| | | CGContextRef _Nullable YYCGContextCreateGrayBitmapContext(CGSize size, CGFloat scale); |
| | | |
| | | |
| | | |
| | | /// Get main screen's scale. |
| | | CGFloat YYScreenScale(); |
| | | |
| | | /// Get main screen's size. Height is always larger than width. |
| | | CGSize YYScreenSize(); |
| | | |
| | | |
| | | |
| | | /// Convert degrees to radians. |
| | | static inline CGFloat DegreesToRadians(CGFloat degrees) { |
| | | return degrees * M_PI / 180; |
| | | } |
| | | |
| | | /// Convert radians to degrees. |
| | | static inline CGFloat RadiansToDegrees(CGFloat radians) { |
| | | return radians * 180 / M_PI; |
| | | } |
| | | |
| | | |
| | | |
| | | /// Get the transform rotation. |
| | | /// @return the rotation in radians [-PI,PI] ([-180°,180°]) |
| | | static inline CGFloat CGAffineTransformGetRotation(CGAffineTransform transform) { |
| | | return atan2(transform.b, transform.a); |
| | | } |
| | | |
| | | /// Get the transform's scale.x |
| | | static inline CGFloat CGAffineTransformGetScaleX(CGAffineTransform transform) { |
| | | return sqrt(transform.a * transform.a + transform.c * transform.c); |
| | | } |
| | | |
| | | /// Get the transform's scale.y |
| | | static inline CGFloat CGAffineTransformGetScaleY(CGAffineTransform transform) { |
| | | return sqrt(transform.b * transform.b + transform.d * transform.d); |
| | | } |
| | | |
| | | /// Get the transform's translate.x |
| | | static inline CGFloat CGAffineTransformGetTranslateX(CGAffineTransform transform) { |
| | | return transform.tx; |
| | | } |
| | | |
| | | /// Get the transform's translate.y |
| | | static inline CGFloat CGAffineTransformGetTranslateY(CGAffineTransform transform) { |
| | | return transform.ty; |
| | | } |
| | | |
| | | /** |
| | | If you have 3 pair of points transformed by a same CGAffineTransform: |
| | | p1 (transform->) q1 |
| | | p2 (transform->) q2 |
| | | p3 (transform->) q3 |
| | | This method returns the original transform matrix from these 3 pair of points. |
| | | |
| | | @see http://stackoverflow.com/questions/13291796/calculate-values-for-a-cgaffinetransform-from-three-points-in-each-of-two-uiview |
| | | */ |
| | | CGAffineTransform YYCGAffineTransformGetFromPoints(CGPoint before[3], CGPoint after[3]); |
| | | |
| | | /// Get the transform which can converts a point from the coordinate system of a given view to another. |
| | | CGAffineTransform YYCGAffineTransformGetFromViews(UIView *from, UIView *to); |
| | | |
| | | /// Create a skew transform. |
| | | static inline CGAffineTransform CGAffineTransformMakeSkew(CGFloat x, CGFloat y){ |
| | | CGAffineTransform transform = CGAffineTransformIdentity; |
| | | transform.c = -x; |
| | | transform.b = y; |
| | | return transform; |
| | | } |
| | | |
| | | /// Negates/inverts a UIEdgeInsets. |
| | | static inline UIEdgeInsets UIEdgeInsetsInvert(UIEdgeInsets insets) { |
| | | return UIEdgeInsetsMake(-insets.top, -insets.left, -insets.bottom, -insets.right); |
| | | } |
| | | |
| | | /// Convert CALayer's gravity string to UIViewContentMode. |
| | | UIViewContentMode YYCAGravityToUIViewContentMode(NSString *gravity); |
| | | |
| | | /// Convert UIViewContentMode to CALayer's gravity string. |
| | | NSString *YYUIViewContentModeToCAGravity(UIViewContentMode contentMode); |
| | | |
| | | |
| | | |
| | | /** |
| | | Returns a rectangle to fit the @param rect with specified content mode. |
| | | |
| | | @param rect The constrant rect |
| | | @param size The content size |
| | | @param mode The content mode |
| | | @return A rectangle for the given content mode. |
| | | @discussion UIViewContentModeRedraw is same as UIViewContentModeScaleToFill. |
| | | */ |
| | | CGRect YYCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode); |
| | | |
| | | /// Returns the center for the rectangle. |
| | | static inline CGPoint CGRectGetCenter(CGRect rect) { |
| | | return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); |
| | | } |
| | | |
| | | /// Returns the area of the rectangle. |
| | | static inline CGFloat CGRectGetArea(CGRect rect) { |
| | | if (CGRectIsNull(rect)) return 0; |
| | | rect = CGRectStandardize(rect); |
| | | return rect.size.width * rect.size.height; |
| | | } |
| | | |
| | | /// Returns the distance between two points. |
| | | static inline CGFloat CGPointGetDistanceToPoint(CGPoint p1, CGPoint p2) { |
| | | return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); |
| | | } |
| | | |
| | | /// Returns the minmium distance between a point to a rectangle. |
| | | static inline CGFloat CGPointGetDistanceToRect(CGPoint p, CGRect r) { |
| | | r = CGRectStandardize(r); |
| | | if (CGRectContainsPoint(r, p)) return 0; |
| | | CGFloat distV, distH; |
| | | if (CGRectGetMinY(r) <= p.y && p.y <= CGRectGetMaxY(r)) { |
| | | distV = 0; |
| | | } else { |
| | | distV = p.y < CGRectGetMinY(r) ? CGRectGetMinY(r) - p.y : p.y - CGRectGetMaxY(r); |
| | | } |
| | | if (CGRectGetMinX(r) <= p.x && p.x <= CGRectGetMaxX(r)) { |
| | | distH = 0; |
| | | } else { |
| | | distH = p.x < CGRectGetMinX(r) ? CGRectGetMinX(r) - p.x : p.x - CGRectGetMaxX(r); |
| | | } |
| | | return MAX(distV, distH); |
| | | } |
| | | |
| | | |
| | | |
| | | /// Convert point to pixel. |
| | | static inline CGFloat CGFloatToPixel(CGFloat value) { |
| | | return value * YYScreenScale(); |
| | | } |
| | | |
| | | /// Convert pixel to point. |
| | | static inline CGFloat CGFloatFromPixel(CGFloat value) { |
| | | return value / YYScreenScale(); |
| | | } |
| | | |
| | | |
| | | |
| | | /// floor point value for pixel-aligned |
| | | static inline CGFloat CGFloatPixelFloor(CGFloat value) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return floor(value * scale) / scale; |
| | | } |
| | | |
| | | /// round point value for pixel-aligned |
| | | static inline CGFloat CGFloatPixelRound(CGFloat value) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return round(value * scale) / scale; |
| | | } |
| | | |
| | | /// ceil point value for pixel-aligned |
| | | static inline CGFloat CGFloatPixelCeil(CGFloat value) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return ceil(value * scale) / scale; |
| | | } |
| | | |
| | | /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) |
| | | static inline CGFloat CGFloatPixelHalf(CGFloat value) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return (floor(value * scale) + 0.5) / scale; |
| | | } |
| | | |
| | | |
| | | |
| | | /// floor point value for pixel-aligned |
| | | static inline CGPoint CGPointPixelFloor(CGPoint point) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return CGPointMake(floor(point.x * scale) / scale, |
| | | floor(point.y * scale) / scale); |
| | | } |
| | | |
| | | /// round point value for pixel-aligned |
| | | static inline CGPoint CGPointPixelRound(CGPoint point) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return CGPointMake(round(point.x * scale) / scale, |
| | | round(point.y * scale) / scale); |
| | | } |
| | | |
| | | /// ceil point value for pixel-aligned |
| | | static inline CGPoint CGPointPixelCeil(CGPoint point) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return CGPointMake(ceil(point.x * scale) / scale, |
| | | ceil(point.y * scale) / scale); |
| | | } |
| | | |
| | | /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) |
| | | static inline CGPoint CGPointPixelHalf(CGPoint point) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return CGPointMake((floor(point.x * scale) + 0.5) / scale, |
| | | (floor(point.y * scale) + 0.5) / scale); |
| | | } |
| | | |
| | | |
| | | |
| | | /// floor point value for pixel-aligned |
| | | static inline CGSize CGSizePixelFloor(CGSize size) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return CGSizeMake(floor(size.width * scale) / scale, |
| | | floor(size.height * scale) / scale); |
| | | } |
| | | |
| | | /// round point value for pixel-aligned |
| | | static inline CGSize CGSizePixelRound(CGSize size) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return CGSizeMake(round(size.width * scale) / scale, |
| | | round(size.height * scale) / scale); |
| | | } |
| | | |
| | | /// ceil point value for pixel-aligned |
| | | static inline CGSize CGSizePixelCeil(CGSize size) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return CGSizeMake(ceil(size.width * scale) / scale, |
| | | ceil(size.height * scale) / scale); |
| | | } |
| | | |
| | | /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) |
| | | static inline CGSize CGSizePixelHalf(CGSize size) { |
| | | CGFloat scale = YYScreenScale(); |
| | | return CGSizeMake((floor(size.width * scale) + 0.5) / scale, |
| | | (floor(size.height * scale) + 0.5) / scale); |
| | | } |
| | | |
| | | |
| | | |
| | | /// floor point value for pixel-aligned |
| | | static inline CGRect CGRectPixelFloor(CGRect rect) { |
| | | CGPoint origin = CGPointPixelCeil(rect.origin); |
| | | CGPoint corner = CGPointPixelFloor(CGPointMake(rect.origin.x + rect.size.width, |
| | | rect.origin.y + rect.size.height)); |
| | | CGRect ret = CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); |
| | | if (ret.size.width < 0) ret.size.width = 0; |
| | | if (ret.size.height < 0) ret.size.height = 0; |
| | | return ret; |
| | | } |
| | | |
| | | /// round point value for pixel-aligned |
| | | static inline CGRect CGRectPixelRound(CGRect rect) { |
| | | CGPoint origin = CGPointPixelRound(rect.origin); |
| | | CGPoint corner = CGPointPixelRound(CGPointMake(rect.origin.x + rect.size.width, |
| | | rect.origin.y + rect.size.height)); |
| | | return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); |
| | | } |
| | | |
| | | /// ceil point value for pixel-aligned |
| | | static inline CGRect CGRectPixelCeil(CGRect rect) { |
| | | CGPoint origin = CGPointPixelFloor(rect.origin); |
| | | CGPoint corner = CGPointPixelCeil(CGPointMake(rect.origin.x + rect.size.width, |
| | | rect.origin.y + rect.size.height)); |
| | | return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); |
| | | } |
| | | |
| | | /// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) |
| | | static inline CGRect CGRectPixelHalf(CGRect rect) { |
| | | CGPoint origin = CGPointPixelHalf(rect.origin); |
| | | CGPoint corner = CGPointPixelHalf(CGPointMake(rect.origin.x + rect.size.width, |
| | | rect.origin.y + rect.size.height)); |
| | | return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); |
| | | } |
| | | |
| | | |
| | | |
| | | /// floor UIEdgeInset for pixel-aligned |
| | | static inline UIEdgeInsets UIEdgeInsetPixelFloor(UIEdgeInsets insets) { |
| | | insets.top = CGFloatPixelFloor(insets.top); |
| | | insets.left = CGFloatPixelFloor(insets.left); |
| | | insets.bottom = CGFloatPixelFloor(insets.bottom); |
| | | insets.right = CGFloatPixelFloor(insets.right); |
| | | return insets; |
| | | } |
| | | |
| | | /// ceil UIEdgeInset for pixel-aligned |
| | | static inline UIEdgeInsets UIEdgeInsetPixelCeil(UIEdgeInsets insets) { |
| | | insets.top = CGFloatPixelCeil(insets.top); |
| | | insets.left = CGFloatPixelCeil(insets.left); |
| | | insets.bottom = CGFloatPixelCeil(insets.bottom); |
| | | insets.right = CGFloatPixelCeil(insets.right); |
| | | return insets; |
| | | } |
| | | |
| | | // main screen's scale |
| | | #ifndef kScreenScale |
| | | #define kScreenScale YYScreenScale() |
| | | #endif |
| | | |
| | | // main screen's size (portrait) |
| | | #ifndef kScreenSize |
| | | #define kScreenSize YYScreenSize() |
| | | #endif |
| | | |
| | | // main screen's width (portrait) |
| | | #ifndef kScreenWidth |
| | | #define kScreenWidth YYScreenSize().width |
| | | #endif |
| | | |
| | | // main screen's height (portrait) |
| | | #ifndef kScreenHeight |
| | | #define kScreenHeight YYScreenSize().height |
| | | #endif |
| | | |
| | | NS_ASSUME_NONNULL_END |