Skip to content

Instantly share code, notes, and snippets.

@erkanyildiz
Last active January 29, 2020 03:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save erkanyildiz/bac85bd1fa07c44603fd to your computer and use it in GitHub Desktop.
Save erkanyildiz/bac85bd1fa07c44603fd to your computer and use it in GitHub Desktop.
Utilities for Cocoa
// erkanyildiz
// 20191228-2138+0900
//
// EYUtils.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#ifndef DEBUG_MODE
#define DEBUG_MODE 1
#endif
#if DEBUG_MODE
#define DLOG(...) NSLog(__VA_ARGS__)
#else
#define DLOG(...)
#endif
#define MLOG DLOG(@"%s",__FUNCTION__)
#pragma mark ---
#define SCREEN_W (MIN(UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height))
#define SCREEN_H (MAX(UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height))
#define IS_IPAD (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)
#define IS_1024 (IS_IPAD && SCREEN_H == 1024)
#define IS_1080 (IS_IPAD && SCREEN_H == 1080)
#define IS_1112 (IS_IPAD && SCREEN_H == 1112)
#define IS_1366 (IS_IPAD && SCREEN_H == 1366)
#define IS_IPHONE (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone)
#define IS_480 (IS_IPHONE && SCREEN_H == 480)
#define IS_568 (IS_IPHONE && SCREEN_H == 568)
#define IS_667 (IS_IPHONE && SCREEN_H == 667)
#define IS_736 (IS_IPHONE && SCREEN_H == 736)
#define IS_812 (IS_IPHONE && SCREEN_H == 812)
#define IS_896 (IS_IPHONE && SCREEN_H == 896)
#define IS_W320 (IS_IPHONE && SCREEN_W == 320)
#define IS_W375 (IS_IPHONE && SCREEN_W == 375)
#define IS_W414 (IS_IPHONE && SCREEN_W == 414)
#define VFH(a,b,c,d,e,f) (IS_480?(a):IS_568?(b):IS_667?(c):IS_736?(d):IS_812?(e):IS_896?(f):(f))
#define VFW(a,b,c) (IS_W320?(a):IS_W375?(b):IS_W414?(c):(c))
#pragma mark ---
#define IS_IOS8 ([UIDevice.currentDevice.systemVersion hasPrefix:@"8"])
#define IS_IOS9 ([UIDevice.currentDevice.systemVersion hasPrefix:@"9"])
#define IS_IOS10 ([UIDevice.currentDevice.systemVersion hasPrefix:@"10"])
#define IS_IOS11 ([UIDevice.currentDevice.systemVersion hasPrefix:@"11"])
#define IS_IOS12 ([UIDevice.currentDevice.systemVersion hasPrefix:@"12"])
#define IS_IOS13 ([UIDevice.currentDevice.systemVersion hasPrefix:@"13"])
#pragma mark ---
#define IMG(n) [UIImage imageNamed:(n)]
#define FONT(n,s) [UIFont fontWithName:(n) size:(s)]
#define RGB(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
#define RGBA(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:(a)]
#define RGBW(w, a) [UIColor colorWithWhite:(w)/255.0 alpha:(a)]
#define NSUD NSUserDefaults.standardUserDefaults
#define LS(str) NSLocalizedString(str, nil)
#define FS(...) [NSString stringWithFormat:__VA_ARGS__]
#pragma mark ---
#define D2R(angle) ((angle) * M_PI / 180.0)
#define R2D(angle) ((angle) * 180.0 / M_PI)
#define RATIO_16_9 (16.0 / 9.0)
#pragma mark -
void afterDelay(NSTimeInterval delay, void(^block)(void));
void onMainThread(void(^block)(void));
#pragma mark -
@interface EYUtils : NSObject
+ (instancetype)sharedInstance;
+ (void)setShouldUseLightActivityIndicatorStyle:(BOOL)shouldUseLightActivityIndicatorStyle;
@end
#pragma mark -
@interface NSObject (EYUtils)
- (void)dump;
- (void)dumpWith:(NSString *)logText;
@end
#pragma mark -
@interface NSString (EYUtils)
+ (NSString *)randomStringWithLength:(NSUInteger)length;
- (NSString *)stringByDeletingNewLines;
#pragma mark Validation
- (BOOL)isValidEMail;
#pragma mark Encryption | Hashing
- (NSString *)stringByEncryptingWithKey:(NSString *)key;
- (NSString *)stringByDecryptingWithKey:(NSString *)key;
- (NSString *)SHA256;
- (NSString *)MD5;
#pragma mark Network Operations
- (NSURL *)URL;
- (NSURLRequest *)request;
@end
#pragma mark -
@interface NSURLRequest (EYUtils)
- (void)fetchJSON:(void (^)(id JSONResponse, NSError* error))handler;
- (void)fetchImage:(void (^)(UIImage* image, NSError* error))handler;
- (void)fetchData:(void (^)(NSData* data, NSError* error))handler;
- (NSString *)cURLCommand;
@end
#pragma mark -
@interface NSURL (EYUtils)
- (NSURL *)URLByAddingQueryParameters:(NSDictionary *)parameters;
- (NSString *)valueForQueryParameter:(NSString *)parameter;
@end
#pragma mark -
@interface NSArray (EYUtils)
- (NSString *)JSON;
@end
#pragma mark -
@interface NSDictionary (EYUtils)
- (NSString *)JSON;
@end
#pragma mark -
@interface NSMutableData (EYUtils)
- (void)appendString:(NSString *)string;
@end
#pragma mark -
@interface UIColor (EYUtils)
- (UIColor *)colorByMixingWithColor:(UIColor *)color atRatio:(CGFloat)ratio;
+ (UIColor *)colorFromHexString:(NSString *)hexString;
@end
#pragma mark -
@interface UIImage (EYUtils)
- (UIImage *)imageByCombiningWithImage:(UIImage *)upperImage;
+ (UIImage *)imageWithColor:(UIColor *)color andSize:(CGSize)size;
@end
#pragma mark -
@interface UIApplication (EYUtils)
+ (NSString *)displayName;
+ (NSString *)version;
+ (NSString *)build;
@end
#pragma mark -
@interface UIWindow (EYUtils)
+ (void)alert:(NSString *)message;
@end
#pragma mark -
@interface UIViewController (EYUtils)
+ (instancetype)createFromInterfaceFile;
+ (instancetype)createFromInterfaceFileWithName:(NSString *)interfaceFileName;
- (UIViewController *)previousViewControllerOnStack;
- (BOOL)isFirstAppearance;
#pragma mark Activity
- (void)showActivityIndicator;
- (void)hideActivityIndicator;
#pragma mark Alert
- (void)alert:(NSString *)message;
- (void)alert:(NSString *)message title:(NSString *)title;
- (void)alert:(NSString *)message dismissText:(NSString *)dismissText;
- (void)alert:(NSString *)message title:(NSString *)title dismissText:(NSString *)dismissText;
- (void)alert:(NSString *)message title:(NSString *)title dismissText:(NSString *)dismissText completion:(void(^)(UIAlertController* alertController))completion;
@end
#pragma mark -
@interface UIButton (EYUtils)
@property (nonatomic, copy) void (^onClick)(id sender);
@end
#pragma mark -
@interface UIScrollView (EYUtils)
- (BOOL)isAtTheBottom;
#if TARGET_OS_IOS
- (void)setupAutoKeyboardAdjustment;
#endif
@end
#pragma mark -
@interface UITextField (EYUtils)
- (void)addLeftPadding:(CGFloat)padding;
@end
#pragma mark -
@interface UILabel (EYUtils)
- (void)setLineSpacing:(CGFloat)lineSpacing andKerning:(CGFloat)kerning;
- (NSInteger)lineCount;
@end
#pragma mark -
@interface UITableView (EYUtils)
- (void)updateCellHeightsWithBufferDelay;
@end
#pragma mark -
@interface UIView (EYUtils)
#pragma mark Positioning
- (void)moveRight:(CGFloat)value;
- (void)moveLeft:(CGFloat)value;
- (void)moveUp:(CGFloat)value;
- (void)moveDown:(CGFloat)value;
- (void)centerInSuperView;
- (void)centerRightInSuperView;
- (void)centerLeftInSuperView;
- (void)centerTopInSuperView;
- (void)centerBottomInSuperView;
- (void)stickToRightInSuperView;
- (void)stickToLeftInSuperView;
- (void)stickToTopInSuperView;
- (void)stickToBottomInSuperView;
- (void)stickToBottomOfView:(UIView *)targetView;
- (void)stickToBottomOfView:(UIView *)targetView withMargin:(CGFloat)margin;
- (void)stickToRightOfView:(UIView *)targetView;
- (void)stickToRightOfView:(UIView *)targetView withMargin:(CGFloat)margin;
- (void)setCenterX:(CGFloat)value;
- (void)setCenterY:(CGFloat)value;
- (void)setOriginX:(CGFloat)value;
- (void)setOriginY:(CGFloat)value;
- (void)setOrigin:(CGPoint)origin;
#pragma mark Resizing
- (void)setHeight:(CGFloat)value;
- (void)setWidth:(CGFloat)value;
- (void)expandRight:(CGFloat)value;
- (void)expandLeft:(CGFloat)value;
- (void)expandUp:(CGFloat)value;
- (void)expandDown:(CGFloat)value;
#pragma mark Border & Shadow
//NOTE: Borders will be drawn using CALayer's, thus do not support auto resizing.
- (void)addBottomBorderWithColor:(UIColor *)color andWidth:(CGFloat)borderWidth;
- (void)addLeftBorderWithColor:(UIColor *)color andWidth:(CGFloat)borderWidth;
- (void)addRightBorderWithColor:(UIColor *)color andWidth:(CGFloat)borderWidth;
- (void)addTopBorderWithColor:(UIColor *)color andWidth:(CGFloat)borderWidth;
- (void)addBorderWithColor:(UIColor *)color andWidth:(CGFloat)borderWidth;
- (void)addDropShadow;
- (void)roundCorners;
- (void)roundCornersWithRadius:(CGFloat)radius;
#pragma mark Animated Positioning
- (void)moveAnimatedRight:(CGFloat)value;
- (void)moveAnimatedRight:(CGFloat)value duration:(NSTimeInterval)duration;
- (void)moveAnimatedLeft:(CGFloat)value;
- (void)moveAnimatedLeft:(CGFloat)value duration:(NSTimeInterval)duration;
- (void)moveAnimatedUp:(CGFloat)value;
- (void)moveAnimatedUp:(CGFloat)value duration:(NSTimeInterval)duration;
- (void)moveAnimatedDown:(CGFloat)value;
- (void)moveAnimatedDown:(CGFloat)value duration:(NSTimeInterval)duration;
- (void)centerAnimatedInSuperView;
- (void)centerAnimatedInSuperViewWithDuration:(NSTimeInterval)duration;
#pragma mark Fading
- (void)fadeIn;
- (void)fadeInWithDuration:(NSTimeInterval)duration;
- (void)fadeOut;
- (void)fadeOutWithDuration:(NSTimeInterval)duration;
#pragma mark Animated Resizing
- (void)scaleTo:(CGFloat)ratio;
- (void)scaleTo:(CGFloat)ratio duration:(NSTimeInterval)duration;
- (void)shrink;
- (void)shrinkWithDuration:(NSTimeInterval)duration;
- (void)unshrink;
- (void)unshrinkWithDuration:(NSTimeInterval)duration;
#pragma mark Image Rendering
- (UIImage *)renderedImage;
- (BOOL)saveRenderedImageToDocuments;
#pragma mark Activity
- (void)showActivityIndicator;
- (void)hideActivityIndicator;
#pragma mark Shimmering
- (void)startShimmering;
- (void)stopShimmering;
#pragma mark Creating
+ (instancetype)createFromXIB;
#pragma mark Debugging
- (void)dumpFrame:(NSString *)comment;
@end
// erkanyildiz
// 20191228-2138+0900
//
// EYUtils.m
#import "EYUtils.h"
#import <objc/runtime.h>
#include <CommonCrypto/CommonDigest.h>
#pragma mark - EYUtils Class Core
@interface EYUtils ()
@property (nonatomic) BOOL shouldUseLightActivityIndicatorStyle;
@property (nonatomic) UIWindow* windowsForFloatingAlerts;
@property (nonatomic) NSInteger countForFloatingAlerts;
@end
@implementation EYUtils
+ (instancetype)sharedInstance
{
static id s_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ s_sharedInstance = self.new; });
return s_sharedInstance;
}
+ (void)setShouldUseLightActivityIndicatorStyle:(BOOL)shouldUseLightActivityIndicatorStyle
{
EYUtils.sharedInstance.shouldUseLightActivityIndicatorStyle = shouldUseLightActivityIndicatorStyle;
}
@end
#pragma mark - Helper Functions
#pragma mark -
NSString* JSONFromObject(id object)
{
if ([NSJSONSerialization isValidJSONObject:object])
{
NSError* error = nil;
NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:&error];
if (error) { NSLog(@"NSJSONSerialization internal error:\n%@", error); }
return [NSString.alloc initWithData:data encoding:NSUTF8StringEncoding];
}
return nil;
}
void afterDelay(NSTimeInterval delay, void(^block)(void))
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), block);
}
void onMainThread(void(^block)(void))
{
if (NSThread.isMainThread)
block();
else
dispatch_async(dispatch_get_main_queue(), block);
}
#pragma mark - Helper Subclasses
#pragma mark -
@interface EYUtilsAlertController : UIAlertController
@end
@implementation EYUtilsAlertController
#if TARGET_OS_IOS
- (BOOL)shouldAutorotate
{
return [self.presentingViewController shouldAutorotate];
}
- (BOOL)prefersStatusBarHidden
{
return [self.presentingViewController prefersStatusBarHidden];
}
- (BOOL)prefersHomeIndicatorAutoHidden
{
return [self.presentingViewController prefersHomeIndicatorAutoHidden];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return [self.presentingViewController preferredStatusBarStyle];
}
#endif
@end
#pragma mark -
@interface EYUtilsActivityContainer : UIVisualEffectView
@property (nonatomic) NSInteger count;
@end
@implementation EYUtilsActivityContainer
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
BOOL isLight = EYUtils.sharedInstance.shouldUseLightActivityIndicatorStyle;
self.count = 1;
self.userInteractionEnabled = NO;
self.effect = [UIBlurEffect effectWithStyle:isLight ? UIBlurEffectStyleExtraLight : UIBlurEffectStyleDark];
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
UIActivityIndicatorView* aiv = [UIActivityIndicatorView.alloc initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
aiv.color = isLight ? UIColor.blackColor : nil;
aiv.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.contentView addSubview:aiv];
[aiv centerInSuperView];
[aiv startAnimating];
}
return self;
}
@end
#pragma mark - Categories
#pragma mark -
@implementation NSObject (EYUtils)
- (void)dump
{
NSLog(@"%@", [self description]);
}
- (void)dumpWith:(NSString *)logText
{
NSLog(@"%@: %@", logText, [self description]);
}
@end
#pragma mark -
@implementation NSString (EYUtils)
+ (NSString *)randomStringWithLength:(NSUInteger)length
{
NSMutableString* random = [NSMutableString stringWithCapacity:length];
for (NSUInteger i=0; i<length; i++)
{
char c = '0' + (unichar)arc4random()%36;
if(c > '9') c += ('a'-'9'-1);
[random appendFormat:@"%c", c];
}
return random;
}
- (NSString *)stringByDeletingNewLines
{
NSString* lineFeedCleared = [self stringByReplacingOccurrencesOfString:@"\n" withString:@" "];
NSString* carriageReturnCleared = [lineFeedCleared stringByReplacingOccurrencesOfString:@"\r" withString:@" "];
return carriageReturnCleared;
}
#pragma mark Validation
- (BOOL)isValidEMail
{
NSString* emailRegex = @"^[a-zA-Z0-9!'#$%&*+\\/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!'#$%&*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$";
NSRegularExpression* regExp = [NSRegularExpression regularExpressionWithPattern:emailRegex options:0 error:nil];
NSUInteger numberOfMatches = [regExp numberOfMatchesInString:self options:0 range:(NSRange){0, self.length}];
if (numberOfMatches == 0)
return NO;
return YES;
}
#pragma mark Encryption
- (NSString *)stringByEncryptingWithKey:(NSString *)key
{
NSData* rawData = [self dataUsingEncoding:NSUTF8StringEncoding];
const char* rawBytes = rawData.bytes;
NSUInteger keyLength = key.length;
NSMutableData* encData = NSMutableData.data;
for (int i = 0; i < rawData.length; i++)
{
NSUInteger keyIndex = i % keyLength;
char encChar = rawBytes[i] ^ [key characterAtIndex:keyIndex];
[encData appendBytes:&encChar length:1];
}
return [encData base64EncodedStringWithOptions:0];
}
- (NSString *)stringByDecryptingWithKey:(NSString *)key
{
NSData* encData = [NSData.alloc initWithBase64EncodedString:self options:0];
const char* encBytes = encData.bytes;
NSUInteger keyLength = key.length;
NSMutableData* rawData = NSMutableData.data;
for (int i = 0; i < encData.length; i++)
{
NSUInteger keyIndex = i % keyLength;
char encChar = encBytes[i] ^ [key characterAtIndex:keyIndex];
[rawData appendBytes:&encChar length:1];
}
return [NSString.alloc initWithData:rawData encoding:NSUTF8StringEncoding];
}
#pragma mark Hashing
- (NSString *)SHA256
{
const char* s = [self UTF8String];
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(s, (CC_LONG)strlen(s), digest);
NSMutableString *hash = NSMutableString.new;
for(int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++)
[hash appendFormat:@"%02x", digest[i]];
return hash;
}
- (NSString *)MD5
{
const char *s = [self UTF8String];
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5(s, (CC_LONG)strlen(s), digest);
NSMutableString *hash = NSMutableString.new;
for (int i = 0; i < 16; i++)
[hash appendFormat:@"%02x", digest[i]];
return hash;
}
#pragma mark Network Operations
- (NSURL *)URL
{
return [NSURL URLWithString:self];
}
- (NSURLRequest *)request
{
return [NSURLRequest requestWithURL:[self URL]];
}
@end
#pragma mark -
@implementation NSURLRequest (EYUtils)
- (void)fetchJSON:(void (^)(id JSONResponse, NSError* error))handler
{
NSURLSessionTask* task = [NSURLSession.sharedSession dataTaskWithRequest:self completionHandler:^(NSData* data, NSURLResponse* response, NSError* error)
{
if(error)
{
NSMutableDictionary* userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo];
userInfo[@"response"] = response;
NSError* errorWithResponse = [NSError.alloc initWithDomain:error.domain code:error.code userInfo:userInfo];
onMainThread(^
{
handler(nil, errorWithResponse);
});
}
else
{
NSError* jsonError = nil;
id JSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
onMainThread(^
{
if(jsonError)
{
handler(nil, jsonError);
}
else
{
handler(JSON, nil);
}
});
}
}];
[task resume];
}
- (void)fetchImage:(void (^)(UIImage* image, NSError* error))handler
{
NSURLSessionTask* task = [NSURLSession.sharedSession dataTaskWithRequest:self completionHandler:^(NSData* data, NSURLResponse* response, NSError* error)
{
if(error)
{
NSMutableDictionary* userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo];
userInfo[@"response"] = response;
NSError* errorWithResponse = [NSError.alloc initWithDomain:error.domain code:error.code userInfo:userInfo];
onMainThread(^
{
handler(nil, errorWithResponse);
});
}
else
{
UIImage* image = [UIImage imageWithData:data];
onMainThread(^
{
if(image)
{
handler(image, nil);
}
else
{
handler(nil, [NSError errorWithDomain:@"EYUtilsRequestImageError" code:1000 userInfo:@{@"Description:":@"Invalid image data"}]);
}
});
}
}];
[task resume];
}
- (void)fetchData:(void (^)(NSData* data, NSError* error))handler
{
NSURLSessionTask* task = [NSURLSession.sharedSession dataTaskWithRequest:self completionHandler:^(NSData* data, NSURLResponse* response, NSError* error)
{
if(error)
{
NSMutableDictionary* userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo];
userInfo[@"response"] = response;
NSError* errorWithResponse = [NSError.alloc initWithDomain:error.domain code:error.code userInfo:userInfo];
onMainThread(^
{
handler(nil, errorWithResponse);
});
}
else
{
onMainThread(^
{
handler(data, nil);
});
}
}];
[task resume];
}
- (NSString *)cURLCommand
{
__block NSMutableString* command = [NSMutableString stringWithFormat:@"curl -v %@", self.HTTPMethod];
[command appendFormat:@" \'%@\'", self.URL.absoluteString];
[self.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id val, BOOL *stop)
{
[command appendFormat:@" -H \'%@: %@\'", key, val];
}];
if (self.HTTPBody)
{
NSString* body = [NSString.alloc initWithData:self.HTTPBody encoding:NSUTF8StringEncoding];
body = [body stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"];
[command appendFormat:@" -d \'%@\'", body];
}
return command;
}
@end
#pragma mark -
@implementation NSURL (EYUtils)
- (NSURL *)URLByAddingQueryParameters:(NSDictionary *)parameters
{
if (!parameters)
return self;
NSURLComponents* components = [NSURLComponents.alloc initWithURL:self resolvingAgainstBaseURL:NO];
NSMutableArray* queryItems = components.queryItems ? components.queryItems.mutableCopy : @[].mutableCopy;
[parameters enumerateKeysAndObjectsUsingBlock:^(NSString* key, id rawValue, BOOL* stop)
{
NSString* value = nil;
if ([rawValue isKindOfClass:NSString.class])
value = rawValue;
else if ([rawValue isKindOfClass:NSNumber.class])
value = [rawValue stringValue];
else if ([rawValue isKindOfClass:NSArray.class] || [rawValue isKindOfClass:NSDictionary.class])
value = [rawValue JSON];
else
value = [value description];
if (value)
{
NSURLQueryItem* queryItem = [NSURLQueryItem.alloc initWithName:key value:value];
[queryItems addObject:queryItem];
}
}];
[components setQueryItems:queryItems];
return [components URL];
}
- (NSString *)valueForQueryParameter:(NSString *)parameter
{
NSURLComponents* components = [NSURLComponents componentsWithURL:self resolvingAgainstBaseURL:NO];
NSArray* queryItems = components.queryItems;
for (NSURLQueryItem* queryItem in queryItems)
if ([queryItem.name isEqualToString:parameter])
return queryItem.value;
return nil;
}
@end
#pragma mark -
@implementation NSArray (EYUtils)
- (NSString *)JSON
{
return JSONFromObject(self);
}
@end
#pragma mark -
@implementation NSDictionary (EYUtils)
- (NSString *)JSON
{
return JSONFromObject(self);
}
@end
#pragma mark -
@implementation NSMutableData (EYUtils)
- (void)appendString:(NSString *)string
{
[self appendData:[string dataUsingEncoding:NSUTF8StringEncoding]];
}
@end
#pragma mark -
@implementation UIColor (EYUtils)
- (UIColor *)colorByMixingWithColor:(UIColor *)color atRatio:(CGFloat)ratio
{
CGFloat oR, oG, oB, oA;
[self getRed:&oR green:&oG blue:&oB alpha:&oA];
CGFloat cR, cG, cB, cA;
[color getRed:&cR green:&cG blue:&cB alpha:&cA];
CGFloat dR = cR - oR;
CGFloat dG = cG - oG;
CGFloat dB = cB - oB;
CGFloat dA = cA - oA;
CGFloat r = MAX(MIN(ratio, 1.0),0.0);
return [UIColor colorWithRed:oR + dR * r green:oG + dG * r blue:oB + dB * r alpha:oA + dA * r];
}
+ (UIColor *)colorFromHexString:(NSString *)hexString
{
hexString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""];
unsigned hexInt = 0;
[[NSScanner scannerWithString:hexString] scanHexInt:&hexInt];
return RGB((hexInt & 0xFF0000) >> 16, (hexInt & 0x00FF00) >> 8, (hexInt & 0x0000FF));
}
@end
#pragma mark -
@implementation UIImage (EYUtils)
- (UIImage *)imageByCombiningWithImage:(UIImage *)upperImage
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
[self drawAtPoint:CGPointZero];
CGPoint p = (CGPoint){0.5 * (self.size.width - upperImage.size.width), 0.5 * (self.size.height - upperImage.size.height)};
[upperImage drawAtPoint:p];
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
+ (UIImage *)imageWithColor:(UIColor *)color andSize:(CGSize)size
{
CGRect rect = (CGRect){0.0f, 0.0f, size.width, size.height};
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
@end
#pragma mark -
@implementation UIApplication (EYUtils)
+ (NSString *)displayName
{
return [NSBundle.mainBundle.infoDictionary objectForKey:@"CFBundleDisplayName"];
}
+ (NSString *)version
{
return [NSBundle.mainBundle.infoDictionary objectForKey:@"CFBundleShortVersionString"];
}
+ (NSString *)build
{
return [NSBundle.mainBundle.infoDictionary objectForKey:(NSString *)kCFBundleVersionKey];
}
@end
#pragma mark -
@implementation UIWindow (EYUtils)
+ (void)alert:(NSString *)message
{
if (!EYUtils.sharedInstance.windowsForFloatingAlerts)
{
EYUtils.sharedInstance.windowsForFloatingAlerts = [UIWindow.alloc initWithFrame:UIScreen.mainScreen.bounds];
EYUtils.sharedInstance.windowsForFloatingAlerts.windowLevel = UIWindowLevelAlert;
UIViewController* tempVC = UIViewController.new;
tempVC.view.backgroundColor = UIColor.clearColor;
EYUtils.sharedInstance.windowsForFloatingAlerts.rootViewController = tempVC;
[EYUtils.sharedInstance.windowsForFloatingAlerts makeKeyAndVisible];
}
EYUtils.sharedInstance.countForFloatingAlerts++;
[EYUtils.sharedInstance.windowsForFloatingAlerts.rootViewController alert:message title:nil dismissText:nil completion:^(UIAlertController *alertController)
{
EYUtils.sharedInstance.countForFloatingAlerts--;
if (EYUtils.sharedInstance.countForFloatingAlerts <= 0)
EYUtils.sharedInstance.windowsForFloatingAlerts = nil;
}];
}
@end
#pragma mark -
@implementation UIViewController (EYUtils)
+ (instancetype)createFromInterfaceFile
{
NSString* fileName = NSStringFromClass(self.class);
return [self createFromInterfaceFileWithName:fileName];
}
+ (instancetype)createFromInterfaceFileWithName:(NSString *)interfaceFileName
{
NSString* fileName = interfaceFileName;
#if TARGET_OS_TV
if (![fileName hasPrefix:@"tv"])
fileName = [@"tv" stringByAppendingString:fileName];
#endif
if ([NSBundle.mainBundle pathForResource:fileName ofType:@"nib"])
return [self.alloc initWithNibName:fileName bundle:nil];
if ([NSBundle.mainBundle pathForResource:fileName ofType:@"storyboardc"])
{
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:fileName bundle:nil];
return [storyboard instantiateViewControllerWithIdentifier:fileName];
}
return [self.alloc init];
}
- (UIViewController *)previousViewControllerOnStack
{
NSInteger count = self.navigationController.viewControllers.count;
if (count < 2)
return nil;
else
return [self.navigationController.viewControllers objectAtIndex:count - 2];
}
- (BOOL)isFirstAppearance
{
return self.isBeingPresented || self.isMovingToParentViewController;
}
#pragma mark Activity
- (void)showActivityIndicator;
{
[self.view showActivityIndicator];
}
- (void)hideActivityIndicator
{
[self.view hideActivityIndicator];
}
#pragma mark Alert
- (void)alert:(NSString *)message
{
[self alert:message title:nil dismissText:nil completion:nil];
}
- (void)alert:(NSString *)message title:(NSString *)title
{
[self alert:message title:title dismissText:nil completion:nil];
}
- (void)alert:(NSString *)message dismissText:(NSString *)dismissText
{
[self alert:message title:nil dismissText:dismissText completion:nil];
}
- (void)alert:(NSString *)message title:(NSString *)title dismissText:(NSString *)dismissText
{
[self alert:message title:title dismissText:dismissText completion:nil];
}
- (void)alert:(NSString *)message title:(NSString *)title dismissText:(NSString *)dismissText completion:(void(^)(UIAlertController* alertController))completion
{
EYUtilsAlertController* controller = [EYUtilsAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
__weak typeof(controller) wController = controller;
UIAlertAction* action = [UIAlertAction actionWithTitle:dismissText ?: self.localizedDismissText style:UIAlertActionStyleDefault handler:^(UIAlertAction* action)
{
if (completion)
completion (wController);
}];
[controller addAction:action];
[self presentViewController:controller animated:YES completion:nil];
}
- (NSString *)localizedDismissText
{
NSString* langDesignator = [NSLocale.preferredLanguages.firstObject substringToIndex:2];
NSDictionary* dict =
@{
@"en": @"Dismiss",
@"tr": @"Kapat",
@"ja": @"閉じる",
};
NSString* localizedDismissText = dict[langDesignator];
if (!localizedDismissText)
localizedDismissText = dict[@"en"];
return localizedDismissText;
}
@end
#pragma mark -
@implementation UIButton (EYUtils)
- (void(^)(__strong id))onClick
{
return objc_getAssociatedObject(self, @selector(setOnClick:));
}
- (void)setOnClick:(void (^)(id))onClick
{
objc_setAssociatedObject(self, @selector(setOnClick:), onClick, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)clickAction:(id)sender
{
void(^onClick)(id sender) = objc_getAssociatedObject(self, @selector(setOnClick:));
if (onClick)
onClick(sender);
}
@end
#pragma mark -
@implementation UIScrollView (EYUtils)
- (BOOL)isAtTheBottom
{
return (self.contentOffset.y >= self.contentSize.height - self.bounds.size.height);
}
#if TARGET_OS_IOS
- (void)setupAutoKeyboardAdjustment
{
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(onKeyboardWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
- (void)onKeyboardWillChange:(NSNotification *)notification
{
CGFloat duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
UIViewAnimationCurve curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
UIViewAnimationOptions options = (UIViewAnimationOptions)curve << 16;
CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect convertedFrame = [self.superview convertRect:keyboardFrame fromView:nil];
CGFloat newHeight = convertedFrame.origin.y - self.frame.origin.y;
[UIView animateWithDuration:duration delay:0 options:options animations:^
{
[self setHeight:newHeight];
} completion:nil];
}
#endif
@end
#pragma mark -
@implementation UITextField (EYUtils)
- (void)addLeftPadding:(CGFloat)padding
{
UIView* paddingView = [UIView.alloc initWithFrame:self.bounds];
[paddingView setWidth:padding];
self.leftView = paddingView;
self.leftViewMode = UITextFieldViewModeAlways;
}
@end
#pragma mark -
@implementation UILabel (EYUtils)
- (void)setLineSpacing:(CGFloat)lineSpacing andKerning:(CGFloat)kerning
{
if (!self.text)
return;
NSMutableParagraphStyle* paragraphStyle = NSMutableParagraphStyle.new;
paragraphStyle.lineSpacing = lineSpacing;
paragraphStyle.alignment = self.textAlignment;
paragraphStyle.lineBreakMode = self.lineBreakMode;
NSDictionary* attributes =
@{
NSFontAttributeName: self.font,
NSParagraphStyleAttributeName: paragraphStyle,
NSKernAttributeName: @(kerning),
};
NSMutableAttributedString* attributedString = [NSMutableAttributedString.alloc initWithString:self.text attributes:attributes];
self.attributedText = attributedString;
}
- (NSInteger)lineCount
{
CGSize textSize = CGSizeMake(self.frame.size.width, MAXFLOAT);
NSInteger totalHeight = lroundf([self sizeThatFits:textSize].height);
NSInteger rowHeight = lroundf(self.font.lineHeight);
NSInteger lineCount = totalHeight / rowHeight;
return lineCount;
}
@end
#pragma mark -
@implementation UITableView (EYUtils)
- (void)updateCellHeightsWithBufferDelay
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(updateCellHeights) object:nil];
[self performSelector:@selector(updateCellHeights) withObject:nil afterDelay:0.1];
}
- (void)updateCellHeights
{
[self beginUpdates];
[self endUpdates];
}
@end
#pragma mark -
@implementation UIView (EYUtils)
#pragma mark Positioning
- (void)moveRight:(CGFloat)value
{
CGPoint p = self.center;
p.x += value;
self.center = p;
}
- (void)moveLeft:(CGFloat)value
{
CGPoint p = self.center;
p.x -= value;
self.center = p;
}
- (void)moveUp:(CGFloat)value
{
CGPoint p = self.center;
p.y -= value;
self.center = p;
}
- (void)moveDown:(CGFloat)value
{
CGPoint p = self.center;
p.y += value;
self.center = p;
}
- (void)centerInSuperView
{
self.center = (CGPoint){0.5 * self.superview.bounds.size.width, 0.5 * self.superview.bounds.size.height};
}
- (void)centerRightInSuperView
{
[self centerInSuperView];
[self stickToRightInSuperView];
}
- (void)centerLeftInSuperView
{
[self centerInSuperView];
[self stickToLeftInSuperView];
}
- (void)centerTopInSuperView
{
[self centerInSuperView];
[self stickToTopInSuperView];
}
- (void)centerBottomInSuperView
{
[self centerInSuperView];
[self stickToBottomInSuperView];
}
- (void)stickToRightInSuperView
{
[self setOriginX:self.superview.bounds.size.width - self.bounds.size.width];
}
- (void)stickToLeftInSuperView
{
[self setOriginX:0.0];
}
- (void)stickToTopInSuperView
{
[self setOriginY:0.0];
}
- (void)stickToBottomInSuperView
{
[self setOriginY:self.superview.bounds.size.height - self.bounds.size.height];
}
- (void)stickToBottomOfView:(UIView *)targetView
{
[self stickToBottomOfView:targetView withMargin:0.0];
}
- (void)stickToBottomOfView:(UIView *)targetView withMargin:(CGFloat)margin
{
CGFloat y = CGRectGetMaxY(targetView.frame);
if (targetView.frame.size.height > 0.0)
y += margin;
[self setOriginY:y];
}
- (void)stickToRightOfView:(UIView *)targetView
{
[self stickToRightOfView:targetView withMargin:0.0];
}
- (void)stickToRightOfView:(UIView *)targetView withMargin:(CGFloat)margin
{
CGFloat x = CGRectGetMaxX(targetView.frame);
if (targetView.frame.size.width > 0.0)
x += margin;
[self setOriginX:x];
}
- (void)setCenterX:(CGFloat)value
{
self.center = (CGPoint){value, self.center.y};
}
- (void)setCenterY:(CGFloat)value
{
self.center = (CGPoint){self.center.x, value};
}
- (void)setOriginX:(CGFloat)value
{
CGRect r = self.frame;
r.origin.x = value;
self.frame = r;
}
- (void)setOriginY:(CGFloat)value
{
CGRect r = self.frame;
r.origin.y = value;
self.frame = r;
}
- (void)setOrigin:(CGPoint)origin
{
CGRect r = self.frame;
r.origin = origin;
self.frame = r;
}
#pragma mark Resizing
- (void)setHeight:(CGFloat)value
{
CGRect r = self.frame;
r.size.height = value;
self.frame = r ;
}
- (void)setWidth:(CGFloat)value
{
CGRect r = self.frame;
r.size.width = value;
self.frame = r;
}
- (void)expandRight:(CGFloat)value
{
CGFloat newValue = self.bounds.size.width + value;
[self setWidth:newValue];
}
- (void)expandLeft:(CGFloat)value
{
[self moveLeft:value];
[self expandRight:value];
}
- (void)expandUp:(CGFloat)value
{
[self moveUp:value];
[self expandDown:value];
}
- (void)expandDown:(CGFloat)value
{
CGFloat newValue = self.bounds.size.height + value;
[self setHeight:newValue];
}
#pragma mark Border & Shadow
- (void)addTopBorderWithColor:(UIColor *)color andWidth:(CGFloat)borderWidth
{
CALayer* border = CALayer.layer;
border.backgroundColor = color.CGColor;
border.frame = (CGRect){0.0, 0.0, self.frame.size.width, borderWidth};
[self.layer addSublayer:border];
}
- (void)addBottomBorderWithColor:(UIColor *)color andWidth:(CGFloat)borderWidth
{
CALayer *border = [CALayer layer];
border.backgroundColor = color.CGColor;
border.frame = (CGRect){0.0, self.frame.size.height - borderWidth, self.frame.size.width, borderWidth};
[self.layer addSublayer:border];
}
- (void)addLeftBorderWithColor:(UIColor *)color andWidth:(CGFloat)borderWidth
{
CALayer *border = [CALayer layer];
border.backgroundColor = color.CGColor;
border.frame = (CGRect){0.0, 0.0, borderWidth, self.frame.size.height};
[self.layer addSublayer:border];
}
- (void)addRightBorderWithColor:(UIColor *)color andWidth:(CGFloat)borderWidth
{
CALayer *border = [CALayer layer];
border.backgroundColor = color.CGColor;
border.frame = (CGRect){self.frame.size.width - borderWidth, 0.0, borderWidth, self.frame.size.height};
[self.layer addSublayer:border];
}
- (void)addBorderWithColor:(UIColor *)color andWidth:(CGFloat)borderWidth
{
self.layer.borderWidth = borderWidth;
self.layer.borderColor = color.CGColor;
}
- (void)addDropShadow
{
self.layer.shadowColor = UIColor.blackColor.CGColor;
self.layer.shadowOpacity = 0.2;
self.layer.shadowOffset = CGSizeMake(0.0f, 3.0f);
self.layer.masksToBounds = NO;
}
- (void)roundCorners
{
[self roundCornersWithRadius:self.bounds.size.height * 0.5];
}
- (void)roundCornersWithRadius:(CGFloat)radius
{
self.layer.cornerRadius = radius;
self.layer.masksToBounds = YES;
}
#pragma mark Animated Positioning
const NSTimeInterval kEYUtilsDefaultAnimationDuration = 0.25;
- (void)moveAnimatedRight:(CGFloat)value
{
[self moveAnimatedRight:value duration:kEYUtilsDefaultAnimationDuration];
}
- (void)moveAnimatedRight:(CGFloat)value duration:(NSTimeInterval)duration
{
[UIView animateWithDuration:duration delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{ [self moveRight:value]; } completion:nil];
}
- (void)moveAnimatedLeft:(CGFloat)value
{
[self moveAnimatedLeft:value duration:kEYUtilsDefaultAnimationDuration];
}
- (void)moveAnimatedLeft:(CGFloat)value duration:(NSTimeInterval)duration
{
[UIView animateWithDuration:duration delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{ [self moveLeft:value]; } completion:nil];
}
- (void)moveAnimatedUp:(CGFloat)value
{
[self moveAnimatedUp:value duration:kEYUtilsDefaultAnimationDuration];
}
- (void)moveAnimatedUp:(CGFloat)value duration:(NSTimeInterval)duration
{
[UIView animateWithDuration:duration delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{ [self moveUp:value]; } completion:nil];
}
- (void)moveAnimatedDown:(CGFloat)value
{
[self moveAnimatedDown:value duration:kEYUtilsDefaultAnimationDuration];
}
- (void)moveAnimatedDown:(CGFloat)value duration:(NSTimeInterval)duration
{
[UIView animateWithDuration:duration delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{ [self moveDown:value]; } completion:nil];
}
- (void)centerAnimatedInSuperView
{
[self centerAnimatedInSuperViewWithDuration:kEYUtilsDefaultAnimationDuration];
}
- (void)centerAnimatedInSuperViewWithDuration:(NSTimeInterval)duration
{
[UIView animateWithDuration:duration delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{ [self centerInSuperView]; } completion:nil];
}
#pragma mark Fading
- (void)fadeIn
{
[self fadeInWithDuration:kEYUtilsDefaultAnimationDuration];
}
- (void)fadeInWithDuration:(NSTimeInterval)duration
{
[UIView animateWithDuration:duration delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{ self.alpha = 1.0; } completion:nil];
}
- (void)fadeOut
{
[self fadeOutWithDuration:kEYUtilsDefaultAnimationDuration];
}
- (void)fadeOutWithDuration:(NSTimeInterval)duration
{
[UIView animateWithDuration:duration delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{ self.alpha = 0.0; } completion:nil];
}
#pragma mark Animated Resizing
- (void)scaleTo:(CGFloat)ratio
{
[self scaleTo:ratio duration:kEYUtilsDefaultAnimationDuration];
}
- (void)scaleTo:(CGFloat)ratio duration:(NSTimeInterval)duration
{
[UIView animateWithDuration:duration delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{ self.transform = (ratio == 1) ? CGAffineTransformIdentity : CGAffineTransformMakeScale(ratio, ratio); } completion:nil];
}
- (void)shrink
{
[self shrinkWithDuration:kEYUtilsDefaultAnimationDuration];
}
- (void)shrinkWithDuration:(NSTimeInterval)duration
{
[self scaleTo:0.0001 duration:duration];
}
- (void)unshrink
{
[self unshrinkWithDuration:kEYUtilsDefaultAnimationDuration];
}
- (void)unshrinkWithDuration:(NSTimeInterval)duration
{
[self scaleTo:1 duration:duration];
}
#pragma mark Image Rendering
- (UIImage *)renderedImage
{
BOOL isOpaque = self.opaque;
BOOL isHidden = self.hidden;
self.opaque = NO;
self.hidden = NO;
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, UIScreen.mainScreen.scale);
[UIColor.clearColor set];
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.opaque = isOpaque;
self.hidden = isHidden;
return image;
}
- (BOOL)saveRenderedImageToDocuments
{
NSData* imageData = UIImagePNGRepresentation([self renderedImage]);
NSURL* documentsDirectory = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].lastObject;
NSString* fileName = [NSString stringWithFormat:@"%@-%@.png", NSStringFromClass(self.class), NSDate.date.description];
NSURL *file = [documentsDirectory URLByAppendingPathComponent:fileName];
return [imageData writeToFile:file.path atomically:YES];
}
#pragma mark Activity
- (EYUtilsActivityContainer *)activityContainer
{
return objc_getAssociatedObject(self, @selector(activityContainer));
}
- (void)setActivityContainer:(EYUtilsActivityContainer *)activityContainer
{
objc_setAssociatedObject(self, @selector(activityContainer), activityContainer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)showActivityIndicator;
{
if (self.activityContainer)
{
self.activityContainer.count++;
return;
}
self.activityContainer = [EYUtilsActivityContainer.alloc initWithFrame:self.bounds];
[self addSubview:self.activityContainer];
self.activityContainer.userInteractionEnabled = self.userInteractionEnabled;
self.userInteractionEnabled = NO;
if ([self isKindOfClass:UIScrollView.class])
{
UIScrollView* scrollView = (UIScrollView *)self;
[scrollView setContentOffset:scrollView.contentOffset animated:NO];
}
}
- (void)hideActivityIndicator
{
if (!self.activityContainer)
return;
self.activityContainer.count --;
if (self.activityContainer.count == 0)
{
[self.activityContainer removeFromSuperview];
self.userInteractionEnabled = self.activityContainer.userInteractionEnabled;
self.activityContainer = nil;
}
}
#pragma mark Shimmering
- (CALayer *)shimmerLayer
{
return objc_getAssociatedObject(self, @selector(shimmerLayer));
}
- (void)setShimmerLayer:(CALayer *)shimmerLayer
{
objc_setAssociatedObject(self, @selector(shimmerLayer), shimmerLayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)startShimmering
{
if (self.shimmerLayer)
return;
CALayer* imageMaskLayer = nil;
if ([self isKindOfClass:UIImageView.class])
{
CALayer* imageOutline = CALayer.new;
imageOutline.frame = self.bounds;
imageOutline.contents = (id)((UIImageView *)self).image.CGImage;
imageMaskLayer = CALayer.new;
imageMaskLayer.frame = self.bounds;
imageMaskLayer.mask = imageOutline;
[self.layer addSublayer:imageMaskLayer];
}
CALayer* shimmerLayer = CALayer.new;
shimmerLayer.frame = self.bounds;
shimmerLayer.backgroundColor = UIColor.whiteColor.CGColor;
[imageMaskLayer ?: self.layer addSublayer:shimmerLayer];
self.shimmerLayer = shimmerLayer;
id light = (id)UIColor.whiteColor.CGColor;
id alpha = (id)[UIColor.whiteColor colorWithAlphaComponent:0.0].CGColor;
const CGFloat tilt = 0.022;
CAGradientLayer* gradient = CAGradientLayer.new;
gradient.colors = @[alpha, light, alpha];
gradient.frame = shimmerLayer.bounds;
gradient.startPoint = (CGPoint){0.0, 0.5 - tilt};
gradient.endPoint = (CGPoint){1.0, 0.5 + tilt};
gradient.locations = @[@0.0, @0.0, @0.0]; //NOTE: initial values are not important as they will be animated, but they have to be set
shimmerLayer.mask = gradient;
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"locations"];
animation.fromValue = @[@(-1.0), @(-0.5), @(0.0)];
animation.toValue = @[@(1.0), @(1.5), @(2.0)];
animation.duration = 1.0;
animation.repeatCount = MAXFLOAT;
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[gradient addAnimation:animation forKey:@"shimmer"];
[self addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)stopShimmering
{
if (!self.shimmerLayer)
return;
[self removeObserver:self forKeyPath:@"frame"];
[self.shimmerLayer removeFromSuperlayer];
self.shimmerLayer = nil;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
UIView* view = object;
if (view == self && [keyPath isEqualToString:@"frame"])
{
self.shimmerLayer.frame = view.bounds;
self.shimmerLayer.mask.frame = view.bounds;
}
}
#pragma mark Creating
+ (instancetype)createFromXIB;
{
NSString* fileName = NSStringFromClass(self.class);
if ([NSBundle.mainBundle pathForResource:fileName ofType:@"nib"])
return [NSBundle.mainBundle loadNibNamed:fileName owner:nil options:nil].firstObject;
return [self.alloc init];
}
#pragma mark Debugging
- (void)dumpFrame:(NSString *)comment
{
NSLog(@"\n%@Frame: %@\nCenter: %@", (comment) ? [comment stringByAppendingString:@": \n"] : @"",
NSStringFromCGRect(self.frame),
NSStringFromCGPoint(self.center));
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment