From 10af5bfd86b41b6390650e46b2e4a84ebf3272be Mon Sep 17 00:00:00 2001 From: Pierre-Yves Bertholon Date: Tue, 17 Dec 2019 15:18:49 +0100 Subject: [PATCH 1/8] Bump version number --- Common.xcconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common.xcconfig b/Common.xcconfig index 54d715d..a2e6205 100644 --- a/Common.xcconfig +++ b/Common.xcconfig @@ -1,5 +1,5 @@ // Version information -MARKETING_VERSION = 2.0.0 +MARKETING_VERSION = 2.0.1 // Deployment targets IPHONEOS_DEPLOYMENT_TARGET = 9.0 From 7bd5c3d6c70262cd85c9c2fceadc02d6463bc705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 19 Mar 2020 09:03:18 +0100 Subject: [PATCH 2/8] Add optional color filling to vector image generation --- Framework/Sources/UIImage+SRGAppearance.h | 44 +++++++++++++++-------- Framework/Sources/UIImage+SRGAppearance.m | 28 +++++++++++---- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Framework/Sources/UIImage+SRGAppearance.h b/Framework/Sources/UIImage+SRGAppearance.h index 92837b1..d3e152a 100644 --- a/Framework/Sources/UIImage+SRGAppearance.h +++ b/Framework/Sources/UIImage+SRGAppearance.h @@ -11,25 +11,39 @@ NS_ASSUME_NONNULL_BEGIN @interface UIImage (SRGAppearance) /** - * Return an image generated from the vector image at the specified path. - * - * @param filePath The path of the vector image to use. - * @param size The size of the image to create. - * - * @return The generated image, `nil` if generation failed. +* Return an image generated from the vector image at the specified path. The image is fitted to the specified size +* and remaining space is filled with the provided color. +* +* @param filePath The path of the vector image to use. +* @param size The size of the image to create. +* @param fillColor The color to use for filling, transparent if `nil`. +* +* @return The generated image, `nil` if generation failed. +*/ ++ (nullable UIImage *)srg_vectorImageAtPath:(NSString *)filePath withSize:(CGSize)size fillColor:(nullable UIColor *)fillColor; + +/** + * Same as `-srg_vectorImageAtPath:withSize:fillColor:` without fill color. */ + (nullable UIImage *)srg_vectorImageAtPath:(NSString *)filePath withSize:(CGSize)size; /** - * Return the file URL of an image generated from the vector image at the specified path. - * - * @param filePath The path of the vector image to use. - * @param size The size of the image to create. - * - * @return The generated image, `nil` if generation failed. - * - * @discussion Images are stored in the `/Library/Caches` directory. - */ +* Return the file URL of an image generated from the vector image at the specified path. The image is fitted to the +* specified size and remaining space is filled with the provided color. +* +* @param filePath The path of the vector image to use. +* @param size The size of the image to create. +* @param fillColor The color to use for filling, transparent if `nil`. +* +* @return The generated image, `nil` if generation failed. +* +* @discussion Images are stored in the `/Library/Caches` directory. +*/ ++ (nullable NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size fillColor:(nullable UIColor *)fillColor; + +/** +* Same as `-srg_URLForVectorImageAtPath:withSize:fillColor:` without fill color. +*/ + (nullable NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size; /** diff --git a/Framework/Sources/UIImage+SRGAppearance.m b/Framework/Sources/UIImage+SRGAppearance.m index 28f8e49..14b35c9 100644 --- a/Framework/Sources/UIImage+SRGAppearance.m +++ b/Framework/Sources/UIImage+SRGAppearance.m @@ -6,6 +6,8 @@ #import "UIImage+SRGAppearance.h" +// Implementation borrowed from https://github.com/erica/useful-things + static CGFloat SRGAppearanceImageAspectScaleFit(CGSize sourceSize, CGRect destRect) { CGSize destSize = destRect.size; @@ -27,7 +29,7 @@ static CGRect SRGAppearanceImageRectByFittingRect(CGRect sourceRect, CGRect dest return SRGAppearanceImageRectAroundCenter(center, targetSize); } -static void SRGAppearanceImageDrawPDFPageInRect(CGPDFPageRef pageRef, CGRect rect) +static void SRGAppearanceImageDrawPDFPageInRect(CGPDFPageRef pageRef, CGRect rect, CGColorRef fillColor) { CGContextRef context = UIGraphicsGetCurrentContext(); @@ -43,6 +45,11 @@ static void SRGAppearanceImageDrawPDFPageInRect(CGPDFPageRef pageRef, CGRect rec // Flip the rect, which remains in UIKit space CGRect d = CGRectApplyAffineTransform(rect, transform); + if (fillColor) { + CGContextSetFillColorWithColor(context, fillColor); + CGContextFillRect(context, d); + } + // Calculate a rectangle to draw to CGRect pageRect = CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox); CGFloat drawingAspect = SRGAppearanceImageAspectScaleFit(pageRect.size, d); @@ -70,14 +77,18 @@ static void SRGAppearanceImageDrawPDFPageInRect(CGPDFPageRef pageRef, CGRect rec @implementation UIImage (SRGAppearance) -// Implementation borrowed from https://github.com/erica/useful-things -+ (UIImage *)srg_vectorImageAtPath:(NSString *)filePath withSize:(CGSize)size ++ (UIImage *)srg_vectorImageAtPath:(NSString *)filePath withSize:(CGSize)size fillColor:(UIColor *)fillColor { - NSURL *fileURL = [self srg_URLForVectorImageAtPath:filePath withSize:size]; + NSURL *fileURL = [self srg_URLForVectorImageAtPath:filePath withSize:size fillColor:fillColor]; return fileURL ? [UIImage imageWithContentsOfFile:fileURL.path] : nil; } -+ (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size ++ (UIImage *)srg_vectorImageAtPath:(NSString *)filePath withSize:(CGSize)size +{ + return [self srg_vectorImageAtPath:filePath withSize:size fillColor:nil]; +} + ++ (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size fillColor:(UIColor *)fillColor { // Check cached image existence at the very beginning, and return it if available NSString *cachedFileName = [NSString stringWithFormat:@"%@_%@_%@.png", @(filePath.hash), @(size.width), @(size.height)]; @@ -102,7 +113,7 @@ + (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)siz UIGraphicsBeginImageContextWithOptions(size, NO, 1. /* Force scale to 1 (0 would use device scale) */); CGPDFPageRef pageRef = CGPDFDocumentGetPage(pdfDocumentRef, 1); - SRGAppearanceImageDrawPDFPageInRect(pageRef, CGRectMake(0.f, 0.f, size.width, size.height)); + SRGAppearanceImageDrawPDFPageInRect(pageRef, CGRectMake(0.f, 0.f, size.width, size.height), fillColor.CGColor); UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); @@ -120,6 +131,11 @@ + (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)siz return [NSURL fileURLWithPath:cachedFilePath]; } ++ (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size +{ + return [self srg_URLForVectorImageAtPath:filePath withSize:size fillColor:nil]; +} + + (void)srg_clearVectorImageCache { NSString *cachesDirectory = SRGAppearanceVectorImageCachesDirectory(); From 6e5dc51f3f72730e5f012a2e6649b301c6d47a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 19 Mar 2020 10:22:03 +0100 Subject: [PATCH 3/8] Add color property to retrieve hexadecimal representation --- Framework/Sources/UIColor+SRGAppearance.h | 5 +++++ Framework/Sources/UIColor+SRGAppearance.m | 9 +++++++++ Tests/Sources/ColorTestCase.m | 8 ++++++++ 3 files changed, 22 insertions(+) diff --git a/Framework/Sources/UIColor+SRGAppearance.h b/Framework/Sources/UIColor+SRGAppearance.h index cbbf2d6..df26751 100644 --- a/Framework/Sources/UIColor+SRGAppearance.h +++ b/Framework/Sources/UIColor+SRGAppearance.h @@ -32,6 +32,11 @@ OBJC_EXPORT NSValueTransformer *SRGHexadecimalColorTransformer(void); */ + (nullable UIColor *)srg_colorFromHexadecimalString:(NSString *)hexadecimalString; +/** + * Return the color as a hexadecimal value. + */ +@property (nonatomic, readonly, copy) NSString *srg_hexadecimalString; + @end NS_ASSUME_NONNULL_END diff --git a/Framework/Sources/UIColor+SRGAppearance.m b/Framework/Sources/UIColor+SRGAppearance.m index e80bf8c..805cc89 100644 --- a/Framework/Sources/UIColor+SRGAppearance.m +++ b/Framework/Sources/UIColor+SRGAppearance.m @@ -12,6 +12,8 @@ @interface SRGHexadecimalColorValueTransformer : NSValueTransformer @implementation UIColor (SRGAppearance) +#pragma mark Class methods + + (UIColor *)srg_redColor { return [UIColor srg_colorFromHexadecimalString:@"#9d0018"]; @@ -27,6 +29,13 @@ + (UIColor *)srg_colorFromHexadecimalString:(NSString *)hexadecimalString return [SRGHexadecimalColorTransformer() transformedValue:hexadecimalString]; } +#pragma mark Getters and setters + +- (NSString *)srg_hexadecimalString +{ + return [SRGHexadecimalColorTransformer() reverseTransformedValue:self]; +} + @end @implementation SRGHexadecimalColorValueTransformer diff --git a/Tests/Sources/ColorTestCase.m b/Tests/Sources/ColorTestCase.m index 37b691d..8979c3b 100644 --- a/Tests/Sources/ColorTestCase.m +++ b/Tests/Sources/ColorTestCase.m @@ -19,11 +19,19 @@ - (void)testColorFromHexadecimalString XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"#00ff00"], UIColor.greenColor); XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"#0000ff"], UIColor.blueColor); + XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"FF0000"], UIColor.redColor); XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"ff0000"], UIColor.redColor); XCTAssertNil([UIColor srg_colorFromHexadecimalString:@"#zzzzzz"]); } +- (void)testHexadecimalString +{ + XCTAssertEqualObjects(UIColor.redColor.srg_hexadecimalString, @"#ff0000"); + XCTAssertEqualObjects(UIColor.greenColor.srg_hexadecimalString, @"#00ff00"); + XCTAssertEqualObjects(UIColor.blueColor.srg_hexadecimalString, @"#0000ff"); +} + - (void)testHexadecimalColorTransformer { XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#ff0000"], UIColor.redColor); From c75240af6176b85d68f1f65e42aca3a174f56adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 19 Mar 2020 10:44:07 +0100 Subject: [PATCH 4/8] Add support for alpha channel in color hexadecimal strings --- Framework/Sources/UIColor+SRGAppearance.h | 6 ++-- Framework/Sources/UIColor+SRGAppearance.m | 37 ++++++++++++++++++----- Tests/Sources/ColorTestCase.m | 29 ++++++++++++++++++ 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/Framework/Sources/UIColor+SRGAppearance.h b/Framework/Sources/UIColor+SRGAppearance.h index df26751..229be0c 100644 --- a/Framework/Sources/UIColor+SRGAppearance.h +++ b/Framework/Sources/UIColor+SRGAppearance.h @@ -27,13 +27,13 @@ OBJC_EXPORT NSValueTransformer *SRGHexadecimalColorTransformer(void); @property (class, nonatomic, readonly) UIColor *srg_blueColor; /** - * Return the color matching a hexadecimal string (with or without leading wildcard), `nil` if the string does - * not correspond to a color. + * Return the color matching a hexadecimal #rrggbbaa or #rrggbb string (the leading wildcard is optional), or `nil` if + * the string does not correspond to a color. */ + (nullable UIColor *)srg_colorFromHexadecimalString:(NSString *)hexadecimalString; /** - * Return the color as a hexadecimal value. + * Return the color as a hexadecimal value #rrggbbaa, or simply #rrggbb if the alpha channel is 1. */ @property (nonatomic, readonly, copy) NSString *srg_hexadecimalString; diff --git a/Framework/Sources/UIColor+SRGAppearance.m b/Framework/Sources/UIColor+SRGAppearance.m index 805cc89..f4b0aa2 100644 --- a/Framework/Sources/UIColor+SRGAppearance.m +++ b/Framework/Sources/UIColor+SRGAppearance.m @@ -58,20 +58,30 @@ - (id)transformedValue:(id)value return nil; } - NSScanner *scanner = [NSScanner scannerWithString:value]; - if ([value hasPrefix:@"#"]) { - scanner.scanLocation = 1; + NSString *string = [value hasPrefix:@"#"] ? [value substringFromIndex:1] : value; + if (string.length != 6 && string.length != 8) { + return nil; } unsigned rgbValue = 0; + NSScanner *scanner = [NSScanner scannerWithString:string]; if (! [scanner scanHexInt:&rgbValue]) { return nil; } - CGFloat red = ((rgbValue & 0xFF0000) >> 16) / 255.f; - CGFloat green = ((rgbValue & 0x00FF00) >> 8) / 255.f; - CGFloat blue = (rgbValue & 0x0000FF) / 255.f; - return [UIColor colorWithRed:red green:green blue:blue alpha:1.f]; + if (string.length == 8) { + CGFloat red = ((rgbValue & 0xFF000000) >> 24) / 255.f; + CGFloat green = ((rgbValue & 0x00FF0000) >> 16) / 255.f; + CGFloat blue = ((rgbValue & 0x0000FF00) >> 8) / 255.f; + CGFloat alpha = (rgbValue & 0x000000FF) / 255.f; + return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; + } + else { + CGFloat red = ((rgbValue & 0xFF0000) >> 16) / 255.f; + CGFloat green = ((rgbValue & 0x00FF00) >> 8) / 255.f; + CGFloat blue = (rgbValue & 0x0000FF) / 255.f; + return [UIColor colorWithRed:red green:green blue:blue alpha:1.f]; + } } - (id)reverseTransformedValue:(id)value @@ -81,7 +91,18 @@ - (id)reverseTransformedValue:(id)value } const CGFloat *components = CGColorGetComponents([value CGColor]); - return [NSString stringWithFormat:@"#%02lx%02lx%02lx", lroundf(components[0] * 255), lroundf(components[1] * 255), lroundf(components[2] * 255)]; + + long red = lroundf(components[0] * 255); + long green = lroundf(components[1] * 255); + long blue = lroundf(components[2] * 255); + long alpha = lroundf(components[3] * 255); + + if (alpha == 255) { + return [NSString stringWithFormat:@"#%02lx%02lx%02lx", red, green, blue]; + } + else { + return [NSString stringWithFormat:@"#%02lx%02lx%02lx%02lx", red, green, blue, alpha]; + } } @end diff --git a/Tests/Sources/ColorTestCase.m b/Tests/Sources/ColorTestCase.m index 8979c3b..4a84f33 100644 --- a/Tests/Sources/ColorTestCase.m +++ b/Tests/Sources/ColorTestCase.m @@ -19,6 +19,10 @@ - (void)testColorFromHexadecimalString XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"#00ff00"], UIColor.greenColor); XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"#0000ff"], UIColor.blueColor); + XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"#ff000080"], [UIColor.redColor colorWithAlphaComponent:128.f / 255.f]); + XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"#ff000000"], [UIColor.redColor colorWithAlphaComponent:0.f]); + XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"#ff0000ff"], [UIColor.redColor colorWithAlphaComponent:1.f]); + XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"FF0000"], UIColor.redColor); XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"ff0000"], UIColor.redColor); @@ -30,6 +34,11 @@ - (void)testHexadecimalString XCTAssertEqualObjects(UIColor.redColor.srg_hexadecimalString, @"#ff0000"); XCTAssertEqualObjects(UIColor.greenColor.srg_hexadecimalString, @"#00ff00"); XCTAssertEqualObjects(UIColor.blueColor.srg_hexadecimalString, @"#0000ff"); + + XCTAssertEqualObjects([UIColor.redColor colorWithAlphaComponent:128.f / 255.f].srg_hexadecimalString, @"#ff000080"); + XCTAssertEqualObjects([UIColor.redColor colorWithAlphaComponent:0.5f].srg_hexadecimalString, @"#ff000080"); + + XCTAssertEqualObjects([UIColor srg_colorFromHexadecimalString:@"FF0000FF"].srg_hexadecimalString, @"#ff0000"); } - (void)testHexadecimalColorTransformer @@ -38,11 +47,31 @@ - (void)testHexadecimalColorTransformer XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#00ff00"], UIColor.greenColor); XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#0000ff"], UIColor.blueColor); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#ff0000ff"], UIColor.redColor); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#00ff00ff"], UIColor.greenColor); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#0000ffff"], UIColor.blueColor); + + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#ff000080"], [UIColor.redColor colorWithAlphaComponent:128.f / 255.f]); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#00ff0080"], [UIColor.greenColor colorWithAlphaComponent:128.f / 255.f]); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#0000ff80"], [UIColor.blueColor colorWithAlphaComponent:128.f / 255.f]); + + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#ff000000"], [UIColor.redColor colorWithAlphaComponent:0.f]); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#00ff0000"], [UIColor.greenColor colorWithAlphaComponent:0.f]); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"#0000ff00"], [UIColor.blueColor colorWithAlphaComponent:0.f]); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() transformedValue:@"ff0000"], UIColor.redColor); XCTAssertEqualObjects([SRGHexadecimalColorTransformer() reverseTransformedValue:UIColor.redColor], @"#ff0000"); XCTAssertEqualObjects([SRGHexadecimalColorTransformer() reverseTransformedValue:UIColor.greenColor], @"#00ff00"); XCTAssertEqualObjects([SRGHexadecimalColorTransformer() reverseTransformedValue:UIColor.blueColor], @"#0000ff"); + + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() reverseTransformedValue:[UIColor.redColor colorWithAlphaComponent:128.f / 255.f]], @"#ff000080"); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() reverseTransformedValue:[UIColor.greenColor colorWithAlphaComponent:128.f / 255.f]], @"#00ff0080"); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() reverseTransformedValue:[UIColor.blueColor colorWithAlphaComponent:128.f / 255.f]], @"#0000ff80"); + + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() reverseTransformedValue:[UIColor.redColor colorWithAlphaComponent:0.f]], @"#ff000000"); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() reverseTransformedValue:[UIColor.greenColor colorWithAlphaComponent:0.f]], @"#00ff0000"); + XCTAssertEqualObjects([SRGHexadecimalColorTransformer() reverseTransformedValue:[UIColor.blueColor colorWithAlphaComponent:0.f]], @"#0000ff00"); } @end From bcd8195eaae68f20aa06ac5eaf7d63dd8997ff94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 19 Mar 2020 11:36:20 +0100 Subject: [PATCH 5/8] Cache different images for different colors --- Framework/Sources/UIImage+SRGAppearance.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Framework/Sources/UIImage+SRGAppearance.m b/Framework/Sources/UIImage+SRGAppearance.m index 14b35c9..e8fb7e6 100644 --- a/Framework/Sources/UIImage+SRGAppearance.m +++ b/Framework/Sources/UIImage+SRGAppearance.m @@ -6,6 +6,8 @@ #import "UIImage+SRGAppearance.h" +#import "UIColor+SRGAppearance.h" + // Implementation borrowed from https://github.com/erica/useful-things static CGFloat SRGAppearanceImageAspectScaleFit(CGSize sourceSize, CGRect destRect) @@ -91,7 +93,7 @@ + (UIImage *)srg_vectorImageAtPath:(NSString *)filePath withSize:(CGSize)size + (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size fillColor:(UIColor *)fillColor { // Check cached image existence at the very beginning, and return it if available - NSString *cachedFileName = [NSString stringWithFormat:@"%@_%@_%@.png", @(filePath.hash), @(size.width), @(size.height)]; + NSString *cachedFileName = [NSString stringWithFormat:@"%@_%@_%@_%@.png", @(filePath.hash), @(size.width), @(size.height), fillColor.srg_hexadecimalString ?: @"none"]; NSString *cachesDirectory = SRGAppearanceVectorImageCachesDirectory(); NSString *cachedFilePath = [cachesDirectory stringByAppendingPathComponent:cachedFileName]; if ([[NSFileManager defaultManager] fileExistsAtPath:cachedFilePath]) { From f444d4d94d77bc73f1b1e0a13849ea462a9ff1e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 19 Mar 2020 11:41:53 +0100 Subject: [PATCH 6/8] Improve documentation --- Framework/Sources/UIColor+SRGAppearance.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Framework/Sources/UIColor+SRGAppearance.h b/Framework/Sources/UIColor+SRGAppearance.h index 229be0c..66d4651 100644 --- a/Framework/Sources/UIColor+SRGAppearance.h +++ b/Framework/Sources/UIColor+SRGAppearance.h @@ -27,13 +27,17 @@ OBJC_EXPORT NSValueTransformer *SRGHexadecimalColorTransformer(void); @property (class, nonatomic, readonly) UIColor *srg_blueColor; /** - * Return the color matching a hexadecimal #rrggbbaa or #rrggbb string (the leading wildcard is optional), or `nil` if - * the string does not correspond to a color. + * Return the color matching a hexadecimal #rrggbbaa or #rrggbb string representation (the leading wildcard is optional), + * or `nil` if the string does not correspond to a color. + * + * @discussion Supports uppercase or lowercase digits. */ + (nullable UIColor *)srg_colorFromHexadecimalString:(NSString *)hexadecimalString; /** - * Return the color as a hexadecimal value #rrggbbaa, or simply #rrggbb if the alpha channel is 1. + * Return the color as a hexadecimal #rrggbbaa (#rrggbb if the alpha channel is 1) string representation. + * + * @discussion Always return lowercase digits with a leading wildcard. */ @property (nonatomic, readonly, copy) NSString *srg_hexadecimalString; From 6c773c1473e43eda291e6652151d7be5490acaac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 19 Mar 2020 18:07:53 +0100 Subject: [PATCH 7/8] Add vector image generation methods using asset intrinsic size --- Framework/Sources/UIImage+SRGAppearance.h | 33 ++++++++++++++-- Framework/Sources/UIImage+SRGAppearance.m | 46 ++++++++++++++++++++++- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/Framework/Sources/UIImage+SRGAppearance.h b/Framework/Sources/UIImage+SRGAppearance.h index d3e152a..7eb6158 100644 --- a/Framework/Sources/UIImage+SRGAppearance.h +++ b/Framework/Sources/UIImage+SRGAppearance.h @@ -15,7 +15,8 @@ NS_ASSUME_NONNULL_BEGIN * and remaining space is filled with the provided color. * * @param filePath The path of the vector image to use. -* @param size The size of the image to create. +* @param size The size of the image to create. Components set to 0 are calculated automatically based on the +* resource aspect ratio (or its intrinsic size if both are 0). * @param fillColor The color to use for filling, transparent if `nil`. * * @return The generated image, `nil` if generation failed. @@ -27,12 +28,24 @@ NS_ASSUME_NONNULL_BEGIN */ + (nullable UIImage *)srg_vectorImageAtPath:(NSString *)filePath withSize:(CGSize)size; +/** + * Same as `-srg_vectorImageAtPath:withSize:fillColor:`, calculating the other dimension based on the image aspect ratio. + */ ++ (nullable UIImage *)srg_vectorImageAtPath:(NSString *)filePath withWidth:(CGFloat)width; ++ (nullable UIImage *)srg_vectorImageAtPath:(NSString *)filePath withHeight:(CGFloat)height; + +/** + * Same as `-srg_vectorImageAtPath:withSize:fillColor:`, returning the image with its intrinsic size. + */ ++ (nullable UIImage *)srg_vectorImageAtPath:(NSString *)filePath; + /** * Return the file URL of an image generated from the vector image at the specified path. The image is fitted to the * specified size and remaining space is filled with the provided color. * * @param filePath The path of the vector image to use. -* @param size The size of the image to create. +* @param size The size of the image to create. Components set to 0 are calculated automatically based on the +* resource aspect ratio (or its intrinsic size if both are 0). * @param fillColor The color to use for filling, transparent if `nil`. * * @return The generated image, `nil` if generation failed. @@ -42,10 +55,22 @@ NS_ASSUME_NONNULL_BEGIN + (nullable NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size fillColor:(nullable UIColor *)fillColor; /** -* Same as `-srg_URLForVectorImageAtPath:withSize:fillColor:` without fill color. -*/ + * Same as `-srg_URLForVectorImageAtPath:withSize:fillColor:` without fill color. + */ + (nullable NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size; +/** + * Same as `-srg_URLForVectorImageAtPath:withSize:fillColor:`, calculating the other dimension based on the image + * aspect ratio. + */ ++ (nullable NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withWidth:(CGFloat)width; ++ (nullable NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withHeight:(CGFloat)height; + +/** + * Same as `-srg_URLForVectorImageAtPath:withSize:fillColor:`, returning the image with its intrinsic size. + */ ++ (nullable NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath; + /** * Clears the vector image cache. */ diff --git a/Framework/Sources/UIImage+SRGAppearance.m b/Framework/Sources/UIImage+SRGAppearance.m index e8fb7e6..99eb65a 100644 --- a/Framework/Sources/UIImage+SRGAppearance.m +++ b/Framework/Sources/UIImage+SRGAppearance.m @@ -52,7 +52,7 @@ static void SRGAppearanceImageDrawPDFPageInRect(CGPDFPageRef pageRef, CGRect rec CGContextFillRect(context, d); } - // Calculate a rectangle to draw to + // Calculate the rectangle to draw to CGRect pageRect = CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox); CGFloat drawingAspect = SRGAppearanceImageAspectScaleFit(pageRect.size, d); CGRect drawingRect = SRGAppearanceImageRectByFittingRect(pageRect, d); @@ -90,6 +90,21 @@ + (UIImage *)srg_vectorImageAtPath:(NSString *)filePath withSize:(CGSize)size return [self srg_vectorImageAtPath:filePath withSize:size fillColor:nil]; } ++ (UIImage *)srg_vectorImageAtPath:(NSString *)filePath withWidth:(CGFloat)width +{ + return [self srg_vectorImageAtPath:filePath withSize:CGSizeMake(width, 0.f) fillColor:nil]; +} + ++ (UIImage *)srg_vectorImageAtPath:(NSString *)filePath withHeight:(CGFloat)height +{ + return [self srg_vectorImageAtPath:filePath withSize:CGSizeMake(0.f, height) fillColor:nil]; +} + ++ (UIImage *)srg_vectorImageAtPath:(NSString *)filePath +{ + return [self srg_vectorImageAtPath:filePath withSize:CGSizeZero fillColor:nil]; +} + + (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size fillColor:(UIColor *)fillColor { // Check cached image existence at the very beginning, and return it if available @@ -113,8 +128,20 @@ + (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)siz return nil; } - UIGraphicsBeginImageContextWithOptions(size, NO, 1. /* Force scale to 1 (0 would use device scale) */); CGPDFPageRef pageRef = CGPDFDocumentGetPage(pdfDocumentRef, 1); + CGRect pageRect = CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox); + + if (CGSizeEqualToSize(size, CGSizeZero)) { + size = pageRect.size; + } + else if (size.width == 0.f) { + size.width = size.height * pageRect.size.width / pageRect.size.height; + } + else if (size.height == 0.f) { + size.height = size.width * pageRect.size.height / pageRect.size.width; + } + + UIGraphicsBeginImageContextWithOptions(size, NO, 1. /* Force scale to 1 (0 would use device scale) */); SRGAppearanceImageDrawPDFPageInRect(pageRef, CGRectMake(0.f, 0.f, size.width, size.height), fillColor.CGColor); UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); @@ -138,6 +165,21 @@ + (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)siz return [self srg_URLForVectorImageAtPath:filePath withSize:size fillColor:nil]; } ++ (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withWidth:(CGFloat)width +{ + return [self srg_URLForVectorImageAtPath:filePath withSize:CGSizeMake(width, 0.f) fillColor:nil]; +} + ++ (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withHeight:(CGFloat)height +{ + return [self srg_URLForVectorImageAtPath:filePath withSize:CGSizeMake(0.f, height) fillColor:nil]; +} + ++ (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath +{ + return [self srg_URLForVectorImageAtPath:filePath withSize:CGSizeZero fillColor:nil]; +} + + (void)srg_clearVectorImageCache { NSString *cachesDirectory = SRGAppearanceVectorImageCachesDirectory(); From 0409b76a26ff774be74be722822dd17a54988c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Tue, 31 Mar 2020 16:36:46 +0200 Subject: [PATCH 8/8] Bump minor version number --- Common.xcconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common.xcconfig b/Common.xcconfig index a2e6205..2a9693f 100644 --- a/Common.xcconfig +++ b/Common.xcconfig @@ -1,5 +1,5 @@ // Version information -MARKETING_VERSION = 2.0.1 +MARKETING_VERSION = 2.1.0 // Deployment targets IPHONEOS_DEPLOYMENT_TARGET = 9.0