├── JKRShimmeringLabel.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── huhuaiyi.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── huhuaiyi.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── JKRShimmeringLabel ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── JKRLabel │ ├── JKRAutoScrollLabel.h │ ├── JKRAutoScrollLabel.m │ ├── JKRShimmeringLabel.h │ └── JKRShimmeringLabel.m ├── ViewController.h ├── ViewController.m ├── YYKit │ ├── Base │ │ ├── Foundation │ │ │ ├── NSArray+YYAdd.h │ │ │ ├── NSArray+YYAdd.m │ │ │ ├── NSBundle+YYAdd.h │ │ │ ├── NSBundle+YYAdd.m │ │ │ ├── NSData+YYAdd.h │ │ │ ├── NSData+YYAdd.m │ │ │ ├── NSDate+YYAdd.h │ │ │ ├── NSDate+YYAdd.m │ │ │ ├── NSDictionary+YYAdd.h │ │ │ ├── NSDictionary+YYAdd.m │ │ │ ├── NSKeyedUnarchiver+YYAdd.h │ │ │ ├── NSKeyedUnarchiver+YYAdd.m │ │ │ ├── NSNotificationCenter+YYAdd.h │ │ │ ├── NSNotificationCenter+YYAdd.m │ │ │ ├── NSNumber+YYAdd.h │ │ │ ├── NSNumber+YYAdd.m │ │ │ ├── NSObject+YYAdd.h │ │ │ ├── NSObject+YYAdd.m │ │ │ ├── NSObject+YYAddForARC.h │ │ │ ├── NSObject+YYAddForARC.m │ │ │ ├── NSObject+YYAddForKVO.h │ │ │ ├── NSObject+YYAddForKVO.m │ │ │ ├── NSString+YYAdd.h │ │ │ ├── NSString+YYAdd.m │ │ │ ├── NSThread+YYAdd.h │ │ │ ├── NSThread+YYAdd.m │ │ │ ├── NSTimer+YYAdd.h │ │ │ └── NSTimer+YYAdd.m │ │ ├── Quartz │ │ │ ├── CALayer+YYAdd.h │ │ │ ├── CALayer+YYAdd.m │ │ │ ├── YYCGUtilities.h │ │ │ └── YYCGUtilities.m │ │ ├── UIKit │ │ │ ├── UIApplication+YYAdd.h │ │ │ ├── UIApplication+YYAdd.m │ │ │ ├── UIBarButtonItem+YYAdd.h │ │ │ ├── UIBarButtonItem+YYAdd.m │ │ │ ├── UIBezierPath+YYAdd.h │ │ │ ├── UIBezierPath+YYAdd.m │ │ │ ├── UIColor+YYAdd.h │ │ │ ├── UIColor+YYAdd.m │ │ │ ├── UIControl+YYAdd.h │ │ │ ├── UIControl+YYAdd.m │ │ │ ├── UIDevice+YYAdd.h │ │ │ ├── UIDevice+YYAdd.m │ │ │ ├── UIFont+YYAdd.h │ │ │ ├── UIFont+YYAdd.m │ │ │ ├── UIGestureRecognizer+YYAdd.h │ │ │ ├── UIGestureRecognizer+YYAdd.m │ │ │ ├── UIImage+YYAdd.h │ │ │ ├── UIImage+YYAdd.m │ │ │ ├── UIScreen+YYAdd.h │ │ │ ├── UIScreen+YYAdd.m │ │ │ ├── UIScrollView+YYAdd.h │ │ │ ├── UIScrollView+YYAdd.m │ │ │ ├── UITableView+YYAdd.h │ │ │ ├── UITableView+YYAdd.m │ │ │ ├── UITextField+YYAdd.h │ │ │ ├── UITextField+YYAdd.m │ │ │ ├── UIView+YYAdd.h │ │ │ └── UIView+YYAdd.m │ │ └── YYKitMacro.h │ ├── Cache │ │ ├── YYCache.h │ │ ├── YYCache.m │ │ ├── YYDiskCache.h │ │ ├── YYDiskCache.m │ │ ├── YYKVStorage.h │ │ ├── YYKVStorage.m │ │ ├── YYMemoryCache.h │ │ └── YYMemoryCache.m │ ├── Image │ │ ├── Categories │ │ │ ├── CALayer+YYWebImage.h │ │ │ ├── CALayer+YYWebImage.m │ │ │ ├── MKAnnotationView+YYWebImage.h │ │ │ ├── MKAnnotationView+YYWebImage.m │ │ │ ├── UIButton+YYWebImage.h │ │ │ ├── UIButton+YYWebImage.m │ │ │ ├── UIImageView+YYWebImage.h │ │ │ ├── UIImageView+YYWebImage.m │ │ │ ├── _YYWebImageSetter.h │ │ │ └── _YYWebImageSetter.m │ │ ├── YYAnimatedImageView.h │ │ ├── YYAnimatedImageView.m │ │ ├── YYFrameImage.h │ │ ├── YYFrameImage.m │ │ ├── YYImage.h │ │ ├── YYImage.m │ │ ├── YYImageCache.h │ │ ├── YYImageCache.m │ │ ├── YYImageCoder.h │ │ ├── YYImageCoder.m │ │ ├── YYSpriteSheetImage.h │ │ ├── YYSpriteSheetImage.m │ │ ├── YYWebImageManager.h │ │ ├── YYWebImageManager.m │ │ ├── YYWebImageOperation.h │ │ └── YYWebImageOperation.m │ ├── Model │ │ ├── NSObject+YYModel.h │ │ ├── NSObject+YYModel.m │ │ ├── YYClassInfo.h │ │ └── YYClassInfo.m │ ├── Text │ │ ├── Component │ │ │ ├── YYTextContainerView.h │ │ │ ├── YYTextContainerView.m │ │ │ ├── YYTextDebugOption.h │ │ │ ├── YYTextDebugOption.m │ │ │ ├── YYTextEffectWindow.h │ │ │ ├── YYTextEffectWindow.m │ │ │ ├── YYTextInput.h │ │ │ ├── YYTextInput.m │ │ │ ├── YYTextKeyboardManager.h │ │ │ ├── YYTextKeyboardManager.m │ │ │ ├── YYTextLayout.h │ │ │ ├── YYTextLayout.m │ │ │ ├── YYTextLine.h │ │ │ ├── YYTextLine.m │ │ │ ├── YYTextMagnifier.h │ │ │ ├── YYTextMagnifier.m │ │ │ ├── YYTextSelectionView.h │ │ │ └── YYTextSelectionView.m │ │ ├── String │ │ │ ├── NSAttributedString+YYText.h │ │ │ ├── NSAttributedString+YYText.m │ │ │ ├── NSParagraphStyle+YYText.h │ │ │ ├── NSParagraphStyle+YYText.m │ │ │ ├── UIPasteboard+YYText.h │ │ │ ├── UIPasteboard+YYText.m │ │ │ ├── YYTextArchiver.h │ │ │ ├── YYTextArchiver.m │ │ │ ├── YYTextAttribute.h │ │ │ ├── YYTextAttribute.m │ │ │ ├── YYTextParser.h │ │ │ ├── YYTextParser.m │ │ │ ├── YYTextRubyAnnotation.h │ │ │ ├── YYTextRubyAnnotation.m │ │ │ ├── YYTextRunDelegate.h │ │ │ ├── YYTextRunDelegate.m │ │ │ ├── YYTextUtilities.h │ │ │ └── YYTextUtilities.m │ │ ├── YYLabel.h │ │ ├── YYLabel.m │ │ ├── YYTextView.h │ │ └── YYTextView.m │ ├── Utility │ │ ├── YYAsyncLayer.h │ │ ├── YYAsyncLayer.m │ │ ├── YYDispatchQueuePool.h │ │ ├── YYDispatchQueuePool.m │ │ ├── YYFileHash.h │ │ ├── YYFileHash.m │ │ ├── YYGestureRecognizer.h │ │ ├── YYGestureRecognizer.m │ │ ├── YYKeychain.h │ │ ├── YYKeychain.m │ │ ├── YYReachability.h │ │ ├── YYReachability.m │ │ ├── YYSentinel.h │ │ ├── YYSentinel.m │ │ ├── YYThreadSafeArray.h │ │ ├── YYThreadSafeArray.m │ │ ├── YYThreadSafeDictionary.h │ │ ├── YYThreadSafeDictionary.m │ │ ├── YYTimer.h │ │ ├── YYTimer.m │ │ ├── YYTransaction.h │ │ ├── YYTransaction.m │ │ ├── YYWeakProxy.h │ │ └── YYWeakProxy.m │ └── YYKit.h ├── ar.lproj │ ├── LaunchScreen.strings │ └── Main.strings └── main.m ├── LTR.GIF ├── README.md └── RTL.GIF /JKRShimmeringLabel.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JKRShimmeringLabel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /JKRShimmeringLabel.xcodeproj/project.xcworkspace/xcuserdata/huhuaiyi.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joker-388/JKRShimmeringLabel/d613b3d2ce70c5bebe70017a192cbfe19a20742f/JKRShimmeringLabel.xcodeproj/project.xcworkspace/xcuserdata/huhuaiyi.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /JKRShimmeringLabel.xcodeproj/xcuserdata/huhuaiyi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /JKRShimmeringLabel.xcodeproj/xcuserdata/huhuaiyi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | JKRShimmeringLabel.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // JKRShimmeringLabel 4 | // 5 | // Created by 胡怀刈 on 2023/4/11. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (nonatomic, strong) UIWindow *window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // JKRShimmeringLabel 4 | // 5 | // Created by 胡怀刈 on 2023/4/11. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/JKRLabel/JKRAutoScrollLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // JKRAutoScrollLabel.h 3 | // XMScrollCanvas 4 | // 5 | // Created by Howie on 2021/10/27. 6 | // Copyright © 2021 wxm. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface JKRAutoScrollLabel : UILabel 14 | 15 | @property (nonatomic, assign) NSTimeInterval scrollDelay; 16 | 17 | @property (nonatomic, strong, nullable) UIImage *mask; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/JKRLabel/JKRShimmeringLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // JKRShimmeringLabel.h 3 | // SoldierShimmering 4 | // 5 | // Created by 胡怀刈 on 2022/12/7. 6 | // Copyright © 2022 Soldier. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface JKRShimmeringLabel : UILabel 14 | 15 | @property (nonatomic, strong, nullable) UIImage *mask; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // JKRShimmeringLabel 4 | // 5 | // Created by 胡怀刈 on 2023/4/11. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSBundle+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/20. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `NSBundle` to get resource by @2x or @3x... 18 | 19 | Example: ico.png, ico@2x.png, ico@3x.png. Call scaledResource:@"ico" ofType:@"png" 20 | on iPhone6 will return "ico@2x.png"'s path. 21 | */ 22 | @interface NSBundle (YYAdd) 23 | 24 | /** 25 | An array of NSNumber objects, shows the best order for path scale search. 26 | e.g. iPhone3GS:@[@1,@2,@3] iPhone5:@[@2,@3,@1] iPhone6 Plus:@[@3,@2,@1] 27 | */ 28 | + (NSArray *)preferredScales; 29 | 30 | /** 31 | Returns the full pathname for the resource file identified by the specified 32 | name and extension and residing in a given bundle directory. It first search 33 | the file with current screen's scale (such as @2x), then search from higher 34 | scale to lower scale. 35 | 36 | @param name The name of a resource file contained in the directory 37 | specified by bundlePath. 38 | 39 | @param ext If extension is an empty string or nil, the extension is 40 | assumed not to exist and the file is the first file encountered that exactly matches name. 41 | 42 | @param bundlePath The path of a top-level bundle directory. This must be a 43 | valid path. For example, to specify the bundle directory for a Mac app, you 44 | might specify the path /Applications/MyApp.app. 45 | 46 | @return The full pathname for the resource file or nil if the file could not be 47 | located. This method also returns nil if the bundle specified by the bundlePath 48 | parameter does not exist or is not a readable directory. 49 | */ 50 | + (nullable NSString *)pathForScaledResource:(NSString *)name 51 | ofType:(nullable NSString *)ext 52 | inDirectory:(NSString *)bundlePath; 53 | 54 | /** 55 | Returns the full pathname for the resource identified by the specified name and 56 | file extension. It first search the file with current screen's scale (such as @2x), 57 | then search from higher scale to lower scale. 58 | 59 | @param name The name of the resource file. If name is an empty string or 60 | nil, returns the first file encountered of the supplied type. 61 | 62 | @param ext If extension is an empty string or nil, the extension is 63 | assumed not to exist and the file is the first file encountered that exactly matches name. 64 | 65 | 66 | @return The full pathname for the resource file or nil if the file could not be located. 67 | */ 68 | - (nullable NSString *)pathForScaledResource:(NSString *)name ofType:(nullable NSString *)ext; 69 | 70 | /** 71 | Returns the full pathname for the resource identified by the specified name and 72 | file extension and located in the specified bundle subdirectory. It first search 73 | the file with current screen's scale (such as @2x), then search from higher 74 | scale to lower scale. 75 | 76 | @param name The name of the resource file. 77 | 78 | @param ext If extension is an empty string or nil, all the files in 79 | subpath and its subdirectories are returned. If an extension is provided the 80 | subdirectories are not searched. 81 | 82 | @param subpath The name of the bundle subdirectory. Can be nil. 83 | 84 | @return The full pathname for the resource file or nil if the file could not be located. 85 | */ 86 | - (nullable NSString *)pathForScaledResource:(NSString *)name 87 | ofType:(nullable NSString *)ext 88 | inDirectory:(nullable NSString *)subpath; 89 | 90 | @end 91 | 92 | NS_ASSUME_NONNULL_END 93 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSBundle+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/20. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSBundle+YYAdd.h" 13 | #import "NSString+YYAdd.h" 14 | #import "YYKitMacro.h" 15 | 16 | YYSYNTH_DUMMY_CLASS(NSBundle_YYAdd) 17 | 18 | @implementation NSBundle (YYAdd) 19 | 20 | + (NSArray *)preferredScales { 21 | static NSArray *scales; 22 | static dispatch_once_t onceToken; 23 | dispatch_once(&onceToken, ^{ 24 | CGFloat screenScale = [UIScreen mainScreen].scale; 25 | if (screenScale <= 1) { 26 | scales = @[@1,@2,@3]; 27 | } else if (screenScale <= 2) { 28 | scales = @[@2,@3,@1]; 29 | } else { 30 | scales = @[@3,@2,@1]; 31 | } 32 | }); 33 | return scales; 34 | } 35 | 36 | + (NSString *)pathForScaledResource:(NSString *)name ofType:(NSString *)ext inDirectory:(NSString *)bundlePath { 37 | if (name.length == 0) return nil; 38 | if ([name hasSuffix:@"/"]) return [self pathForResource:name ofType:ext inDirectory:bundlePath]; 39 | 40 | NSString *path = nil; 41 | NSArray *scales = [self preferredScales]; 42 | for (int s = 0; s < scales.count; s++) { 43 | CGFloat scale = ((NSNumber *)scales[s]).floatValue; 44 | NSString *scaledName = ext.length ? [name stringByAppendingNameScale:scale] 45 | : [name stringByAppendingPathScale:scale]; 46 | path = [self pathForResource:scaledName ofType:ext inDirectory:bundlePath]; 47 | if (path) break; 48 | } 49 | 50 | return path; 51 | } 52 | 53 | - (NSString *)pathForScaledResource:(NSString *)name ofType:(NSString *)ext { 54 | if (name.length == 0) return nil; 55 | if ([name hasSuffix:@"/"]) return [self pathForResource:name ofType:ext]; 56 | 57 | NSString *path = nil; 58 | NSArray *scales = [NSBundle preferredScales]; 59 | for (int s = 0; s < scales.count; s++) { 60 | CGFloat scale = ((NSNumber *)scales[s]).floatValue; 61 | NSString *scaledName = ext.length ? [name stringByAppendingNameScale:scale] 62 | : [name stringByAppendingPathScale:scale]; 63 | path = [self pathForResource:scaledName ofType:ext]; 64 | if (path) break; 65 | } 66 | 67 | return path; 68 | } 69 | 70 | - (NSString *)pathForScaledResource:(NSString *)name ofType:(NSString *)ext inDirectory:(NSString *)subpath { 71 | if (name.length == 0) return nil; 72 | if ([name hasSuffix:@"/"]) return [self pathForResource:name ofType:ext]; 73 | 74 | NSString *path = nil; 75 | NSArray *scales = [NSBundle preferredScales]; 76 | for (int s = 0; s < scales.count; s++) { 77 | CGFloat scale = ((NSNumber *)scales[s]).floatValue; 78 | NSString *scaledName = ext.length ? [name stringByAppendingNameScale:scale] 79 | : [name stringByAppendingPathScale:scale]; 80 | path = [self pathForResource:scaledName ofType:ext inDirectory:subpath]; 81 | if (path) break; 82 | } 83 | 84 | return path; 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSKeyedUnarchiver+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSKeyedUnarchiver+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/8/4. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `NSKeyedUnarchiver`. 18 | */ 19 | @interface NSKeyedUnarchiver (YYAdd) 20 | 21 | /** 22 | Same as unarchiveObjectWithData:, except it returns the exception by reference. 23 | 24 | @param data The data need unarchived. 25 | 26 | @param exception Pointer which will, upon return, if an exception occurred and 27 | said pointer is not NULL, point to said NSException. 28 | */ 29 | + (nullable id)unarchiveObjectWithData:(NSData *)data 30 | exception:(NSException *_Nullable *_Nullable)exception; 31 | 32 | /** 33 | Same as unarchiveObjectWithFile:, except it returns the exception by reference. 34 | 35 | @param path The path of archived object file. 36 | 37 | @param exception Pointer which will, upon return, if an exception occurred and 38 | said pointer is not NULL, point to said NSException. 39 | */ 40 | + (nullable id)unarchiveObjectWithFile:(NSString *)path 41 | exception:(NSException *_Nullable *_Nullable)exception; 42 | 43 | @end 44 | 45 | NS_ASSUME_NONNULL_END 46 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSKeyedUnarchiver+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSKeyedUnarchiver+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/8/4. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSKeyedUnarchiver+YYAdd.h" 13 | #import "YYKitMacro.h" 14 | 15 | YYSYNTH_DUMMY_CLASS(NSKeyedUnarchiver_YYAdd) 16 | 17 | 18 | @implementation NSKeyedUnarchiver (YYAdd) 19 | 20 | + (id)unarchiveObjectWithData:(NSData *)data exception:(__autoreleasing NSException **)exception { 21 | id object = nil; 22 | @try { 23 | object = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 24 | } 25 | @catch (NSException *e) 26 | { 27 | if (exception) *exception = e; 28 | } 29 | @finally 30 | { 31 | } 32 | return object; 33 | } 34 | 35 | + (id)unarchiveObjectWithFile:(NSString *)path exception:(__autoreleasing NSException **)exception { 36 | id object = nil; 37 | 38 | @try { 39 | object = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; 40 | } 41 | @catch (NSException *e) 42 | { 43 | if (exception) *exception = e; 44 | } 45 | @finally 46 | { 47 | } 48 | return object; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSNotificationCenter+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/8/24. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provide some method for `NSNotificationCenter` 18 | to post notification in different thread. 19 | */ 20 | @interface NSNotificationCenter (YYAdd) 21 | 22 | /** 23 | Posts a given notification to the receiver on main thread. 24 | If current thread is main thread, the notification is posted synchronously; 25 | otherwise, is posted asynchronously. 26 | 27 | @param notification The notification to post. 28 | An exception is raised if notification is nil. 29 | */ 30 | - (void)postNotificationOnMainThread:(NSNotification *)notification; 31 | 32 | /** 33 | Posts a given notification to the receiver on main thread. 34 | 35 | @param notification The notification to post. 36 | An exception is raised if notification is nil. 37 | 38 | @param wait A Boolean that specifies whether the current thread blocks 39 | until after the specified notification is posted on the 40 | receiver on the main thread. Specify YES to block this 41 | thread; otherwise, specify NO to have this method return 42 | immediately. 43 | */ 44 | - (void)postNotificationOnMainThread:(NSNotification *)notification 45 | waitUntilDone:(BOOL)wait; 46 | 47 | /** 48 | Creates a notification with a given name and sender and posts it to the 49 | receiver on main thread. If current thread is main thread, the notification 50 | is posted synchronously; otherwise, is posted asynchronously. 51 | 52 | @param name The name of the notification. 53 | 54 | @param object The object posting the notification. 55 | */ 56 | - (void)postNotificationOnMainThreadWithName:(NSString *)name 57 | object:(nullable id)object; 58 | 59 | /** 60 | Creates a notification with a given name and sender and posts it to the 61 | receiver on main thread. If current thread is main thread, the notification 62 | is posted synchronously; otherwise, is posted asynchronously. 63 | 64 | @param name The name of the notification. 65 | 66 | @param object The object posting the notification. 67 | 68 | @param userInfo Information about the the notification. May be nil. 69 | */ 70 | - (void)postNotificationOnMainThreadWithName:(NSString *)name 71 | object:(nullable id)object 72 | userInfo:(nullable NSDictionary *)userInfo; 73 | 74 | /** 75 | Creates a notification with a given name and sender and posts it to the 76 | receiver on main thread. 77 | 78 | @param name The name of the notification. 79 | 80 | @param object The object posting the notification. 81 | 82 | @param userInfo Information about the the notification. May be nil. 83 | 84 | @param wait A Boolean that specifies whether the current thread blocks 85 | until after the specified notification is posted on the 86 | receiver on the main thread. Specify YES to block this 87 | thread; otherwise, specify NO to have this method return 88 | immediately. 89 | */ 90 | - (void)postNotificationOnMainThreadWithName:(NSString *)name 91 | object:(nullable id)object 92 | userInfo:(nullable NSDictionary *)userInfo 93 | waitUntilDone:(BOOL)wait; 94 | 95 | @end 96 | 97 | NS_ASSUME_NONNULL_END 98 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSNotificationCenter+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/8/24. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSNotificationCenter+YYAdd.h" 13 | #include 14 | #import "YYKitMacro.h" 15 | 16 | YYSYNTH_DUMMY_CLASS(NSNotificationCenter_YYAdd) 17 | 18 | 19 | @implementation NSNotificationCenter (YYAdd) 20 | 21 | - (void)postNotificationOnMainThread:(NSNotification *)notification { 22 | if (pthread_main_np()) return [self postNotification:notification]; 23 | [self postNotificationOnMainThread:notification waitUntilDone:NO]; 24 | } 25 | 26 | - (void)postNotificationOnMainThread:(NSNotification *)notification waitUntilDone:(BOOL)wait { 27 | if (pthread_main_np()) return [self postNotification:notification]; 28 | [[self class] performSelectorOnMainThread:@selector(_yy_postNotification:) withObject:notification waitUntilDone:wait]; 29 | } 30 | 31 | - (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object { 32 | if (pthread_main_np()) return [self postNotificationName:name object:object userInfo:nil]; 33 | [self postNotificationOnMainThreadWithName:name object:object userInfo:nil waitUntilDone:NO]; 34 | } 35 | 36 | - (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo { 37 | if (pthread_main_np()) return [self postNotificationName:name object:object userInfo:userInfo]; 38 | [self postNotificationOnMainThreadWithName:name object:object userInfo:userInfo waitUntilDone:NO]; 39 | } 40 | 41 | - (void)postNotificationOnMainThreadWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo waitUntilDone:(BOOL)wait { 42 | if (pthread_main_np()) return [self postNotificationName:name object:object userInfo:userInfo]; 43 | NSMutableDictionary *info = [[NSMutableDictionary allocWithZone:nil] initWithCapacity:3]; 44 | if (name) [info setObject:name forKey:@"name"]; 45 | if (object) [info setObject:object forKey:@"object"]; 46 | if (userInfo) [info setObject:userInfo forKey:@"userInfo"]; 47 | [[self class] performSelectorOnMainThread:@selector(_yy_postNotificationName:) withObject:info waitUntilDone:wait]; 48 | } 49 | 50 | + (void)_yy_postNotification:(NSNotification *)notification { 51 | [[self defaultCenter] postNotification:notification]; 52 | } 53 | 54 | + (void)_yy_postNotificationName:(NSDictionary *)info { 55 | NSString *name = [info objectForKey:@"name"]; 56 | id object = [info objectForKey:@"object"]; 57 | NSDictionary *userInfo = [info objectForKey:@"userInfo"]; 58 | 59 | [[self defaultCenter] postNotificationName:name object:object userInfo:userInfo]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSNumber+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/8/24. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provide a method to parse `NSString` for `NSNumber`. 18 | */ 19 | @interface NSNumber (YYAdd) 20 | 21 | /** 22 | Creates and returns an NSNumber object from a string. 23 | Valid format: @"12", @"12.345", @" -0xFF", @" .23e99 "... 24 | 25 | @param string The string described an number. 26 | 27 | @return an NSNumber when parse succeed, or nil if an error occurs. 28 | */ 29 | + (nullable NSNumber *)numberWithString:(NSString *)string; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSNumber+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/8/24. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSNumber+YYAdd.h" 13 | #import "NSString+YYAdd.h" 14 | #import "YYKitMacro.h" 15 | 16 | YYSYNTH_DUMMY_CLASS(NSNumber_YYAdd) 17 | 18 | 19 | @implementation NSNumber (YYAdd) 20 | 21 | + (NSNumber *)numberWithString:(NSString *)string { 22 | NSString *str = [[string stringByTrim] lowercaseString]; 23 | if (!str || !str.length) { 24 | return nil; 25 | } 26 | 27 | static NSDictionary *dic; 28 | static dispatch_once_t onceToken; 29 | dispatch_once(&onceToken, ^{ 30 | dic = @{@"true" : @(YES), 31 | @"yes" : @(YES), 32 | @"false" : @(NO), 33 | @"no" : @(NO), 34 | @"nil" : [NSNull null], 35 | @"null" : [NSNull null], 36 | @"" : [NSNull null]}; 37 | }); 38 | id num = dic[str]; 39 | if (num) { 40 | if (num == [NSNull null]) return nil; 41 | return num; 42 | } 43 | 44 | // hex number 45 | int sign = 0; 46 | if ([str hasPrefix:@"0x"]) sign = 1; 47 | else if ([str hasPrefix:@"-0x"]) sign = -1; 48 | if (sign != 0) { 49 | NSScanner *scan = [NSScanner scannerWithString:str]; 50 | unsigned num = -1; 51 | BOOL suc = [scan scanHexInt:&num]; 52 | if (suc) 53 | return [NSNumber numberWithLong:((long)num * sign)]; 54 | else 55 | return nil; 56 | } 57 | // normal number 58 | NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 59 | [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; 60 | return [formatter numberFromString:string]; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSObject+YYAddForARC.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYAddForARC.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/12/15. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | /** 15 | Debug method for NSObject when using ARC. 16 | */ 17 | @interface NSObject (YYAddForARC) 18 | 19 | /// Same as `retain` 20 | - (instancetype)arcDebugRetain; 21 | 22 | /// Same as `release` 23 | - (oneway void)arcDebugRelease; 24 | 25 | /// Same as `autorelease` 26 | - (instancetype)arcDebugAutorelease; 27 | 28 | /// Same as `retainCount` 29 | - (NSUInteger)arcDebugRetainCount; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSObject+YYAddForARC.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYAddForARC.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/12/15. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSObject+YYAddForARC.h" 13 | 14 | @interface NSObject_YYAddForARC : NSObject @end 15 | @implementation NSObject_YYAddForARC @end 16 | 17 | #if __has_feature(objc_arc) 18 | #error This file must be compiled without ARC. Specify the -fno-objc-arc flag to this file. 19 | #endif 20 | 21 | 22 | @implementation NSObject (YYAddForARC) 23 | 24 | - (instancetype)arcDebugRetain { 25 | return [self retain]; 26 | } 27 | 28 | - (oneway void)arcDebugRelease { 29 | [self release]; 30 | } 31 | 32 | - (instancetype)arcDebugAutorelease { 33 | return [self autorelease]; 34 | } 35 | 36 | - (NSUInteger)arcDebugRetainCount { 37 | return [self retainCount]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSObject+YYAddForKVO.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYAddForKVO.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/15. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Observer with block (KVO). 18 | */ 19 | @interface NSObject (YYAddForKVO) 20 | 21 | /** 22 | Registers a block to receive KVO notifications for the specified key-path 23 | relative to the receiver. 24 | 25 | @discussion The block and block captured objects are retained. Call 26 | `removeObserverBlocksForKeyPath:` or `removeObserverBlocks` to release. 27 | 28 | @param keyPath The key path, relative to the receiver, of the property to 29 | observe. This value must not be nil. 30 | 31 | @param block The block to register for KVO notifications. 32 | */ 33 | - (void)addObserverBlockForKeyPath:(NSString*)keyPath block:(void (^)(id _Nonnull obj, _Nullable id oldVal, _Nullable id newVal))block; 34 | 35 | /** 36 | Stops all blocks (associated by `addObserverBlockForKeyPath:block:`) from 37 | receiving change notifications for the property specified by a given key-path 38 | relative to the receiver, and release these blocks. 39 | 40 | @param keyPath A key-path, relative to the receiver, for which blocks is 41 | registered to receive KVO change notifications. 42 | */ 43 | - (void)removeObserverBlocksForKeyPath:(NSString*)keyPath; 44 | 45 | /** 46 | Stops all blocks (associated by `addObserverBlockForKeyPath:block:`) from 47 | receiving change notifications, and release these blocks. 48 | */ 49 | - (void)removeObserverBlocks; 50 | 51 | @end 52 | 53 | NS_ASSUME_NONNULL_END 54 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSObject+YYAddForKVO.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYAddForKVO.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/15. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSObject+YYAddForKVO.h" 13 | #import "YYKitMacro.h" 14 | #import 15 | #import 16 | 17 | YYSYNTH_DUMMY_CLASS(NSObject_YYAddForKVO) 18 | 19 | 20 | 21 | 22 | static const int block_key; 23 | 24 | @interface _YYNSObjectKVOBlockTarget : NSObject 25 | 26 | @property (nonatomic, copy) void (^block)(__weak id obj, id oldVal, id newVal); 27 | 28 | - (id)initWithBlock:(void (^)(__weak id obj, id oldVal, id newVal))block; 29 | 30 | @end 31 | 32 | @implementation _YYNSObjectKVOBlockTarget 33 | 34 | - (id)initWithBlock:(void (^)(__weak id obj, id oldVal, id newVal))block { 35 | self = [super init]; 36 | if (self) { 37 | self.block = block; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 43 | if (!self.block) return; 44 | 45 | BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]; 46 | if (isPrior) return; 47 | 48 | NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue]; 49 | if (changeKind != NSKeyValueChangeSetting) return; 50 | 51 | id oldVal = [change objectForKey:NSKeyValueChangeOldKey]; 52 | if (oldVal == [NSNull null]) oldVal = nil; 53 | 54 | id newVal = [change objectForKey:NSKeyValueChangeNewKey]; 55 | if (newVal == [NSNull null]) newVal = nil; 56 | 57 | self.block(object, oldVal, newVal); 58 | } 59 | 60 | @end 61 | 62 | 63 | 64 | @implementation NSObject (YYAddForKVO) 65 | 66 | - (void)addObserverBlockForKeyPath:(NSString *)keyPath block:(void (^)(__weak id obj, id oldVal, id newVal))block { 67 | if (!keyPath || !block) return; 68 | _YYNSObjectKVOBlockTarget *target = [[_YYNSObjectKVOBlockTarget alloc] initWithBlock:block]; 69 | NSMutableDictionary *dic = [self _yy_allNSObjectObserverBlocks]; 70 | NSMutableArray *arr = dic[keyPath]; 71 | if (!arr) { 72 | arr = [NSMutableArray new]; 73 | dic[keyPath] = arr; 74 | } 75 | [arr addObject:target]; 76 | [self addObserver:target forKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL]; 77 | } 78 | 79 | - (void)removeObserverBlocksForKeyPath:(NSString *)keyPath { 80 | if (!keyPath) return; 81 | NSMutableDictionary *dic = [self _yy_allNSObjectObserverBlocks]; 82 | NSMutableArray *arr = dic[keyPath]; 83 | [arr enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { 84 | [self removeObserver:obj forKeyPath:keyPath]; 85 | }]; 86 | 87 | [dic removeObjectForKey:keyPath]; 88 | } 89 | 90 | - (void)removeObserverBlocks { 91 | NSMutableDictionary *dic = [self _yy_allNSObjectObserverBlocks]; 92 | [dic enumerateKeysAndObjectsUsingBlock: ^(NSString *key, NSArray *arr, BOOL *stop) { 93 | [arr enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { 94 | [self removeObserver:obj forKeyPath:key]; 95 | }]; 96 | }]; 97 | 98 | [dic removeAllObjects]; 99 | } 100 | 101 | - (NSMutableDictionary *)_yy_allNSObjectObserverBlocks { 102 | NSMutableDictionary *targets = objc_getAssociatedObject(self, &block_key); 103 | if (!targets) { 104 | targets = [NSMutableDictionary new]; 105 | objc_setAssociatedObject(self, &block_key, targets, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 106 | } 107 | return targets; 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSThread+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSThread+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/7/3. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | @interface NSThread (YYAdd) 15 | 16 | /** 17 | Add an autorelease pool to current runloop for current thread. 18 | 19 | @discussion If you create your own thread (NSThread/pthread), and you use 20 | runloop to manage your task, you may use this method to add an autorelease pool 21 | to the runloop. Its behavior is the same as the main thread's autorelease pool. 22 | */ 23 | + (void)addAutoreleasePoolToCurrentRunloop; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSThread+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSThread+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/7/3. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSThread+YYAdd.h" 13 | #import 14 | 15 | @interface NSThread_YYAdd : NSObject @end 16 | @implementation NSThread_YYAdd @end 17 | 18 | #if __has_feature(objc_arc) 19 | #error This file must be compiled without ARC. Specify the -fno-objc-arc flag to this file. 20 | #endif 21 | 22 | static NSString *const YYNSThreadAutoleasePoolKey = @"YYNSThreadAutoleasePoolKey"; 23 | static NSString *const YYNSThreadAutoleasePoolStackKey = @"YYNSThreadAutoleasePoolStackKey"; 24 | 25 | static const void *PoolStackRetainCallBack(CFAllocatorRef allocator, const void *value) { 26 | return value; 27 | } 28 | 29 | static void PoolStackReleaseCallBack(CFAllocatorRef allocator, const void *value) { 30 | CFRelease((CFTypeRef)value); 31 | } 32 | 33 | 34 | static inline void YYAutoreleasePoolPush() { 35 | NSMutableDictionary *dic = [NSThread currentThread].threadDictionary; 36 | NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey]; 37 | 38 | if (!poolStack) { 39 | /* 40 | do not retain pool on push, 41 | but release on pop to avoid memory analyze warning 42 | */ 43 | CFArrayCallBacks callbacks = {0}; 44 | callbacks.retain = PoolStackRetainCallBack; 45 | callbacks.release = PoolStackReleaseCallBack; 46 | poolStack = (id)CFArrayCreateMutable(CFAllocatorGetDefault(), 0, &callbacks); 47 | dic[YYNSThreadAutoleasePoolStackKey] = poolStack; 48 | CFRelease(poolStack); 49 | } 50 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; ///< create 51 | [poolStack addObject:pool]; // push 52 | } 53 | 54 | static inline void YYAutoreleasePoolPop() { 55 | NSMutableDictionary *dic = [NSThread currentThread].threadDictionary; 56 | NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey]; 57 | [poolStack removeLastObject]; // pop 58 | } 59 | 60 | static void YYRunLoopAutoreleasePoolObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { 61 | switch (activity) { 62 | case kCFRunLoopEntry: { 63 | YYAutoreleasePoolPush(); 64 | } break; 65 | case kCFRunLoopBeforeWaiting: { 66 | YYAutoreleasePoolPop(); 67 | YYAutoreleasePoolPush(); 68 | } break; 69 | case kCFRunLoopExit: { 70 | YYAutoreleasePoolPop(); 71 | } break; 72 | default: break; 73 | } 74 | } 75 | 76 | static void YYRunloopAutoreleasePoolSetup() { 77 | CFRunLoopRef runloop = CFRunLoopGetCurrent(); 78 | 79 | CFRunLoopObserverRef pushObserver; 80 | pushObserver = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopEntry, 81 | true, // repeat 82 | -0x7FFFFFFF, // before other observers 83 | YYRunLoopAutoreleasePoolObserverCallBack, NULL); 84 | CFRunLoopAddObserver(runloop, pushObserver, kCFRunLoopCommonModes); 85 | CFRelease(pushObserver); 86 | 87 | CFRunLoopObserverRef popObserver; 88 | popObserver = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopBeforeWaiting | kCFRunLoopExit, 89 | true, // repeat 90 | 0x7FFFFFFF, // after other observers 91 | YYRunLoopAutoreleasePoolObserverCallBack, NULL); 92 | CFRunLoopAddObserver(runloop, popObserver, kCFRunLoopCommonModes); 93 | CFRelease(popObserver); 94 | } 95 | 96 | @implementation NSThread (YYAdd) 97 | 98 | + (void)addAutoreleasePoolToCurrentRunloop { 99 | if ([NSThread isMainThread]) return; // The main thread already has autorelease pool. 100 | NSThread *thread = [self currentThread]; 101 | if (!thread) return; 102 | if (thread.threadDictionary[YYNSThreadAutoleasePoolKey]) return; // already added 103 | YYRunloopAutoreleasePoolSetup(); 104 | thread.threadDictionary[YYNSThreadAutoleasePoolKey] = YYNSThreadAutoleasePoolKey; // mark the state 105 | } 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSTimer+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/15/11. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `NSTimer`. 18 | */ 19 | @interface NSTimer (YYAdd) 20 | 21 | /** 22 | Creates and returns a new NSTimer object and schedules it on the current run 23 | loop in the default mode. 24 | 25 | @discussion After seconds seconds have elapsed, the timer fires, 26 | sending the message aSelector to target. 27 | 28 | @param seconds The number of seconds between firings of the timer. If seconds 29 | is less than or equal to 0.0, this method chooses the 30 | nonnegative value of 0.1 milliseconds instead. 31 | 32 | @param block The block to invoke when the timer fires. The timer maintains 33 | a strong reference to the block until it (the timer) is invalidated. 34 | 35 | @param repeats If YES, the timer will repeatedly reschedule itself until 36 | invalidated. If NO, the timer will be invalidated after it fires. 37 | 38 | @return A new NSTimer object, configured according to the specified parameters. 39 | */ 40 | + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats; 41 | 42 | /** 43 | Creates and returns a new NSTimer object initialized with the specified block. 44 | 45 | @discussion You must add the new timer to a run loop, using addTimer:forMode:. 46 | Then, after seconds have elapsed, the timer fires, invoking 47 | block. (If the timer is configured to repeat, there is no need 48 | to subsequently re-add the timer to the run loop.) 49 | 50 | @param seconds The number of seconds between firings of the timer. If seconds 51 | is less than or equal to 0.0, this method chooses the 52 | nonnegative value of 0.1 milliseconds instead. 53 | 54 | @param block The block to invoke when the timer fires. The timer instructs 55 | the block to maintain a strong reference to its arguments. 56 | 57 | @param repeats If YES, the timer will repeatedly reschedule itself until 58 | invalidated. If NO, the timer will be invalidated after it fires. 59 | 60 | @return A new NSTimer object, configured according to the specified parameters. 61 | */ 62 | + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats; 63 | 64 | @end 65 | 66 | NS_ASSUME_NONNULL_END 67 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Foundation/NSTimer+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/15/11. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "NSTimer+YYAdd.h" 13 | #import "YYKitMacro.h" 14 | 15 | YYSYNTH_DUMMY_CLASS(NSTimer_YYAdd) 16 | 17 | 18 | @implementation NSTimer (YYAdd) 19 | 20 | + (void)_yy_ExecBlock:(NSTimer *)timer { 21 | if ([timer userInfo]) { 22 | void (^block)(NSTimer *timer) = (void (^)(NSTimer *timer))[timer userInfo]; 23 | block(timer); 24 | } 25 | } 26 | 27 | + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats { 28 | return [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(_yy_ExecBlock:) userInfo:[block copy] repeats:repeats]; 29 | } 30 | 31 | + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats { 32 | return [NSTimer timerWithTimeInterval:seconds target:self selector:@selector(_yy_ExecBlock:) userInfo:[block copy] repeats:repeats]; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/Quartz/CALayer+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/5/10. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | /** 18 | Provides extensions for `CALayer`. 19 | */ 20 | @interface CALayer (YYAdd) 21 | 22 | /** 23 | Take snapshot without transform, image's size equals to bounds. 24 | */ 25 | - (nullable UIImage *)snapshotImage; 26 | 27 | /** 28 | Take snapshot without transform, PDF's page size equals to bounds. 29 | */ 30 | - (nullable NSData *)snapshotPDF; 31 | 32 | /** 33 | Shortcut to set the layer's shadow 34 | 35 | @param color Shadow Color 36 | @param offset Shadow offset 37 | @param radius Shadow radius 38 | */ 39 | - (void)setLayerShadow:(UIColor*)color offset:(CGSize)offset radius:(CGFloat)radius; 40 | 41 | /** 42 | Remove all sublayers. 43 | */ 44 | - (void)removeAllSublayers; 45 | 46 | @property (nonatomic) CGFloat left; ///< Shortcut for frame.origin.x. 47 | @property (nonatomic) CGFloat top; ///< Shortcut for frame.origin.y 48 | @property (nonatomic) CGFloat right; ///< Shortcut for frame.origin.x + frame.size.width 49 | @property (nonatomic) CGFloat bottom; ///< Shortcut for frame.origin.y + frame.size.height 50 | @property (nonatomic) CGFloat width; ///< Shortcut for frame.size.width. 51 | @property (nonatomic) CGFloat height; ///< Shortcut for frame.size.height. 52 | @property (nonatomic) CGPoint center; ///< Shortcut for center. 53 | @property (nonatomic) CGFloat centerX; ///< Shortcut for center.x 54 | @property (nonatomic) CGFloat centerY; ///< Shortcut for center.y 55 | @property (nonatomic) CGPoint origin; ///< Shortcut for frame.origin. 56 | @property (nonatomic, getter=frameSize, setter=setFrameSize:) CGSize size; ///< Shortcut for frame.size. 57 | 58 | 59 | @property (nonatomic) CGFloat transformRotation; ///< key path "tranform.rotation" 60 | @property (nonatomic) CGFloat transformRotationX; ///< key path "tranform.rotation.x" 61 | @property (nonatomic) CGFloat transformRotationY; ///< key path "tranform.rotation.y" 62 | @property (nonatomic) CGFloat transformRotationZ; ///< key path "tranform.rotation.z" 63 | @property (nonatomic) CGFloat transformScale; ///< key path "tranform.scale" 64 | @property (nonatomic) CGFloat transformScaleX; ///< key path "tranform.scale.x" 65 | @property (nonatomic) CGFloat transformScaleY; ///< key path "tranform.scale.y" 66 | @property (nonatomic) CGFloat transformScaleZ; ///< key path "tranform.scale.z" 67 | @property (nonatomic) CGFloat transformTranslationX; ///< key path "tranform.translation.x" 68 | @property (nonatomic) CGFloat transformTranslationY; ///< key path "tranform.translation.y" 69 | @property (nonatomic) CGFloat transformTranslationZ; ///< key path "tranform.translation.z" 70 | 71 | /** 72 | Shortcut for transform.m34, -1/1000 is a good value. 73 | It should be set before other transform shortcut. 74 | */ 75 | @property (nonatomic) CGFloat transformDepth; 76 | 77 | /** 78 | Wrapper for `contentsGravity` property. 79 | */ 80 | @property (nonatomic) UIViewContentMode contentMode; 81 | 82 | /** 83 | Add a fade animation to layer's contents when the contents is changed. 84 | 85 | @param duration Animation duration 86 | @param curve Animation curve. 87 | */ 88 | - (void)addFadeAnimationWithDuration:(NSTimeInterval)duration curve:(UIViewAnimationCurve)curve; 89 | 90 | /** 91 | Cancel fade animation which is added with "-addFadeAnimationWithDuration:curve:". 92 | */ 93 | - (void)removePreviousFadeAnimation; 94 | 95 | @end 96 | 97 | NS_ASSUME_NONNULL_END 98 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIApplication+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/4/4. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `UIApplication`. 18 | */ 19 | @interface UIApplication (YYAdd) 20 | 21 | /// "Documents" folder in this app's sandbox. 22 | @property (nonatomic, readonly) NSURL *documentsURL; 23 | @property (nonatomic, readonly) NSString *documentsPath; 24 | 25 | /// "Caches" folder in this app's sandbox. 26 | @property (nonatomic, readonly) NSURL *cachesURL; 27 | @property (nonatomic, readonly) NSString *cachesPath; 28 | 29 | /// "Library" folder in this app's sandbox. 30 | @property (nonatomic, readonly) NSURL *libraryURL; 31 | @property (nonatomic, readonly) NSString *libraryPath; 32 | 33 | /// Application's Bundle Name (show in SpringBoard). 34 | @property (nullable, nonatomic, readonly) NSString *appBundleName; 35 | 36 | /// Application's Bundle ID. e.g. "com.ibireme.MyApp" 37 | @property (nullable, nonatomic, readonly) NSString *appBundleID; 38 | 39 | /// Application's Version. e.g. "1.2.0" 40 | @property (nullable, nonatomic, readonly) NSString *appVersion; 41 | 42 | /// Application's Build number. e.g. "123" 43 | @property (nullable, nonatomic, readonly) NSString *appBuildVersion; 44 | 45 | /// Whether this app is pirated (not install from appstore). 46 | @property (nonatomic, readonly) BOOL isPirated; 47 | 48 | /// Whether this app is being debugged (debugger attached). 49 | @property (nonatomic, readonly) BOOL isBeingDebugged; 50 | 51 | /// Current thread real memory used in byte. (-1 when error occurs) 52 | @property (nonatomic, readonly) int64_t memoryUsage; 53 | 54 | /// Current thread CPU usage, 1.0 means 100%. (-1 when error occurs) 55 | @property (nonatomic, readonly) float cpuUsage; 56 | 57 | 58 | /** 59 | Increments the number of active network requests. 60 | If this number was zero before incrementing, this will start animating the 61 | status bar network activity indicator. 62 | 63 | This method is thread safe. 64 | 65 | This method has no effect in App Extension. 66 | */ 67 | - (void)incrementNetworkActivityCount; 68 | 69 | /** 70 | Decrements the number of active network requests. 71 | If this number becomes zero after decrementing, this will stop animating the 72 | status bar network activity indicator. 73 | 74 | This method is thread safe. 75 | 76 | This method has no effect in App Extension. 77 | */ 78 | - (void)decrementNetworkActivityCount; 79 | 80 | 81 | /// Returns YES in App Extension. 82 | + (BOOL)isAppExtension; 83 | 84 | /// Same as sharedApplication, but returns nil in App Extension. 85 | + (nullable UIApplication *)sharedExtensionApplication; 86 | 87 | @end 88 | 89 | NS_ASSUME_NONNULL_END 90 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIBarButtonItem+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItem+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/10/15. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `UIBarButtonItem`. 18 | */ 19 | @interface UIBarButtonItem (YYAdd) 20 | 21 | /** 22 | The block that invoked when the item is selected. The objects captured by block 23 | will retained by the ButtonItem. 24 | 25 | @discussion This param is conflict with `target` and `action` property. 26 | Set this will set `target` and `action` property to some internal objects. 27 | */ 28 | @property (nullable, nonatomic, copy) void (^actionBlock)(id); 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIBarButtonItem+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItem+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/10/15. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "UIBarButtonItem+YYAdd.h" 13 | #import "YYKitMacro.h" 14 | #import 15 | 16 | YYSYNTH_DUMMY_CLASS(UIBarButtonItem_YYAdd) 17 | 18 | 19 | static const int block_key; 20 | 21 | @interface _YYUIBarButtonItemBlockTarget : NSObject 22 | 23 | @property (nonatomic, copy) void (^block)(id sender); 24 | 25 | - (id)initWithBlock:(void (^)(id sender))block; 26 | - (void)invoke:(id)sender; 27 | 28 | @end 29 | 30 | @implementation _YYUIBarButtonItemBlockTarget 31 | 32 | - (id)initWithBlock:(void (^)(id sender))block{ 33 | self = [super init]; 34 | if (self) { 35 | _block = [block copy]; 36 | } 37 | return self; 38 | } 39 | 40 | - (void)invoke:(id)sender { 41 | if (self.block) self.block(sender); 42 | } 43 | 44 | @end 45 | 46 | 47 | @implementation UIBarButtonItem (YYAdd) 48 | 49 | - (void)setActionBlock:(void (^)(id sender))block { 50 | _YYUIBarButtonItemBlockTarget *target = [[_YYUIBarButtonItemBlockTarget alloc] initWithBlock:block]; 51 | objc_setAssociatedObject(self, &block_key, target, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 52 | 53 | [self setTarget:target]; 54 | [self setAction:@selector(invoke:)]; 55 | } 56 | 57 | - (void (^)(id)) actionBlock { 58 | _YYUIBarButtonItemBlockTarget *target = objc_getAssociatedObject(self, &block_key); 59 | return target.block; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIBezierPath+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIBezierPath+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/30. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `UIBezierPath`. 18 | */ 19 | @interface UIBezierPath (YYAdd) 20 | 21 | /** 22 | Creates and returns a new UIBezierPath object initialized with the text glyphs 23 | generated from the specified font. 24 | 25 | @discussion It doesnot support apple emoji. If you want get emoji image, try 26 | [UIImage imageWithEmoji:size:] in `UIImage(YYAdd)`. 27 | 28 | @param text The text to generate glyph path. 29 | @param font The font to generate glyph path. 30 | 31 | @return A new path object with the text and font, or nil if an error occurs. 32 | */ 33 | + (nullable UIBezierPath *)bezierPathWithText:(NSString *)text font:(UIFont *)font; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIBezierPath+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIBezierPath+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/30. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "UIBezierPath+YYAdd.h" 13 | #import "UIFont+YYAdd.h" 14 | #import 15 | #import "YYKitMacro.h" 16 | 17 | YYSYNTH_DUMMY_CLASS(UIBezierPath_YYAdd) 18 | 19 | 20 | @implementation UIBezierPath (YYAdd) 21 | 22 | + (UIBezierPath *)bezierPathWithText:(NSString *)text font:(UIFont *)font { 23 | CTFontRef ctFont = font.CTFontRef; 24 | if (!ctFont || !text) return nil; 25 | NSDictionary *attrs = @{ (__bridge id)kCTFontAttributeName:(__bridge id)ctFont }; 26 | NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:text attributes:attrs]; 27 | CFRelease(ctFont); 28 | 29 | CTLineRef line = CTLineCreateWithAttributedString((__bridge CFTypeRef)attrString); 30 | if (!line) return nil; 31 | 32 | CGMutablePathRef cgPath = CGPathCreateMutable(); 33 | CFArrayRef runs = CTLineGetGlyphRuns(line); 34 | for (CFIndex iRun = 0, iRunMax = CFArrayGetCount(runs); iRun < iRunMax; iRun++) { 35 | CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, iRun); 36 | CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName); 37 | 38 | for (CFIndex iGlyph = 0, iGlyphMax = CTRunGetGlyphCount(run); iGlyph < iGlyphMax; iGlyph++) { 39 | CFRange glyphRange = CFRangeMake(iGlyph, 1); 40 | CGGlyph glyph; 41 | CGPoint position; 42 | CTRunGetGlyphs(run, glyphRange, &glyph); 43 | CTRunGetPositions(run, glyphRange, &position); 44 | 45 | CGPathRef glyphPath = CTFontCreatePathForGlyph(runFont, glyph, NULL); 46 | if (glyphPath) { 47 | CGAffineTransform transform = CGAffineTransformMakeTranslation(position.x, position.y); 48 | CGPathAddPath(cgPath, &transform, glyphPath); 49 | CGPathRelease(glyphPath); 50 | } 51 | } 52 | } 53 | UIBezierPath *path = [UIBezierPath bezierPathWithCGPath:cgPath]; 54 | CGRect boundingBox = CGPathGetPathBoundingBox(cgPath); 55 | CFRelease(cgPath); 56 | CFRelease(line); 57 | 58 | [path applyTransform:CGAffineTransformMakeScale(1.0, -1.0)]; 59 | [path applyTransform:CGAffineTransformMakeTranslation(0.0, boundingBox.size.height)]; 60 | 61 | return path; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIControl+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/4/5. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `UIControl`. 18 | */ 19 | @interface UIControl (YYAdd) 20 | 21 | /** 22 | Removes all targets and actions for a particular event (or events) 23 | from an internal dispatch table. 24 | */ 25 | - (void)removeAllTargets; 26 | 27 | /** 28 | Adds or replaces a target and action for a particular event (or events) 29 | to an internal dispatch table. 30 | 31 | @param target The target object—that is, the object to which the 32 | action message is sent. If this is nil, the responder 33 | chain is searched for an object willing to respond to the 34 | action message. 35 | 36 | @param action A selector identifying an action message. It cannot be NULL. 37 | 38 | @param controlEvents A bitmask specifying the control events for which the 39 | action message is sent. 40 | */ 41 | - (void)setTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents; 42 | 43 | /** 44 | Adds a block for a particular event (or events) to an internal dispatch table. 45 | It will cause a strong reference to @a block. 46 | 47 | @param block The block which is invoked then the action message is 48 | sent (cannot be nil). The block is retained. 49 | 50 | @param controlEvents A bitmask specifying the control events for which the 51 | action message is sent. 52 | */ 53 | - (void)addBlockForControlEvents:(UIControlEvents)controlEvents block:(void (^)(id sender))block; 54 | 55 | /** 56 | Adds or replaces a block for a particular event (or events) to an internal 57 | dispatch table. It will cause a strong reference to @a block. 58 | 59 | @param block The block which is invoked then the action message is 60 | sent (cannot be nil). The block is retained. 61 | 62 | @param controlEvents A bitmask specifying the control events for which the 63 | action message is sent. 64 | */ 65 | - (void)setBlockForControlEvents:(UIControlEvents)controlEvents block:(void (^)(id sender))block; 66 | 67 | /** 68 | Removes all blocks for a particular event (or events) from an internal 69 | dispatch table. 70 | 71 | @param controlEvents A bitmask specifying the control events for which the 72 | action message is sent. 73 | */ 74 | - (void)removeAllBlocksForControlEvents:(UIControlEvents)controlEvents; 75 | 76 | @end 77 | 78 | NS_ASSUME_NONNULL_END 79 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIControl+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/4/5. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "UIControl+YYAdd.h" 13 | #import "YYKitMacro.h" 14 | #import 15 | 16 | YYSYNTH_DUMMY_CLASS(UIControl_YYAdd) 17 | 18 | 19 | static const int block_key; 20 | 21 | @interface _YYUIControlBlockTarget : NSObject 22 | 23 | @property (nonatomic, copy) void (^block)(id sender); 24 | @property (nonatomic, assign) UIControlEvents events; 25 | 26 | - (id)initWithBlock:(void (^)(id sender))block events:(UIControlEvents)events; 27 | - (void)invoke:(id)sender; 28 | 29 | @end 30 | 31 | @implementation _YYUIControlBlockTarget 32 | 33 | - (id)initWithBlock:(void (^)(id sender))block events:(UIControlEvents)events { 34 | self = [super init]; 35 | if (self) { 36 | _block = [block copy]; 37 | _events = events; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)invoke:(id)sender { 43 | if (_block) _block(sender); 44 | } 45 | 46 | @end 47 | 48 | 49 | 50 | @implementation UIControl (YYAdd) 51 | 52 | - (void)removeAllTargets { 53 | [[self allTargets] enumerateObjectsUsingBlock: ^(id object, BOOL *stop) { 54 | [self removeTarget:object action:NULL forControlEvents:UIControlEventAllEvents]; 55 | }]; 56 | [[self _yy_allUIControlBlockTargets] removeAllObjects]; 57 | } 58 | 59 | - (void)setTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents { 60 | if (!target || !action || !controlEvents) return; 61 | NSSet *targets = [self allTargets]; 62 | for (id currentTarget in targets) { 63 | NSArray *actions = [self actionsForTarget:currentTarget forControlEvent:controlEvents]; 64 | for (NSString *currentAction in actions) { 65 | [self removeTarget:currentTarget action:NSSelectorFromString(currentAction) 66 | forControlEvents:controlEvents]; 67 | } 68 | } 69 | [self addTarget:target action:action forControlEvents:controlEvents]; 70 | } 71 | 72 | - (void)addBlockForControlEvents:(UIControlEvents)controlEvents 73 | block:(void (^)(id sender))block { 74 | if (!controlEvents) return; 75 | _YYUIControlBlockTarget *target = [[_YYUIControlBlockTarget alloc] 76 | initWithBlock:block events:controlEvents]; 77 | [self addTarget:target action:@selector(invoke:) forControlEvents:controlEvents]; 78 | NSMutableArray *targets = [self _yy_allUIControlBlockTargets]; 79 | [targets addObject:target]; 80 | } 81 | 82 | - (void)setBlockForControlEvents:(UIControlEvents)controlEvents 83 | block:(void (^)(id sender))block { 84 | [self removeAllBlocksForControlEvents:UIControlEventAllEvents]; 85 | [self addBlockForControlEvents:controlEvents block:block]; 86 | } 87 | 88 | - (void)removeAllBlocksForControlEvents:(UIControlEvents)controlEvents { 89 | if (!controlEvents) return; 90 | 91 | NSMutableArray *targets = [self _yy_allUIControlBlockTargets]; 92 | NSMutableArray *removes = [NSMutableArray array]; 93 | for (_YYUIControlBlockTarget *target in targets) { 94 | if (target.events & controlEvents) { 95 | UIControlEvents newEvent = target.events & (~controlEvents); 96 | if (newEvent) { 97 | [self removeTarget:target action:@selector(invoke:) forControlEvents:target.events]; 98 | target.events = newEvent; 99 | [self addTarget:target action:@selector(invoke:) forControlEvents:target.events]; 100 | } else { 101 | [self removeTarget:target action:@selector(invoke:) forControlEvents:target.events]; 102 | [removes addObject:target]; 103 | } 104 | } 105 | } 106 | [targets removeObjectsInArray:removes]; 107 | } 108 | 109 | - (NSMutableArray *)_yy_allUIControlBlockTargets { 110 | NSMutableArray *targets = objc_getAssociatedObject(self, &block_key); 111 | if (!targets) { 112 | targets = [NSMutableArray array]; 113 | objc_setAssociatedObject(self, &block_key, targets, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 114 | } 115 | return targets; 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIGestureRecognizer+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIGestureRecognizer+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/10/13. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `UIGestureRecognizer`. 18 | */ 19 | @interface UIGestureRecognizer (YYAdd) 20 | 21 | /** 22 | Initializes an allocated gesture-recognizer object with a action block. 23 | 24 | @param block An action block that to handle the gesture recognized by the 25 | receiver. nil is invalid. It is retained by the gesture. 26 | 27 | @return An initialized instance of a concrete UIGestureRecognizer subclass or 28 | nil if an error occurred in the attempt to initialize the object. 29 | */ 30 | - (instancetype)initWithActionBlock:(void (^)(id sender))block; 31 | 32 | /** 33 | Adds an action block to a gesture-recognizer object. It is retained by the 34 | gesture. 35 | 36 | @param block A block invoked by the action message. nil is not a valid value. 37 | */ 38 | - (void)addActionBlock:(void (^)(id sender))block; 39 | 40 | /** 41 | Remove all action blocks. 42 | */ 43 | - (void)removeAllActionBlocks; 44 | 45 | @end 46 | 47 | NS_ASSUME_NONNULL_END 48 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIGestureRecognizer+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIGestureRecognizer+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/10/13. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "UIGestureRecognizer+YYAdd.h" 13 | #import "YYKitMacro.h" 14 | #import 15 | 16 | static const int block_key; 17 | 18 | @interface _YYUIGestureRecognizerBlockTarget : NSObject 19 | 20 | @property (nonatomic, copy) void (^block)(id sender); 21 | 22 | - (id)initWithBlock:(void (^)(id sender))block; 23 | - (void)invoke:(id)sender; 24 | 25 | @end 26 | 27 | @implementation _YYUIGestureRecognizerBlockTarget 28 | 29 | - (id)initWithBlock:(void (^)(id sender))block{ 30 | self = [super init]; 31 | if (self) { 32 | _block = [block copy]; 33 | } 34 | return self; 35 | } 36 | 37 | - (void)invoke:(id)sender { 38 | if (_block) _block(sender); 39 | } 40 | 41 | @end 42 | 43 | 44 | 45 | 46 | @implementation UIGestureRecognizer (YYAdd) 47 | 48 | - (instancetype)initWithActionBlock:(void (^)(id sender))block { 49 | self = [self init]; 50 | [self addActionBlock:block]; 51 | return self; 52 | } 53 | 54 | - (void)addActionBlock:(void (^)(id sender))block { 55 | _YYUIGestureRecognizerBlockTarget *target = [[_YYUIGestureRecognizerBlockTarget alloc] initWithBlock:block]; 56 | [self addTarget:target action:@selector(invoke:)]; 57 | NSMutableArray *targets = [self _yy_allUIGestureRecognizerBlockTargets]; 58 | [targets addObject:target]; 59 | } 60 | 61 | - (void)removeAllActionBlocks{ 62 | NSMutableArray *targets = [self _yy_allUIGestureRecognizerBlockTargets]; 63 | [targets enumerateObjectsUsingBlock:^(id target, NSUInteger idx, BOOL *stop) { 64 | [self removeTarget:target action:@selector(invoke:)]; 65 | }]; 66 | [targets removeAllObjects]; 67 | } 68 | 69 | - (NSMutableArray *)_yy_allUIGestureRecognizerBlockTargets { 70 | NSMutableArray *targets = objc_getAssociatedObject(self, &block_key); 71 | if (!targets) { 72 | targets = [NSMutableArray array]; 73 | objc_setAssociatedObject(self, &block_key, targets, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 74 | } 75 | return targets; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIScreen+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScreen+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/4/5. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `UIScreen`. 18 | */ 19 | @interface UIScreen (YYAdd) 20 | 21 | /** 22 | Main screen's scale 23 | 24 | @return screen's scale 25 | */ 26 | + (CGFloat)screenScale; 27 | 28 | /** 29 | Returns the bounds of the screen for the current device orientation. 30 | 31 | @return A rect indicating the bounds of the screen. 32 | @see boundsForOrientation: 33 | */ 34 | - (CGRect)currentBounds NS_EXTENSION_UNAVAILABLE_IOS(""); 35 | 36 | /** 37 | Returns the bounds of the screen for a given device orientation. 38 | `UIScreen`'s `bounds` method always returns the bounds of the 39 | screen of it in the portrait orientation. 40 | 41 | @param orientation The orientation to get the screen's bounds. 42 | @return A rect indicating the bounds of the screen. 43 | @see currentBounds 44 | */ 45 | - (CGRect)boundsForOrientation:(UIInterfaceOrientation)orientation; 46 | 47 | /** 48 | The screen's real size in pixel (width is always smaller than height). 49 | This value may not be very accurate in an unknown device, or simulator. 50 | e.g. (768,1024) 51 | */ 52 | @property (nonatomic, readonly) CGSize sizeInPixel; 53 | 54 | /** 55 | The screen's PPI. 56 | This value may not be very accurate in an unknown device, or simulator. 57 | Default value is 96. 58 | */ 59 | @property (nonatomic, readonly) CGFloat pixelsPerInch; 60 | 61 | @end 62 | 63 | NS_ASSUME_NONNULL_END 64 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIScrollView+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/4/5. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `UIScrollView`. 18 | */ 19 | @interface UIScrollView (YYAdd) 20 | 21 | /** 22 | Scroll content to top with animation. 23 | */ 24 | - (void)scrollToTop; 25 | 26 | /** 27 | Scroll content to bottom with animation. 28 | */ 29 | - (void)scrollToBottom; 30 | 31 | /** 32 | Scroll content to left with animation. 33 | */ 34 | - (void)scrollToLeft; 35 | 36 | /** 37 | Scroll content to right with animation. 38 | */ 39 | - (void)scrollToRight; 40 | 41 | /** 42 | Scroll content to top. 43 | 44 | @param animated Use animation. 45 | */ 46 | - (void)scrollToTopAnimated:(BOOL)animated; 47 | 48 | /** 49 | Scroll content to bottom. 50 | 51 | @param animated Use animation. 52 | */ 53 | - (void)scrollToBottomAnimated:(BOOL)animated; 54 | 55 | /** 56 | Scroll content to left. 57 | 58 | @param animated Use animation. 59 | */ 60 | - (void)scrollToLeftAnimated:(BOOL)animated; 61 | 62 | /** 63 | Scroll content to right. 64 | 65 | @param animated Use animation. 66 | */ 67 | - (void)scrollToRightAnimated:(BOOL)animated; 68 | 69 | @end 70 | 71 | NS_ASSUME_NONNULL_END 72 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIScrollView+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/4/5. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "UIScrollView+YYAdd.h" 13 | #import "YYKitMacro.h" 14 | 15 | YYSYNTH_DUMMY_CLASS(UIScrollView_YYAdd) 16 | 17 | 18 | @implementation UIScrollView (YYAdd) 19 | 20 | - (void)scrollToTop { 21 | [self scrollToTopAnimated:YES]; 22 | } 23 | 24 | - (void)scrollToBottom { 25 | [self scrollToBottomAnimated:YES]; 26 | } 27 | 28 | - (void)scrollToLeft { 29 | [self scrollToLeftAnimated:YES]; 30 | } 31 | 32 | - (void)scrollToRight { 33 | [self scrollToRightAnimated:YES]; 34 | } 35 | 36 | - (void)scrollToTopAnimated:(BOOL)animated { 37 | CGPoint off = self.contentOffset; 38 | off.y = 0 - self.contentInset.top; 39 | [self setContentOffset:off animated:animated]; 40 | } 41 | 42 | - (void)scrollToBottomAnimated:(BOOL)animated { 43 | CGPoint off = self.contentOffset; 44 | off.y = self.contentSize.height - self.bounds.size.height + self.contentInset.bottom; 45 | [self setContentOffset:off animated:animated]; 46 | } 47 | 48 | - (void)scrollToLeftAnimated:(BOOL)animated { 49 | CGPoint off = self.contentOffset; 50 | off.x = 0 - self.contentInset.left; 51 | [self setContentOffset:off animated:animated]; 52 | } 53 | 54 | - (void)scrollToRightAnimated:(BOOL)animated { 55 | CGPoint off = self.contentOffset; 56 | off.x = self.contentSize.width - self.bounds.size.width + self.contentInset.right; 57 | [self setContentOffset:off animated:animated]; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UITableView+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/5/12. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "UITableView+YYAdd.h" 13 | #import "YYKitMacro.h" 14 | 15 | YYSYNTH_DUMMY_CLASS(UITableView_YYAdd) 16 | 17 | 18 | @implementation UITableView (YYAdd) 19 | 20 | - (void)updateWithBlock:(void (^)(UITableView *tableView))block { 21 | [self beginUpdates]; 22 | block(self); 23 | [self endUpdates]; 24 | } 25 | 26 | - (void)scrollToRow:(NSUInteger)row inSection:(NSUInteger)section atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated { 27 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; 28 | [self scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; 29 | } 30 | 31 | - (void)insertRowAtIndexPath:(NSIndexPath *)indexPath withRowAnimation:(UITableViewRowAnimation)animation { 32 | [self insertRowsAtIndexPaths:@[indexPath] withRowAnimation:animation]; 33 | } 34 | 35 | - (void)insertRow:(NSUInteger)row inSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation { 36 | NSIndexPath *toInsert = [NSIndexPath indexPathForRow:row inSection:section]; 37 | [self insertRowAtIndexPath:toInsert withRowAnimation:animation]; 38 | } 39 | 40 | - (void)reloadRowAtIndexPath:(NSIndexPath *)indexPath withRowAnimation:(UITableViewRowAnimation)animation { 41 | [self reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:animation]; 42 | } 43 | 44 | - (void)reloadRow:(NSUInteger)row inSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation { 45 | NSIndexPath *toReload = [NSIndexPath indexPathForRow:row inSection:section]; 46 | [self reloadRowAtIndexPath:toReload withRowAnimation:animation]; 47 | } 48 | 49 | - (void)deleteRowAtIndexPath:(NSIndexPath *)indexPath withRowAnimation:(UITableViewRowAnimation)animation { 50 | [self deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:animation]; 51 | } 52 | 53 | - (void)deleteRow:(NSUInteger)row inSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation { 54 | NSIndexPath *toDelete = [NSIndexPath indexPathForRow:row inSection:section]; 55 | [self deleteRowAtIndexPath:toDelete withRowAnimation:animation]; 56 | } 57 | 58 | - (void)insertSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation { 59 | NSIndexSet *sections = [NSIndexSet indexSetWithIndex:section]; 60 | [self insertSections:sections withRowAnimation:animation]; 61 | } 62 | 63 | - (void)deleteSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation { 64 | NSIndexSet *sections = [NSIndexSet indexSetWithIndex:section]; 65 | [self deleteSections:sections withRowAnimation:animation]; 66 | } 67 | 68 | - (void)reloadSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation { 69 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:section]; 70 | [self reloadSections:indexSet withRowAnimation:animation]; 71 | } 72 | 73 | - (void)clearSelectedRowsAnimated:(BOOL)animated { 74 | NSArray *indexs = [self indexPathsForSelectedRows]; 75 | [indexs enumerateObjectsUsingBlock:^(NSIndexPath* path, NSUInteger idx, BOOL *stop) { 76 | [self deselectRowAtIndexPath:path animated:animated]; 77 | }]; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UITextField+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/5/12. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `UITextField`. 18 | */ 19 | @interface UITextField (YYAdd) 20 | 21 | /** 22 | Set all text selected. 23 | */ 24 | - (void)selectAllText; 25 | 26 | /** 27 | Set text in range selected. 28 | 29 | @param range The range of selected text in a document. 30 | */ 31 | - (void)setSelectedRange:(NSRange)range; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UITextField+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+YYAdd.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/5/12. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "UITextField+YYAdd.h" 13 | #import "YYKitMacro.h" 14 | 15 | YYSYNTH_DUMMY_CLASS(UITextField_YYAdd) 16 | 17 | 18 | @implementation UITextField (YYAdd) 19 | 20 | - (void)selectAllText { 21 | UITextRange *range = [self textRangeFromPosition:self.beginningOfDocument toPosition:self.endOfDocument]; 22 | [self setSelectedTextRange:range]; 23 | } 24 | 25 | - (void)setSelectedRange:(NSRange)range { 26 | UITextPosition *beginning = self.beginningOfDocument; 27 | UITextPosition *startPosition = [self positionFromPosition:beginning offset:range.location]; 28 | UITextPosition *endPosition = [self positionFromPosition:beginning offset:NSMaxRange(range)]; 29 | UITextRange *selectionRange = [self textRangeFromPosition:startPosition toPosition:endPosition]; 30 | [self setSelectedTextRange:selectionRange]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Base/UIKit/UIView+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+YYAdd.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 13/4/3. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `UIView`. 18 | */ 19 | @interface UIView (YYAdd) 20 | 21 | /** 22 | Create a snapshot image of the complete view hierarchy. 23 | */ 24 | - (nullable UIImage *)snapshotImage; 25 | 26 | /** 27 | Create a snapshot image of the complete view hierarchy. 28 | @discussion It's faster than "snapshotImage", but may cause screen updates. 29 | See -[UIView drawViewHierarchyInRect:afterScreenUpdates:] for more information. 30 | */ 31 | - (nullable UIImage *)snapshotImageAfterScreenUpdates:(BOOL)afterUpdates; 32 | 33 | /** 34 | Create a snapshot PDF of the complete view hierarchy. 35 | */ 36 | - (nullable NSData *)snapshotPDF; 37 | 38 | /** 39 | Shortcut to set the view.layer's shadow 40 | 41 | @param color Shadow Color 42 | @param offset Shadow offset 43 | @param radius Shadow radius 44 | */ 45 | - (void)setLayerShadow:(nullable UIColor*)color offset:(CGSize)offset radius:(CGFloat)radius; 46 | 47 | /** 48 | Remove all subviews. 49 | 50 | @warning Never call this method inside your view's drawRect: method. 51 | */ 52 | - (void)removeAllSubviews; 53 | 54 | /** 55 | Returns the view's view controller (may be nil). 56 | */ 57 | @property (nullable, nonatomic, readonly) UIViewController *viewController; 58 | 59 | /** 60 | Returns the visible alpha on screen, taking into account superview and window. 61 | */ 62 | @property (nonatomic, readonly) CGFloat visibleAlpha; 63 | 64 | /** 65 | Converts a point from the receiver's coordinate system to that of the specified view or window. 66 | 67 | @param point A point specified in the local coordinate system (bounds) of the receiver. 68 | @param view The view or window into whose coordinate system point is to be converted. 69 | If view is nil, this method instead converts to window base coordinates. 70 | @return The point converted to the coordinate system of view. 71 | */ 72 | - (CGPoint)convertPoint:(CGPoint)point toViewOrWindow:(nullable UIView *)view; 73 | 74 | /** 75 | Converts a point from the coordinate system of a given view or window to that of the receiver. 76 | 77 | @param point A point specified in the local coordinate system (bounds) of view. 78 | @param view The view or window with point in its coordinate system. 79 | If view is nil, this method instead converts from window base coordinates. 80 | @return The point converted to the local coordinate system (bounds) of the receiver. 81 | */ 82 | - (CGPoint)convertPoint:(CGPoint)point fromViewOrWindow:(nullable UIView *)view; 83 | 84 | /** 85 | Converts a rectangle from the receiver's coordinate system to that of another view or window. 86 | 87 | @param rect A rectangle specified in the local coordinate system (bounds) of the receiver. 88 | @param view The view or window that is the target of the conversion operation. If view is nil, this method instead converts to window base coordinates. 89 | @return The converted rectangle. 90 | */ 91 | - (CGRect)convertRect:(CGRect)rect toViewOrWindow:(nullable UIView *)view; 92 | 93 | /** 94 | Converts a rectangle from the coordinate system of another view or window to that of the receiver. 95 | 96 | @param rect A rectangle specified in the local coordinate system (bounds) of view. 97 | @param view The view or window with rect in its coordinate system. 98 | If view is nil, this method instead converts from window base coordinates. 99 | @return The converted rectangle. 100 | */ 101 | - (CGRect)convertRect:(CGRect)rect fromViewOrWindow:(nullable UIView *)view; 102 | 103 | 104 | @property (nonatomic) CGFloat left; ///< Shortcut for frame.origin.x. 105 | @property (nonatomic) CGFloat top; ///< Shortcut for frame.origin.y 106 | @property (nonatomic) CGFloat right; ///< Shortcut for frame.origin.x + frame.size.width 107 | @property (nonatomic) CGFloat bottom; ///< Shortcut for frame.origin.y + frame.size.height 108 | @property (nonatomic) CGFloat width; ///< Shortcut for frame.size.width. 109 | @property (nonatomic) CGFloat height; ///< Shortcut for frame.size.height. 110 | @property (nonatomic) CGFloat centerX; ///< Shortcut for center.x 111 | @property (nonatomic) CGFloat centerY; ///< Shortcut for center.y 112 | @property (nonatomic) CGPoint origin; ///< Shortcut for frame.origin. 113 | @property (nonatomic) CGSize size; ///< Shortcut for frame.size. 114 | 115 | @end 116 | 117 | NS_ASSUME_NONNULL_END 118 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Cache/YYCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYCache.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/13. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYCache.h" 13 | #import "YYMemoryCache.h" 14 | #import "YYDiskCache.h" 15 | 16 | @implementation YYCache 17 | 18 | - (instancetype) init { 19 | NSLog(@"Use \"initWithName\" or \"initWithPath\" to create YYCache instance."); 20 | return [self initWithPath:@""]; 21 | } 22 | 23 | - (instancetype)initWithName:(NSString *)name { 24 | if (name.length == 0) return nil; 25 | NSString *cacheFolder = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; 26 | NSString *path = [cacheFolder stringByAppendingPathComponent:name]; 27 | return [self initWithPath:path]; 28 | } 29 | 30 | - (instancetype)initWithPath:(NSString *)path { 31 | if (path.length == 0) return nil; 32 | YYDiskCache *diskCache = [[YYDiskCache alloc] initWithPath:path]; 33 | if (!diskCache) return nil; 34 | NSString *name = [path lastPathComponent]; 35 | YYMemoryCache *memoryCache = [YYMemoryCache new]; 36 | memoryCache.name = name; 37 | 38 | self = [super init]; 39 | _name = name; 40 | _diskCache = diskCache; 41 | _memoryCache = memoryCache; 42 | return self; 43 | } 44 | 45 | + (instancetype)cacheWithName:(NSString *)name { 46 | return [[self alloc] initWithName:name]; 47 | } 48 | 49 | + (instancetype)cacheWithPath:(NSString *)path { 50 | return [[self alloc] initWithPath:path]; 51 | } 52 | 53 | - (BOOL)containsObjectForKey:(NSString *)key { 54 | return [_memoryCache containsObjectForKey:key] || [_diskCache containsObjectForKey:key]; 55 | } 56 | 57 | - (void)containsObjectForKey:(NSString *)key withBlock:(void (^)(NSString *key, BOOL contains))block { 58 | if (!block) return; 59 | 60 | if ([_memoryCache containsObjectForKey:key]) { 61 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 62 | block(key, YES); 63 | }); 64 | } else { 65 | [_diskCache containsObjectForKey:key withBlock:block]; 66 | } 67 | } 68 | 69 | - (id)objectForKey:(NSString *)key { 70 | id object = [_memoryCache objectForKey:key]; 71 | if (!object) { 72 | object = [_diskCache objectForKey:key]; 73 | if (object) { 74 | [_memoryCache setObject:object forKey:key]; 75 | } 76 | } 77 | return object; 78 | } 79 | 80 | - (void)objectForKey:(NSString *)key withBlock:(void (^)(NSString *key, id object))block { 81 | if (!block) return; 82 | id object = [_memoryCache objectForKey:key]; 83 | if (object) { 84 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 85 | block(key, object); 86 | }); 87 | } else { 88 | [_diskCache objectForKey:key withBlock:^(NSString *key, id object) { 89 | if (object && ![self->_memoryCache objectForKey:key]) { 90 | [self->_memoryCache setObject:object forKey:key]; 91 | } 92 | block(key, object); 93 | }]; 94 | } 95 | } 96 | 97 | - (void)setObject:(id)object forKey:(NSString *)key { 98 | [_memoryCache setObject:object forKey:key]; 99 | [_diskCache setObject:object forKey:key]; 100 | } 101 | 102 | - (void)setObject:(id)object forKey:(NSString *)key withBlock:(void (^)(void))block { 103 | [_memoryCache setObject:object forKey:key]; 104 | [_diskCache setObject:object forKey:key withBlock:block]; 105 | } 106 | 107 | - (void)removeObjectForKey:(NSString *)key { 108 | [_memoryCache removeObjectForKey:key]; 109 | [_diskCache removeObjectForKey:key]; 110 | } 111 | 112 | - (void)removeObjectForKey:(NSString *)key withBlock:(void (^)(NSString *key))block { 113 | [_memoryCache removeObjectForKey:key]; 114 | [_diskCache removeObjectForKey:key withBlock:block]; 115 | } 116 | 117 | - (void)removeAllObjects { 118 | [_memoryCache removeAllObjects]; 119 | [_diskCache removeAllObjects]; 120 | } 121 | 122 | - (void)removeAllObjectsWithBlock:(void(^)(void))block { 123 | [_memoryCache removeAllObjects]; 124 | [_diskCache removeAllObjectsWithBlock:block]; 125 | } 126 | 127 | - (void)removeAllObjectsWithProgressBlock:(void(^)(int removedCount, int totalCount))progress 128 | endBlock:(void(^)(BOOL error))end { 129 | [_memoryCache removeAllObjects]; 130 | [_diskCache removeAllObjectsWithProgressBlock:progress endBlock:end]; 131 | 132 | } 133 | 134 | - (NSString *)description { 135 | if (_name) return [NSString stringWithFormat:@"<%@: %p> (%@)", self.class, self, _name]; 136 | else return [NSString stringWithFormat:@"<%@: %p>", self.class, self]; 137 | } 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/Categories/CALayer+YYWebImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+YYWebImage.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/23. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | #if __has_include() 16 | #import 17 | #else 18 | #import "YYWebImageManager.h" 19 | #endif 20 | 21 | NS_ASSUME_NONNULL_BEGIN 22 | 23 | /** 24 | Web image methods for CALayer. 25 | It will set image to layer.contents. 26 | */ 27 | @interface CALayer (YYWebImage) 28 | 29 | #pragma mark - image 30 | 31 | /** 32 | Current image URL. 33 | 34 | @discussion Set a new value to this property will cancel the previous request 35 | operation and create a new request operation to fetch image. Set nil to clear 36 | the image and image URL. 37 | */ 38 | @property (nullable, nonatomic, strong) NSURL *imageURL; 39 | 40 | /** 41 | Set the view's `image` with a specified URL. 42 | 43 | @param imageURL The image url (remote or local file path). 44 | @param placeholder The image to be set initially, until the image request finishes. 45 | */ 46 | - (void)setImageWithURL:(nullable NSURL *)imageURL placeholder:(nullable UIImage *)placeholder; 47 | 48 | /** 49 | Set the view's `image` with a specified URL. 50 | 51 | @param imageURL The image url (remote or local file path). 52 | @param options The options to use when request the image. 53 | */ 54 | - (void)setImageWithURL:(nullable NSURL *)imageURL options:(YYWebImageOptions)options; 55 | 56 | /** 57 | Set the view's `image` with a specified URL. 58 | 59 | @param imageURL The image url (remote or local file path). 60 | @param placeholder The image to be set initially, until the image request finishes. 61 | @param options The options to use when request the image. 62 | @param completion The block invoked (on main thread) when image request completed. 63 | */ 64 | - (void)setImageWithURL:(nullable NSURL *)imageURL 65 | placeholder:(nullable UIImage *)placeholder 66 | options:(YYWebImageOptions)options 67 | completion:(nullable YYWebImageCompletionBlock)completion; 68 | 69 | /** 70 | Set the view's `image` with a specified URL. 71 | 72 | @param imageURL The image url (remote or local file path). 73 | @param placeholder The image to be set initially, until the image request finishes. 74 | @param options The options to use when request the image. 75 | @param progress The block invoked (on main thread) during image request. 76 | @param transform The block invoked (on background thread) to do additional image process. 77 | @param completion The block invoked (on main thread) when image request completed. 78 | */ 79 | - (void)setImageWithURL:(nullable NSURL *)imageURL 80 | placeholder:(nullable UIImage *)placeholder 81 | options:(YYWebImageOptions)options 82 | progress:(nullable YYWebImageProgressBlock)progress 83 | transform:(nullable YYWebImageTransformBlock)transform 84 | completion:(nullable YYWebImageCompletionBlock)completion; 85 | 86 | /** 87 | Set the view's `image` with a specified URL. 88 | 89 | @param imageURL The image url (remote or local file path). 90 | @param placeholder he image to be set initially, until the image request finishes. 91 | @param options The options to use when request the image. 92 | @param manager The manager to create image request operation. 93 | @param progress The block invoked (on main thread) during image request. 94 | @param transform The block invoked (on background thread) to do additional image process. 95 | @param completion The block invoked (on main thread) when image request completed. 96 | */ 97 | - (void)setImageWithURL:(nullable NSURL *)imageURL 98 | placeholder:(nullable UIImage *)placeholder 99 | options:(YYWebImageOptions)options 100 | manager:(nullable YYWebImageManager *)manager 101 | progress:(nullable YYWebImageProgressBlock)progress 102 | transform:(nullable YYWebImageTransformBlock)transform 103 | completion:(nullable YYWebImageCompletionBlock)completion; 104 | 105 | /** 106 | Cancel the current image request. 107 | */ 108 | - (void)cancelCurrentImageRequest; 109 | 110 | @end 111 | 112 | NS_ASSUME_NONNULL_END 113 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/Categories/MKAnnotationView+YYWebImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // MKAnnotationView+YYWebImage.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/23. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | #if __has_include() 16 | #import 17 | #else 18 | #import "YYWebImageManager.h" 19 | #endif 20 | 21 | NS_ASSUME_NONNULL_BEGIN 22 | 23 | /** 24 | Web image methods for MKAnnotationView. 25 | */ 26 | @interface MKAnnotationView (YYWebImage) 27 | 28 | /** 29 | Current image URL. 30 | 31 | @discussion Set a new value to this property will cancel the previous request 32 | operation and create a new request operation to fetch image. Set nil to clear 33 | the image and image URL. 34 | */ 35 | @property (nullable, nonatomic, strong) NSURL *imageURL; 36 | 37 | /** 38 | Set the view's `image` with a specified URL. 39 | 40 | @param imageURL The image url (remote or local file path). 41 | @param placeholder The image to be set initially, until the image request finishes. 42 | */ 43 | - (void)setImageWithURL:(nullable NSURL *)imageURL placeholder:(nullable UIImage *)placeholder; 44 | 45 | /** 46 | Set the view's `image` with a specified URL. 47 | 48 | @param imageURL The image url (remote or local file path). 49 | @param options The options to use when request the image. 50 | */ 51 | - (void)setImageWithURL:(nullable NSURL *)imageURL options:(YYWebImageOptions)options; 52 | 53 | /** 54 | Set the view's `image` with a specified URL. 55 | 56 | @param imageURL The image url (remote or local file path). 57 | @param placeholder The image to be set initially, until the image request finishes. 58 | @param options The options to use when request the image. 59 | @param completion The block invoked (on main thread) when image request completed. 60 | */ 61 | - (void)setImageWithURL:(nullable NSURL *)imageURL 62 | placeholder:(nullable UIImage *)placeholder 63 | options:(YYWebImageOptions)options 64 | completion:(nullable YYWebImageCompletionBlock)completion; 65 | 66 | /** 67 | Set the view's `image` with a specified URL. 68 | 69 | @param imageURL The image url (remote or local file path). 70 | @param placeholder The image to be set initially, until the image request finishes. 71 | @param options The options to use when request the image. 72 | @param progress The block invoked (on main thread) during image request. 73 | @param transform The block invoked (on background thread) to do additional image process. 74 | @param completion The block invoked (on main thread) when image request completed. 75 | */ 76 | - (void)setImageWithURL:(nullable NSURL *)imageURL 77 | placeholder:(nullable UIImage *)placeholder 78 | options:(YYWebImageOptions)options 79 | progress:(nullable YYWebImageProgressBlock)progress 80 | transform:(nullable YYWebImageTransformBlock)transform 81 | completion:(nullable YYWebImageCompletionBlock)completion; 82 | 83 | /** 84 | Set the view's `image` with a specified URL. 85 | 86 | @param imageURL The image url (remote or local file path). 87 | @param placeholder he image to be set initially, until the image request finishes. 88 | @param options The options to use when request the image. 89 | @param manager The manager to create image request operation. 90 | @param progress The block invoked (on main thread) during image request. 91 | @param transform The block invoked (on background thread) to do additional image process. 92 | @param completion The block invoked (on main thread) when image request completed. 93 | */ 94 | - (void)setImageWithURL:(nullable NSURL *)imageURL 95 | placeholder:(nullable UIImage *)placeholder 96 | options:(YYWebImageOptions)options 97 | manager:(nullable YYWebImageManager *)manager 98 | progress:(nullable YYWebImageProgressBlock)progress 99 | transform:(nullable YYWebImageTransformBlock)transform 100 | completion:(nullable YYWebImageCompletionBlock)completion; 101 | 102 | /** 103 | Cancel the current image request. 104 | */ 105 | - (void)cancelCurrentImageRequest; 106 | 107 | @end 108 | 109 | NS_ASSUME_NONNULL_END 110 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/Categories/_YYWebImageSetter.h: -------------------------------------------------------------------------------- 1 | // 2 | // _YYWebImageSetter.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/7/15. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #if __has_include() 13 | #import 14 | #else 15 | #import "YYWebImageManager.h" 16 | #endif 17 | 18 | NS_ASSUME_NONNULL_BEGIN 19 | 20 | extern NSString *const _YYWebImageFadeAnimationKey; 21 | extern const NSTimeInterval _YYWebImageFadeTime; 22 | extern const NSTimeInterval _YYWebImageProgressiveFadeTime; 23 | 24 | /** 25 | Private class used by web image categories. 26 | Typically, you should not use this class directly. 27 | */ 28 | @interface _YYWebImageSetter : NSObject 29 | /// Current image url. 30 | @property (nullable, nonatomic, readonly) NSURL *imageURL; 31 | /// Current sentinel. 32 | @property (nonatomic, readonly) int32_t sentinel; 33 | 34 | /// Create new operation for web image and return a sentinel value. 35 | - (int32_t)setOperationWithSentinel:(int32_t)sentinel 36 | url:(nullable NSURL *)imageURL 37 | options:(YYWebImageOptions)options 38 | manager:(YYWebImageManager *)manager 39 | progress:(nullable YYWebImageProgressBlock)progress 40 | transform:(nullable YYWebImageTransformBlock)transform 41 | completion:(nullable YYWebImageCompletionBlock)completion; 42 | 43 | /// Cancel and return a sentinel value. The imageURL will be set to nil. 44 | - (int32_t)cancel; 45 | 46 | /// Cancel and return a sentinel value. The imageURL will be set to new value. 47 | - (int32_t)cancelWithNewURL:(nullable NSURL *)imageURL; 48 | 49 | /// A queue to set operation. 50 | + (dispatch_queue_t)setterQueue; 51 | 52 | @end 53 | 54 | NS_ASSUME_NONNULL_END 55 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/Categories/_YYWebImageSetter.m: -------------------------------------------------------------------------------- 1 | // 2 | // _YYWebImageSetter.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/7/15. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "_YYWebImageSetter.h" 13 | #import "YYWebImageOperation.h" 14 | #import 15 | 16 | NSString *const _YYWebImageFadeAnimationKey = @"YYWebImageFade"; 17 | const NSTimeInterval _YYWebImageFadeTime = 0.2; 18 | const NSTimeInterval _YYWebImageProgressiveFadeTime = 0.4; 19 | 20 | 21 | @implementation _YYWebImageSetter { 22 | dispatch_semaphore_t _lock; 23 | NSURL *_imageURL; 24 | NSOperation *_operation; 25 | int32_t _sentinel; 26 | } 27 | 28 | - (instancetype)init { 29 | self = [super init]; 30 | _lock = dispatch_semaphore_create(1); 31 | return self; 32 | } 33 | 34 | - (NSURL *)imageURL { 35 | dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); 36 | NSURL *imageURL = _imageURL; 37 | dispatch_semaphore_signal(_lock); 38 | return imageURL; 39 | } 40 | 41 | - (void)dealloc { 42 | OSAtomicIncrement32(&_sentinel); 43 | [_operation cancel]; 44 | } 45 | 46 | - (int32_t)setOperationWithSentinel:(int32_t)sentinel 47 | url:(NSURL *)imageURL 48 | options:(YYWebImageOptions)options 49 | manager:(YYWebImageManager *)manager 50 | progress:(YYWebImageProgressBlock)progress 51 | transform:(YYWebImageTransformBlock)transform 52 | completion:(YYWebImageCompletionBlock)completion { 53 | if (sentinel != _sentinel) { 54 | if (completion) completion(nil, imageURL, YYWebImageFromNone, YYWebImageStageCancelled, nil); 55 | return _sentinel; 56 | } 57 | 58 | NSOperation *operation = [manager requestImageWithURL:imageURL options:options progress:progress transform:transform completion:completion]; 59 | if (!operation && completion) { 60 | NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"YYWebImageOperation create failed." }; 61 | completion(nil, imageURL, YYWebImageFromNone, YYWebImageStageFinished, [NSError errorWithDomain:@"com.ibireme.yykit.webimage" code:-1 userInfo:userInfo]); 62 | } 63 | 64 | dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); 65 | if (sentinel == _sentinel) { 66 | if (_operation) [_operation cancel]; 67 | _operation = operation; 68 | sentinel = OSAtomicIncrement32(&_sentinel); 69 | } else { 70 | [operation cancel]; 71 | } 72 | dispatch_semaphore_signal(_lock); 73 | return sentinel; 74 | } 75 | 76 | - (int32_t)cancel { 77 | return [self cancelWithNewURL:nil]; 78 | } 79 | 80 | - (int32_t)cancelWithNewURL:(NSURL *)imageURL { 81 | int32_t sentinel; 82 | dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); 83 | if (_operation) { 84 | [_operation cancel]; 85 | _operation = nil; 86 | } 87 | _imageURL = imageURL; 88 | sentinel = OSAtomicIncrement32(&_sentinel); 89 | dispatch_semaphore_signal(_lock); 90 | return sentinel; 91 | } 92 | 93 | + (dispatch_queue_t)setterQueue { 94 | static dispatch_queue_t queue; 95 | static dispatch_once_t onceToken; 96 | dispatch_once(&onceToken, ^{ 97 | queue = dispatch_queue_create("com.ibireme.yykit.webimage.setter", DISPATCH_QUEUE_SERIAL); 98 | dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); 99 | }); 100 | return queue; 101 | } 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/YYAnimatedImageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYAnimatedImageView.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/19. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | An image view for displaying animated image. 18 | 19 | @discussion It is a fully compatible `UIImageView` subclass. 20 | If the `image` or `highlightedImage` property adopt to the `YYAnimatedImage` protocol, 21 | then it can be used to play the multi-frame animation. The animation can also be 22 | controlled with the UIImageView methods `-startAnimating`, `-stopAnimating` and `-isAnimating`. 23 | 24 | This view request the frame data just in time. When the device has enough free memory, 25 | this view may cache some or all future frames in an inner buffer for lower CPU cost. 26 | Buffer size is dynamically adjusted based on the current state of the device memory. 27 | 28 | Sample Code: 29 | 30 | // ani@3x.gif 31 | YYImage *image = [YYImage imageNamed:@"ani"]; 32 | YYAnimatedImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image]; 33 | [view addSubView:imageView]; 34 | */ 35 | @interface YYAnimatedImageView : UIImageView 36 | 37 | /** 38 | If the image has more than one frame, set this value to `YES` will automatically 39 | play/stop the animation when the view become visible/invisible. 40 | 41 | The default value is `YES`. 42 | */ 43 | @property (nonatomic) BOOL autoPlayAnimatedImage; 44 | 45 | /** 46 | Index of the currently displayed frame (index from 0). 47 | 48 | Set a new value to this property will cause to display the new frame immediately. 49 | If the new value is invalid, this method has no effect. 50 | 51 | You can add an observer to this property to observe the playing status. 52 | */ 53 | @property (nonatomic) NSUInteger currentAnimatedImageIndex; 54 | 55 | /** 56 | Whether the image view is playing animation currently. 57 | 58 | You can add an observer to this property to observe the playing status. 59 | */ 60 | @property (nonatomic, readonly) BOOL currentIsPlayingAnimation; 61 | 62 | /** 63 | The animation timer's runloop mode, default is `NSRunLoopCommonModes`. 64 | 65 | Set this property to `NSDefaultRunLoopMode` will make the animation pause during 66 | UIScrollView scrolling. 67 | */ 68 | @property (nonatomic, copy) NSString *runloopMode; 69 | 70 | /** 71 | The max size (in bytes) for inner frame buffer size, default is 0 (dynamically). 72 | 73 | When the device has enough free memory, this view will request and decode some or 74 | all future frame image into an inner buffer. If this property's value is 0, then 75 | the max buffer size will be dynamically adjusted based on the current state of 76 | the device free memory. Otherwise, the buffer size will be limited by this value. 77 | 78 | When receive memory warning or app enter background, the buffer will be released 79 | immediately, and may grow back at the right time. 80 | */ 81 | @property (nonatomic) NSUInteger maxBufferSize; 82 | 83 | @end 84 | 85 | 86 | 87 | /** 88 | The YYAnimatedImage protocol declares the required methods for animated image 89 | display with YYAnimatedImageView. 90 | 91 | Subclass a UIImage and implement this protocol, so that instances of that class 92 | can be set to YYAnimatedImageView.image or YYAnimatedImageView.highlightedImage 93 | to display animation. 94 | 95 | See `YYImage` and `YYFrameImage` for example. 96 | */ 97 | @protocol YYAnimatedImage 98 | @required 99 | /// Total animated frame count. 100 | /// If the frame count is less than 1, then the methods below will be ignored. 101 | - (NSUInteger)animatedImageFrameCount; 102 | 103 | /// Animation loop count, 0 means infinite looping. 104 | - (NSUInteger)animatedImageLoopCount; 105 | 106 | /// Bytes per frame (in memory). It may used to optimize memory buffer size. 107 | - (NSUInteger)animatedImageBytesPerFrame; 108 | 109 | /// Returns the frame image from a specified index. 110 | /// This method may be called on background thread. 111 | /// @param index Frame index (zero based). 112 | - (nullable UIImage *)animatedImageFrameAtIndex:(NSUInteger)index; 113 | 114 | /// Returns the frames's duration from a specified index. 115 | /// @param index Frame index (zero based). 116 | - (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index; 117 | 118 | @optional 119 | /// A rectangle in image coordinates defining the subrectangle of the image that 120 | /// will be displayed. The rectangle should not outside the image's bounds. 121 | /// It may used to display sprite animation with a single image (sprite sheet). 122 | - (CGRect)animatedImageContentsRectAtIndex:(NSUInteger)index; 123 | @end 124 | 125 | NS_ASSUME_NONNULL_END 126 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/YYFrameImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYFrameImage.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/12/9. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | #import 16 | #else 17 | #import "YYAnimatedImageView.h" 18 | #endif 19 | 20 | NS_ASSUME_NONNULL_BEGIN 21 | 22 | /** 23 | An image to display frame-based animation. 24 | 25 | @discussion It is a fully compatible `UIImage` subclass. 26 | It only support system image format such as png and jpeg. 27 | The animation can be played by YYAnimatedImageView. 28 | 29 | Sample Code: 30 | 31 | NSArray *paths = @[@"/ani/frame1.png", @"/ani/frame2.png", @"/ani/frame3.png"]; 32 | NSArray *times = @[@0.1, @0.2, @0.1]; 33 | YYFrameImage *image = [YYFrameImage alloc] initWithImagePaths:paths frameDurations:times repeats:YES]; 34 | YYAnimatedImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image]; 35 | [view addSubView:imageView]; 36 | */ 37 | @interface YYFrameImage : UIImage 38 | 39 | /** 40 | Create a frame animated image from files. 41 | 42 | @param paths An array of NSString objects, contains the full or 43 | partial path to each image file. 44 | e.g. @[@"/ani/1.png",@"/ani/2.png",@"/ani/3.png"] 45 | 46 | @param oneFrameDuration The duration (in seconds) per frame. 47 | 48 | @param loopCount The animation loop count, 0 means infinite. 49 | 50 | @return An initialized YYFrameImage object, or nil when an error occurs. 51 | */ 52 | - (nullable instancetype)initWithImagePaths:(NSArray *)paths 53 | oneFrameDuration:(NSTimeInterval)oneFrameDuration 54 | loopCount:(NSUInteger)loopCount; 55 | 56 | /** 57 | Create a frame animated image from files. 58 | 59 | @param paths An array of NSString objects, contains the full or 60 | partial path to each image file. 61 | e.g. @[@"/ani/frame1.png",@"/ani/frame2.png",@"/ani/frame3.png"] 62 | 63 | @param frameDurations An array of NSNumber objects, contains the duration (in seconds) per frame. 64 | e.g. @[@0.1, @0.2, @0.3]; 65 | 66 | @param loopCount The animation loop count, 0 means infinite. 67 | 68 | @return An initialized YYFrameImage object, or nil when an error occurs. 69 | */ 70 | - (nullable instancetype)initWithImagePaths:(NSArray *)paths 71 | frameDurations:(NSArray *)frameDurations 72 | loopCount:(NSUInteger)loopCount; 73 | 74 | /** 75 | Create a frame animated image from an array of data. 76 | 77 | @param dataArray An array of NSData objects. 78 | 79 | @param oneFrameDuration The duration (in seconds) per frame. 80 | 81 | @param loopCount The animation loop count, 0 means infinite. 82 | 83 | @return An initialized YYFrameImage object, or nil when an error occurs. 84 | */ 85 | - (nullable instancetype)initWithImageDataArray:(NSArray *)dataArray 86 | oneFrameDuration:(NSTimeInterval)oneFrameDuration 87 | loopCount:(NSUInteger)loopCount; 88 | 89 | /** 90 | Create a frame animated image from an array of data. 91 | 92 | @param dataArray An array of NSData objects. 93 | 94 | @param frameDurations An array of NSNumber objects, contains the duration (in seconds) per frame. 95 | e.g. @[@0.1, @0.2, @0.3]; 96 | 97 | @param loopCount The animation loop count, 0 means infinite. 98 | 99 | @return An initialized YYFrameImage object, or nil when an error occurs. 100 | */ 101 | - (nullable instancetype)initWithImageDataArray:(NSArray *)dataArray 102 | frameDurations:(NSArray *)frameDurations 103 | loopCount:(NSUInteger)loopCount; 104 | 105 | @end 106 | 107 | NS_ASSUME_NONNULL_END 108 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/YYFrameImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYFrameImage.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/12/9. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYFrameImage.h" 13 | #import "NSString+YYAdd.h" 14 | #import "UIImage+YYAdd.h" 15 | #import "YYImageCoder.h" 16 | 17 | @implementation YYFrameImage { 18 | NSUInteger _loopCount; 19 | NSUInteger _oneFrameBytes; 20 | NSArray *_imagePaths; 21 | NSArray *_imageDatas; 22 | NSArray *_frameDurations; 23 | } 24 | 25 | - (instancetype)initWithImagePaths:(NSArray *)paths oneFrameDuration:(NSTimeInterval)oneFrameDuration loopCount:(NSUInteger)loopCount { 26 | NSMutableArray *durations = [NSMutableArray new]; 27 | for (int i = 0, max = (int)paths.count; i < max; i++) { 28 | [durations addObject:@(oneFrameDuration)]; 29 | } 30 | return [self initWithImagePaths:paths frameDurations:durations loopCount:loopCount]; 31 | } 32 | 33 | - (instancetype)initWithImagePaths:(NSArray *)paths frameDurations:(NSArray *)frameDurations loopCount:(NSUInteger)loopCount { 34 | if (paths.count == 0) return nil; 35 | if (paths.count != frameDurations.count) return nil; 36 | 37 | NSString *firstPath = paths[0]; 38 | NSData *firstData = [NSData dataWithContentsOfFile:firstPath]; 39 | CGFloat scale = firstPath.pathScale; 40 | UIImage *firstCG = [[[UIImage alloc] initWithData:firstData] imageByDecoded]; 41 | self = [self initWithCGImage:firstCG.CGImage scale:scale orientation:UIImageOrientationUp]; 42 | if (!self) return nil; 43 | long frameByte = CGImageGetBytesPerRow(firstCG.CGImage) * CGImageGetHeight(firstCG.CGImage); 44 | _oneFrameBytes = (NSUInteger)frameByte; 45 | _imagePaths = paths.copy; 46 | _frameDurations = frameDurations.copy; 47 | _loopCount = loopCount; 48 | 49 | return self; 50 | } 51 | 52 | - (instancetype)initWithImageDataArray:(NSArray *)dataArray oneFrameDuration:(NSTimeInterval)oneFrameDuration loopCount:(NSUInteger)loopCount { 53 | NSMutableArray *durations = [NSMutableArray new]; 54 | for (int i = 0, max = (int)dataArray.count; i < max; i++) { 55 | [durations addObject:@(oneFrameDuration)]; 56 | } 57 | return [self initWithImageDataArray:dataArray frameDurations:durations loopCount:loopCount]; 58 | } 59 | 60 | - (instancetype)initWithImageDataArray:(NSArray *)dataArray frameDurations:(NSArray *)frameDurations loopCount:(NSUInteger)loopCount { 61 | if (dataArray.count == 0) return nil; 62 | if (dataArray.count != frameDurations.count) return nil; 63 | 64 | NSData *firstData = dataArray[0]; 65 | CGFloat scale = [UIScreen mainScreen].scale; 66 | UIImage *firstCG = [[[UIImage alloc] initWithData:firstData] imageByDecoded]; 67 | self = [self initWithCGImage:firstCG.CGImage scale:scale orientation:UIImageOrientationUp]; 68 | if (!self) return nil; 69 | long frameByte = CGImageGetBytesPerRow(firstCG.CGImage) * CGImageGetHeight(firstCG.CGImage); 70 | _oneFrameBytes = (NSUInteger)frameByte; 71 | _imageDatas = dataArray.copy; 72 | _frameDurations = frameDurations.copy; 73 | _loopCount = loopCount; 74 | 75 | return self; 76 | } 77 | 78 | #pragma mark - YYAnimtedImage 79 | 80 | - (NSUInteger)animatedImageFrameCount { 81 | if (_imagePaths) { 82 | return _imagePaths.count; 83 | } else if (_imageDatas) { 84 | return _imageDatas.count; 85 | } else { 86 | return 1; 87 | } 88 | } 89 | 90 | - (NSUInteger)animatedImageLoopCount { 91 | return _loopCount; 92 | } 93 | 94 | - (NSUInteger)animatedImageBytesPerFrame { 95 | return _oneFrameBytes; 96 | } 97 | 98 | - (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index { 99 | if (_imagePaths) { 100 | if (index >= _imagePaths.count) return nil; 101 | NSString *path = _imagePaths[index]; 102 | CGFloat scale = [path pathScale]; 103 | NSData *data = [NSData dataWithContentsOfFile:path]; 104 | return [[UIImage imageWithData:data scale:scale] imageByDecoded]; 105 | } else if (_imageDatas) { 106 | if (index >= _imageDatas.count) return nil; 107 | NSData *data = _imageDatas[index]; 108 | return [[UIImage imageWithData:data scale:[UIScreen mainScreen].scale] imageByDecoded]; 109 | } else { 110 | return index == 0 ? self : nil; 111 | } 112 | } 113 | 114 | - (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index { 115 | if (index >= _frameDurations.count) return 0; 116 | NSNumber *num = _frameDurations[index]; 117 | return [num doubleValue]; 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/YYImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYImage.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/20. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | #import 16 | #import 17 | #else 18 | #import "YYAnimatedImageView.h" 19 | #import "YYImageCoder.h" 20 | #endif 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | /** 25 | A YYImage object is a high-level way to display animated image data. 26 | 27 | @discussion It is a fully compatible `UIImage` subclass. It extends the UIImage 28 | to support animated WebP, APNG and GIF format image data decoding. It also 29 | support NSCoding protocol to archive and unarchive multi-frame image data. 30 | 31 | If the image is created from multi-frame image data, and you want to play the 32 | animation, try replace UIImageView with `YYAnimatedImageView`. 33 | 34 | Sample Code: 35 | 36 | // animation@3x.webp 37 | YYImage *image = [YYImage imageNamed:@"animation.webp"]; 38 | YYAnimatedImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image]; 39 | [view addSubView:imageView]; 40 | 41 | */ 42 | @interface YYImage : UIImage 43 | 44 | + (nullable YYImage *)imageNamed:(NSString *)name; // no cache! 45 | + (nullable YYImage *)imageWithContentsOfFile:(NSString *)path; 46 | + (nullable YYImage *)imageWithData:(NSData *)data; 47 | + (nullable YYImage *)imageWithData:(NSData *)data scale:(CGFloat)scale; 48 | 49 | /** 50 | If the image is created from data or file, then the value indicates the data type. 51 | */ 52 | @property (nonatomic, readonly) YYImageType animatedImageType; 53 | 54 | /** 55 | If the image is created from animated image data (multi-frame GIF/APNG/WebP), 56 | this property stores the original image data. 57 | */ 58 | @property (nullable, nonatomic, readonly) NSData *animatedImageData; 59 | 60 | /** 61 | The total memory usage (in bytes) if all frame images was loaded into memory. 62 | The value is 0 if the image is not created from a multi-frame image data. 63 | */ 64 | @property (nonatomic, readonly) NSUInteger animatedImageMemorySize; 65 | 66 | /** 67 | Preload all frame image to memory. 68 | 69 | @discussion Set this property to `YES` will block the calling thread to decode 70 | all animation frame image to memory, set to `NO` will release the preloaded frames. 71 | If the image is shared by lots of image views (such as emoticon), preload all 72 | frames will reduce the CPU cost. 73 | 74 | See `animatedImageMemorySize` for memory cost. 75 | */ 76 | @property (nonatomic) BOOL preloadAllAnimatedImageFrames; 77 | 78 | @end 79 | 80 | NS_ASSUME_NONNULL_END 81 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/YYSpriteSheetImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYSpriteImage.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/21. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | #import 16 | #else 17 | #import "YYAnimatedImageView.h" 18 | #endif 19 | 20 | NS_ASSUME_NONNULL_BEGIN 21 | 22 | /** 23 | An image to display sprite sheet animation. 24 | 25 | @discussion It is a fully compatible `UIImage` subclass. 26 | The animation can be played by YYAnimatedImageView. 27 | 28 | Sample Code: 29 | 30 | // 8 * 12 sprites in a single sheet image 31 | UIImage *spriteSheet = [UIImage imageNamed:@"sprite-sheet"]; 32 | NSMutableArray *contentRects = [NSMutableArray new]; 33 | NSMutableArray *durations = [NSMutableArray new]; 34 | for (int j = 0; j < 12; j++) { 35 | for (int i = 0; i < 8; i++) { 36 | CGRect rect; 37 | rect.size = CGSizeMake(img.size.width / 8, img.size.height / 12); 38 | rect.origin.x = img.size.width / 8 * i; 39 | rect.origin.y = img.size.height / 12 * j; 40 | [contentRects addObject:[NSValue valueWithCGRect:rect]]; 41 | [durations addObject:@(1 / 60.0)]; 42 | } 43 | } 44 | YYSpriteSheetImage *sprite; 45 | sprite = [[YYSpriteSheetImage alloc] initWithSpriteSheetImage:img 46 | contentRects:contentRects 47 | frameDurations:durations 48 | loopCount:0]; 49 | YYAnimatedImageView *imgView = [YYAnimatedImageView new]; 50 | imgView.size = CGSizeMake(img.size.width / 8, img.size.height / 12); 51 | imgView.image = sprite; 52 | 53 | 54 | 55 | @discussion It can also be used to display single frame in sprite sheet image. 56 | Sample Code: 57 | 58 | YYSpriteSheetImage *sheet = ...; 59 | UIImageView *imageView = ...; 60 | imageView.image = sheet; 61 | imageView.layer.contentsRect = [sheet contentsRectForCALayerAtIndex:6]; 62 | 63 | */ 64 | @interface YYSpriteSheetImage : UIImage 65 | 66 | /** 67 | Creates and returns an image object. 68 | 69 | @param image The sprite sheet image (contains all frames). 70 | 71 | @param contentRects The sprite sheet image frame rects in the image coordinates. 72 | The rectangle should not outside the image's bounds. The objects in this array 73 | should be created with [NSValue valueWithCGRect:]. 74 | 75 | @param frameDurations The sprite sheet image frame's durations in seconds. 76 | The objects in this array should be NSNumber. 77 | 78 | @param loopCount Animation loop count, 0 means infinite looping. 79 | 80 | @return An image object, or nil if an error occurs. 81 | */ 82 | - (nullable instancetype)initWithSpriteSheetImage:(UIImage *)image 83 | contentRects:(NSArray *)contentRects 84 | frameDurations:(NSArray *)frameDurations 85 | loopCount:(NSUInteger)loopCount; 86 | 87 | @property (nonatomic, readonly) NSArray *contentRects; 88 | @property (nonatomic, readonly) NSArray *frameDurations; 89 | @property (nonatomic, readonly) NSUInteger loopCount; 90 | 91 | /** 92 | Get the contents rect for CALayer. 93 | See "contentsRect" property in CALayer for more information. 94 | 95 | @param index Index of frame. 96 | @return Contents Rect. 97 | */ 98 | - (CGRect)contentsRectForCALayerAtIndex:(NSUInteger)index; 99 | 100 | @end 101 | 102 | NS_ASSUME_NONNULL_END 103 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/YYSpriteSheetImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYSpriteImage.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/21. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYSpriteSheetImage.h" 13 | 14 | @implementation YYSpriteSheetImage 15 | 16 | - (instancetype)initWithSpriteSheetImage:(UIImage *)image 17 | contentRects:(NSArray *)contentRects 18 | frameDurations:(NSArray *)frameDurations 19 | loopCount:(NSUInteger)loopCount { 20 | if (!image.CGImage) return nil; 21 | if (contentRects.count < 1 || frameDurations.count < 1) return nil; 22 | if (contentRects.count != frameDurations.count) return nil; 23 | 24 | self = [super initWithCGImage:image.CGImage scale:image.scale orientation:image.imageOrientation]; 25 | if (!self) return nil; 26 | 27 | _contentRects = contentRects.copy; 28 | _frameDurations = frameDurations.copy; 29 | _loopCount = loopCount; 30 | return self; 31 | } 32 | 33 | - (CGRect)contentsRectForCALayerAtIndex:(NSUInteger)index { 34 | CGRect layerRect = CGRectMake(0, 0, 1, 1); 35 | if (index >= _contentRects.count) return layerRect; 36 | 37 | CGSize imageSize = self.size; 38 | CGRect rect = [self animatedImageContentsRectAtIndex:index]; 39 | if (imageSize.width > 0.01 && imageSize.height > 0.01) { 40 | layerRect.origin.x = rect.origin.x / imageSize.width; 41 | layerRect.origin.y = rect.origin.y / imageSize.height; 42 | layerRect.size.width = rect.size.width / imageSize.width; 43 | layerRect.size.height = rect.size.height / imageSize.height; 44 | layerRect = CGRectIntersection(layerRect, CGRectMake(0, 0, 1, 1)); 45 | if (CGRectIsNull(layerRect) || CGRectIsEmpty(layerRect)) { 46 | layerRect = CGRectMake(0, 0, 1, 1); 47 | } 48 | } 49 | return layerRect; 50 | } 51 | 52 | #pragma mark @protocol YYAnimatedImage 53 | 54 | - (NSUInteger)animatedImageFrameCount { 55 | return _contentRects.count; 56 | } 57 | 58 | - (NSUInteger)animatedImageLoopCount { 59 | return _loopCount; 60 | } 61 | 62 | - (NSUInteger)animatedImageBytesPerFrame { 63 | return 0; 64 | } 65 | 66 | - (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index { 67 | return self; 68 | } 69 | 70 | - (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index { 71 | if (index >= _frameDurations.count) return 0; 72 | return ((NSNumber *)_frameDurations[index]).doubleValue; 73 | } 74 | 75 | - (CGRect)animatedImageContentsRectAtIndex:(NSUInteger)index { 76 | if (index >= _contentRects.count) return CGRectZero; 77 | return ((NSValue *)_contentRects[index]).CGRectValue; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/YYWebImageManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYWebImageManager.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/19. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYWebImageManager.h" 13 | #import "YYImageCache.h" 14 | #import "YYWebImageOperation.h" 15 | #import "YYImageCoder.h" 16 | 17 | @implementation YYWebImageManager 18 | 19 | + (instancetype)sharedManager { 20 | static YYWebImageManager *manager; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | YYImageCache *cache = [YYImageCache sharedCache]; 24 | NSOperationQueue *queue = [NSOperationQueue new]; 25 | if ([queue respondsToSelector:@selector(setQualityOfService:)]) { 26 | queue.qualityOfService = NSQualityOfServiceBackground; 27 | } 28 | manager = [[self alloc] initWithCache:cache queue:queue]; 29 | }); 30 | return manager; 31 | } 32 | 33 | - (instancetype)init { 34 | @throw [NSException exceptionWithName:@"YYWebImageManager init error" reason:@"Use the designated initializer to init." userInfo:nil]; 35 | return [self initWithCache:nil queue:nil]; 36 | } 37 | 38 | - (instancetype)initWithCache:(YYImageCache *)cache queue:(NSOperationQueue *)queue{ 39 | self = [super init]; 40 | if (!self) return nil; 41 | _cache = cache; 42 | _queue = queue; 43 | _timeout = 15.0; 44 | if (YYImageWebPAvailable()) { 45 | _headers = @{ @"Accept" : @"image/webp,image/*;q=0.8" }; 46 | } else { 47 | _headers = @{ @"Accept" : @"image/*;q=0.8" }; 48 | } 49 | return self; 50 | } 51 | 52 | - (YYWebImageOperation *)requestImageWithURL:(NSURL *)url 53 | options:(YYWebImageOptions)options 54 | progress:(YYWebImageProgressBlock)progress 55 | transform:(YYWebImageTransformBlock)transform 56 | completion:(YYWebImageCompletionBlock)completion { 57 | 58 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 59 | request.timeoutInterval = _timeout; 60 | request.HTTPShouldHandleCookies = (options & YYWebImageOptionHandleCookies) != 0; 61 | request.allHTTPHeaderFields = [self headersForURL:url]; 62 | request.HTTPShouldUsePipelining = YES; 63 | request.cachePolicy = (options & YYWebImageOptionUseNSURLCache) ? 64 | NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData; 65 | 66 | YYWebImageOperation *operation = [[YYWebImageOperation alloc] initWithRequest:request 67 | options:options 68 | cache:_cache 69 | cacheKey:[self cacheKeyForURL:url] 70 | progress:progress 71 | transform:transform ? transform : _sharedTransformBlock 72 | completion:completion]; 73 | 74 | if (_username && _password) { 75 | operation.credential = [NSURLCredential credentialWithUser:_username password:_password persistence:NSURLCredentialPersistenceForSession]; 76 | } 77 | if (operation) { 78 | NSOperationQueue *queue = _queue; 79 | if (queue) { 80 | [queue addOperation:operation]; 81 | } else { 82 | [operation start]; 83 | } 84 | } 85 | return operation; 86 | } 87 | 88 | - (NSDictionary *)headersForURL:(NSURL *)url { 89 | if (!url) return nil; 90 | return _headersFilter ? _headersFilter(url, _headers) : _headers; 91 | } 92 | 93 | - (NSString *)cacheKeyForURL:(NSURL *)url { 94 | if (!url) return nil; 95 | return _cacheKeyFilter ? _cacheKeyFilter(url) : url.absoluteString; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Image/YYWebImageOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYWebImageOperation.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/15. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | #import 16 | #import 17 | #else 18 | #import "YYImageCache.h" 19 | #import "YYWebImageManager.h" 20 | #endif 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | /** 25 | The YYWebImageOperation class is an NSOperation subclass used to fetch image 26 | from URL request. 27 | 28 | @discussion It's an asynchronous operation. You typically execute it by adding 29 | it to an operation queue, or calls 'start' to execute it manually. When the 30 | operation is started, it will: 31 | 32 | 1. Get the image from the cache, if exist, return it with `completion` block. 33 | 2. Start an URL connection to fetch image from the request, invoke the `progress` 34 | to notify request progress (and invoke `completion` block to return the 35 | progressive image if enabled by progressive option). 36 | 3. Process the image by invoke the `transform` block. 37 | 4. Put the image to cache and return it with `completion` block. 38 | 39 | */ 40 | @interface YYWebImageOperation : NSOperation 41 | 42 | @property (nonatomic, strong, readonly) NSURLRequest *request; ///< The image URL request. 43 | @property (nullable, nonatomic, strong, readonly) NSURLResponse *response; ///< The response for request. 44 | @property (nullable, nonatomic, strong, readonly) YYImageCache *cache; ///< The image cache. 45 | @property (nonatomic, strong, readonly) NSString *cacheKey; ///< The image cache key. 46 | @property (nonatomic, readonly) YYWebImageOptions options; ///< The operation's option. 47 | 48 | /** 49 | Whether the URL connection should consult the credential storage for authenticating 50 | the connection. Default is YES. 51 | 52 | @discussion This is the value that is returned in the `NSURLConnectionDelegate` 53 | method `-connectionShouldUseCredentialStorage:`. 54 | */ 55 | @property (nonatomic) BOOL shouldUseCredentialStorage; 56 | 57 | /** 58 | The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. 59 | 60 | @discussion This will be overridden by any shared credentials that exist for the 61 | username or password of the request URL, if present. 62 | */ 63 | @property (nullable, nonatomic, strong) NSURLCredential *credential; 64 | 65 | /** 66 | Creates and returns a new operation. 67 | 68 | You should call `start` to execute this operation, or you can add the operation 69 | to an operation queue. 70 | 71 | @param request The Image request. This value should not be nil. 72 | @param options A mask to specify options to use for this operation. 73 | @param cache An image cache. Pass nil to avoid image cache. 74 | @param cacheKey An image cache key. Pass nil to avoid image cache. 75 | @param progress A block invoked in image fetch progress. 76 | The block will be invoked in background thread. Pass nil to avoid it. 77 | @param transform A block invoked before image fetch finished to do additional image process. 78 | The block will be invoked in background thread. Pass nil to avoid it. 79 | @param completion A block invoked when image fetch finished or cancelled. 80 | The block will be invoked in background thread. Pass nil to avoid it. 81 | 82 | @return The image request opeartion, or nil if an error occurs. 83 | */ 84 | - (instancetype)initWithRequest:(NSURLRequest *)request 85 | options:(YYWebImageOptions)options 86 | cache:(nullable YYImageCache *)cache 87 | cacheKey:(nullable NSString *)cacheKey 88 | progress:(nullable YYWebImageProgressBlock)progress 89 | transform:(nullable YYWebImageTransformBlock)transform 90 | completion:(nullable YYWebImageCompletionBlock)completion NS_DESIGNATED_INITIALIZER; 91 | 92 | - (instancetype)init UNAVAILABLE_ATTRIBUTE; 93 | + (instancetype)new UNAVAILABLE_ATTRIBUTE; 94 | 95 | @end 96 | 97 | NS_ASSUME_NONNULL_END 98 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/Component/YYTextContainerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextContainerView.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/21. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | #import 16 | #else 17 | #import "YYTextLayout.h" 18 | #endif 19 | 20 | NS_ASSUME_NONNULL_BEGIN 21 | 22 | /** 23 | A simple view to diaplay `YYTextLayout`. 24 | 25 | @discussion This view can become first responder. If this view is first responder, 26 | all the action (such as UIMenu's action) would forward to the `hostView` property. 27 | Typically, you should not use this class directly. 28 | 29 | @warning All the methods in this class should be called on main thread. 30 | */ 31 | @interface YYTextContainerView : UIView 32 | 33 | /// First responder's aciton will forward to this view. 34 | @property (nullable, nonatomic, weak) UIView *hostView; 35 | 36 | /// Debug option for layout debug. Set this property will let the view redraw it's contents. 37 | @property (nullable, nonatomic, copy) YYTextDebugOption *debugOption; 38 | 39 | /// Text vertical alignment. 40 | @property (nonatomic) YYTextVerticalAlignment textVerticalAlignment; 41 | 42 | /// Text layout. Set this property will let the view redraw it's contents. 43 | @property (nullable, nonatomic, strong) YYTextLayout *layout; 44 | 45 | /// The contents fade animation duration when the layout's contents changed. Default is 0 (no animation). 46 | @property (nonatomic) NSTimeInterval contentsFadeDuration; 47 | 48 | /// Convenience method to set `layout` and `contentsFadeDuration`. 49 | /// @param layout Same as `layout` property. 50 | /// @param fadeDuration Same as `contentsFadeDuration` property. 51 | - (void)setLayout:(nullable YYTextLayout *)layout withFadeDuration:(NSTimeInterval)fadeDuration; 52 | 53 | @end 54 | 55 | NS_ASSUME_NONNULL_END 56 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/Component/YYTextDebugOption.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextDebugOption.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/8. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | @class YYTextDebugOption; 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | /** 19 | The YYTextDebugTarget protocol defines the method a debug target should implement. 20 | A debug target can be add to the global container to receive the shared debug 21 | option changed notification. 22 | */ 23 | @protocol YYTextDebugTarget 24 | 25 | @required 26 | /** 27 | When the shared debug option changed, this method would be called on main thread. 28 | It should return as quickly as possible. The option's property should not be changed 29 | in this method. 30 | 31 | @param option The shared debug option. 32 | */ 33 | - (void)setDebugOption:(nullable YYTextDebugOption *)option; 34 | @end 35 | 36 | 37 | 38 | /** 39 | The debug option for YYText. 40 | */ 41 | @interface YYTextDebugOption : NSObject 42 | @property (nullable, nonatomic, strong) UIColor *baselineColor; ///< baseline color 43 | @property (nullable, nonatomic, strong) UIColor *CTFrameBorderColor; ///< CTFrame path border color 44 | @property (nullable, nonatomic, strong) UIColor *CTFrameFillColor; ///< CTFrame path fill color 45 | @property (nullable, nonatomic, strong) UIColor *CTLineBorderColor; ///< CTLine bounds border color 46 | @property (nullable, nonatomic, strong) UIColor *CTLineFillColor; ///< CTLine bounds fill color 47 | @property (nullable, nonatomic, strong) UIColor *CTLineNumberColor; ///< CTLine line number color 48 | @property (nullable, nonatomic, strong) UIColor *CTRunBorderColor; ///< CTRun bounds border color 49 | @property (nullable, nonatomic, strong) UIColor *CTRunFillColor; ///< CTRun bounds fill color 50 | @property (nullable, nonatomic, strong) UIColor *CTRunNumberColor; ///< CTRun number color 51 | @property (nullable, nonatomic, strong) UIColor *CGGlyphBorderColor; ///< CGGlyph bounds border color 52 | @property (nullable, nonatomic, strong) UIColor *CGGlyphFillColor; ///< CGGlyph bounds fill color 53 | 54 | - (BOOL)needDrawDebug; ///< `YES`: at least one debug color is visible. `NO`: all debug color is invisible/nil. 55 | - (void)clear; ///< Set all debug color to nil. 56 | 57 | /** 58 | Add a debug target. 59 | 60 | @discussion When `setSharedDebugOption:` is called, all added debug target will 61 | receive `setDebugOption:` in main thread. It maintains an unsafe_unretained 62 | reference to this target. The target must to removed before dealloc. 63 | 64 | @param target A debug target. 65 | */ 66 | + (void)addDebugTarget:(id)target; 67 | 68 | /** 69 | Remove a debug target which is added by `addDebugTarget:`. 70 | 71 | @param target A debug target. 72 | */ 73 | + (void)removeDebugTarget:(id)target; 74 | 75 | /** 76 | Returns the shared debug option. 77 | 78 | @return The shared debug option, default is nil. 79 | */ 80 | + (nullable YYTextDebugOption *)sharedDebugOption; 81 | 82 | /** 83 | Set a debug option as shared debug option. 84 | This method must be called on main thread. 85 | 86 | @discussion When call this method, the new option will set to all debug target 87 | which is added by `addDebugTarget:`. 88 | 89 | @param option A new debug option (nil is valid). 90 | */ 91 | + (void)setSharedDebugOption:(nullable YYTextDebugOption *)option; 92 | 93 | @end 94 | 95 | NS_ASSUME_NONNULL_END 96 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/Component/YYTextDebugOption.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextDebugOption.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/8. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYTextDebugOption.h" 13 | #import "YYKitMacro.h" 14 | #import "UIColor+YYAdd.h" 15 | #import "YYWeakProxy.h" 16 | 17 | 18 | static pthread_mutex_t _sharedDebugLock; 19 | static CFMutableSetRef _sharedDebugTargets = nil; 20 | static YYTextDebugOption *_sharedDebugOption = nil; 21 | 22 | static const void* _sharedDebugSetRetain(CFAllocatorRef allocator, const void *value) { 23 | return value; 24 | } 25 | 26 | static void _sharedDebugSetRelease(CFAllocatorRef allocator, const void *value) { 27 | } 28 | 29 | void _sharedDebugSetFunction(const void *value, void *context) { 30 | id target = (__bridge id)(value); 31 | [target setDebugOption:_sharedDebugOption]; 32 | } 33 | 34 | static void _initSharedDebug() { 35 | static dispatch_once_t onceToken; 36 | dispatch_once(&onceToken, ^{ 37 | pthread_mutex_init(&_sharedDebugLock, NULL); 38 | CFSetCallBacks callbacks = kCFTypeSetCallBacks; 39 | callbacks.retain = _sharedDebugSetRetain; 40 | callbacks.release = _sharedDebugSetRelease; 41 | _sharedDebugTargets = CFSetCreateMutable(CFAllocatorGetDefault(), 0, &callbacks); 42 | }); 43 | } 44 | 45 | static void _setSharedDebugOption(YYTextDebugOption *option) { 46 | _initSharedDebug(); 47 | pthread_mutex_lock(&_sharedDebugLock); 48 | _sharedDebugOption = option.copy; 49 | CFSetApplyFunction(_sharedDebugTargets, _sharedDebugSetFunction, NULL); 50 | pthread_mutex_unlock(&_sharedDebugLock); 51 | } 52 | 53 | static YYTextDebugOption *_getSharedDebugOption() { 54 | _initSharedDebug(); 55 | pthread_mutex_lock(&_sharedDebugLock); 56 | YYTextDebugOption *op = _sharedDebugOption; 57 | pthread_mutex_unlock(&_sharedDebugLock); 58 | return op; 59 | } 60 | 61 | static void _addDebugTarget(id target) { 62 | _initSharedDebug(); 63 | pthread_mutex_lock(&_sharedDebugLock); 64 | CFSetAddValue(_sharedDebugTargets, (__bridge const void *)(target)); 65 | pthread_mutex_unlock(&_sharedDebugLock); 66 | } 67 | 68 | static void _removeDebugTarget(id target) { 69 | _initSharedDebug(); 70 | pthread_mutex_lock(&_sharedDebugLock); 71 | CFSetRemoveValue(_sharedDebugTargets, (__bridge const void *)(target)); 72 | pthread_mutex_unlock(&_sharedDebugLock); 73 | } 74 | 75 | 76 | @implementation YYTextDebugOption 77 | 78 | - (id)copyWithZone:(NSZone *)zone { 79 | YYTextDebugOption *op = [self.class new]; 80 | op.baselineColor = self.baselineColor; 81 | op.CTFrameBorderColor = self.CTFrameBorderColor; 82 | op.CTFrameFillColor = self.CTFrameFillColor; 83 | op.CTLineBorderColor = self.CTLineBorderColor; 84 | op.CTLineFillColor = self.CTLineFillColor; 85 | op.CTLineNumberColor = self.CTLineNumberColor; 86 | op.CTRunBorderColor = self.CTRunBorderColor; 87 | op.CTRunFillColor = self.CTRunFillColor; 88 | op.CTRunNumberColor = self.CTRunNumberColor; 89 | op.CGGlyphBorderColor = self.CGGlyphBorderColor; 90 | op.CGGlyphFillColor = self.CGGlyphFillColor; 91 | return op; 92 | } 93 | 94 | - (BOOL)needDrawDebug { 95 | if (self.baselineColor || 96 | self.CTFrameBorderColor || 97 | self.CTFrameFillColor || 98 | self.CTLineBorderColor || 99 | self.CTLineFillColor || 100 | self.CTLineNumberColor || 101 | self.CTRunBorderColor || 102 | self.CTRunFillColor || 103 | self.CTRunNumberColor || 104 | self.CGGlyphBorderColor || 105 | self.CGGlyphFillColor) return YES; 106 | return NO; 107 | } 108 | 109 | - (void)clear { 110 | self.baselineColor = nil; 111 | self.CTFrameBorderColor = nil; 112 | self.CTFrameFillColor = nil; 113 | self.CTLineBorderColor = nil; 114 | self.CTLineFillColor = nil; 115 | self.CTLineNumberColor = nil; 116 | self.CTRunBorderColor = nil; 117 | self.CTRunFillColor = nil; 118 | self.CTRunNumberColor = nil; 119 | self.CGGlyphBorderColor = nil; 120 | self.CGGlyphFillColor = nil; 121 | } 122 | 123 | + (void)addDebugTarget:(id)target { 124 | if (target) _addDebugTarget(target); 125 | } 126 | 127 | + (void)removeDebugTarget:(id)target { 128 | if (target) _removeDebugTarget(target); 129 | } 130 | 131 | + (YYTextDebugOption *)sharedDebugOption { 132 | return _getSharedDebugOption(); 133 | } 134 | 135 | + (void)setSharedDebugOption:(YYTextDebugOption *)option { 136 | YYAssertMainThread(); 137 | _setSharedDebugOption(option); 138 | } 139 | 140 | @end 141 | 142 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/Component/YYTextEffectWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextEffectWindow.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/25. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | #import 16 | #import 17 | #else 18 | #import "YYTextMagnifier.h" 19 | #import "YYTextSelectionView.h" 20 | #endif 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | /** 25 | A window to display magnifier and extra contents for text view. 26 | 27 | @discussion Use `sharedWindow` to get the instance, don't create your own instance. 28 | Typically, you should not use this class directly. 29 | */ 30 | @interface YYTextEffectWindow : UIWindow 31 | 32 | /// Returns the shared instance (returns nil in App Extension). 33 | + (nullable instancetype)sharedWindow; 34 | 35 | /// Show the magnifier in this window with a 'popup' animation. @param mag A magnifier. 36 | - (void)showMagnifier:(YYTextMagnifier *)mag; 37 | /// Update the magnifier content and position. @param mag A magnifier. 38 | - (void)moveMagnifier:(YYTextMagnifier *)mag; 39 | /// Remove the magnifier from this window with a 'shrink' animation. @param mag A magnifier. 40 | - (void)hideMagnifier:(YYTextMagnifier *)mag; 41 | 42 | 43 | /// Show the selection dot in this window if the dot is clipped by the selection view. 44 | /// @param selection A selection view. 45 | - (void)showSelectionDot:(YYTextSelectionView *)selection; 46 | /// Remove the selection dot from this window. 47 | /// @param selection A selection view. 48 | - (void)hideSelectionDot:(YYTextSelectionView *)selection; 49 | 50 | @end 51 | 52 | NS_ASSUME_NONNULL_END 53 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/Component/YYTextInput.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextInput.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/17. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Text position affinity. For example, the offset appears after the last 18 | character on a line is backward affinity, before the first character on 19 | the following line is forward affinity. 20 | */ 21 | typedef NS_ENUM(NSInteger, YYTextAffinity) { 22 | YYTextAffinityForward = 0, ///< offset appears before the character 23 | YYTextAffinityBackward = 1, ///< offset appears after the character 24 | }; 25 | 26 | 27 | /** 28 | A YYTextPosition object represents a position in a text container; in other words, 29 | it is an index into the backing string in a text-displaying view. 30 | 31 | YYTextPosition has the same API as Apple's implementation in UITextView/UITextField, 32 | so you can alse use it to interact with UITextView/UITextField. 33 | */ 34 | @interface YYTextPosition : UITextPosition 35 | 36 | @property (nonatomic, readonly) NSInteger offset; 37 | @property (nonatomic, readonly) YYTextAffinity affinity; 38 | 39 | + (instancetype)positionWithOffset:(NSInteger)offset; 40 | + (instancetype)positionWithOffset:(NSInteger)offset affinity:(YYTextAffinity) affinity; 41 | 42 | - (NSComparisonResult)compare:(id)otherPosition; 43 | 44 | @end 45 | 46 | 47 | /** 48 | A YYTextRange object represents a range of characters in a text container; in other words, 49 | it identifies a starting index and an ending index in string backing a text-displaying view. 50 | 51 | YYTextRange has the same API as Apple's implementation in UITextView/UITextField, 52 | so you can alse use it to interact with UITextView/UITextField. 53 | */ 54 | @interface YYTextRange : UITextRange 55 | 56 | @property (nonatomic, readonly) YYTextPosition *start; 57 | @property (nonatomic, readonly) YYTextPosition *end; 58 | @property (nonatomic, readonly, getter=isEmpty) BOOL empty; 59 | 60 | + (instancetype)rangeWithRange:(NSRange)range; 61 | + (instancetype)rangeWithRange:(NSRange)range affinity:(YYTextAffinity) affinity; 62 | + (instancetype)rangeWithStart:(YYTextPosition *)start end:(YYTextPosition *)end; 63 | + (instancetype)defaultRange; ///< <{0,0} Forward> 64 | 65 | - (NSRange)asRange; 66 | 67 | @end 68 | 69 | 70 | /** 71 | A YYTextSelectionRect object encapsulates information about a selected range of 72 | text in a text-displaying view. 73 | 74 | YYTextSelectionRect has the same API as Apple's implementation in UITextView/UITextField, 75 | so you can alse use it to interact with UITextView/UITextField. 76 | */ 77 | @interface YYTextSelectionRect : UITextSelectionRect 78 | 79 | @property (nonatomic, readwrite) CGRect rect; 80 | @property (nonatomic, readwrite) UITextWritingDirection writingDirection; 81 | @property (nonatomic, readwrite) BOOL containsStart; 82 | @property (nonatomic, readwrite) BOOL containsEnd; 83 | @property (nonatomic, readwrite) BOOL isVertical; 84 | 85 | @end 86 | 87 | NS_ASSUME_NONNULL_END 88 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/Component/YYTextInput.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextInput.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/17. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYTextInput.h" 13 | #import "YYKitMacro.h" 14 | 15 | @implementation YYTextPosition 16 | 17 | + (instancetype)positionWithOffset:(NSInteger)offset { 18 | return [self positionWithOffset:offset affinity:YYTextAffinityForward]; 19 | } 20 | 21 | + (instancetype)positionWithOffset:(NSInteger)offset affinity:(YYTextAffinity)affinity { 22 | YYTextPosition *p = [self new]; 23 | p->_offset = offset; 24 | p->_affinity = affinity; 25 | return p; 26 | } 27 | 28 | - (instancetype)copyWithZone:(NSZone *)zone { 29 | return [self.class positionWithOffset:_offset affinity:_affinity]; 30 | } 31 | 32 | - (NSString *)description { 33 | return [NSString stringWithFormat:@"<%@: %p> (%@%@)", self.class, self, @(_offset), _affinity == YYTextAffinityForward ? @"F":@"B"]; 34 | } 35 | 36 | - (NSUInteger)hash { 37 | return _offset * 2 + (_affinity == YYTextAffinityForward ? 1 : 0); 38 | } 39 | 40 | - (BOOL)isEqual:(YYTextPosition *)object { 41 | if (!object) return NO; 42 | return _offset == object.offset && _affinity == object.affinity; 43 | } 44 | 45 | - (NSComparisonResult)compare:(YYTextPosition *)otherPosition { 46 | if (!otherPosition) return NSOrderedAscending; 47 | if (_offset < otherPosition.offset) return NSOrderedAscending; 48 | if (_offset > otherPosition.offset) return NSOrderedDescending; 49 | if (_affinity == YYTextAffinityBackward && otherPosition.affinity == YYTextAffinityForward) return NSOrderedAscending; 50 | if (_affinity == YYTextAffinityForward && otherPosition.affinity == YYTextAffinityBackward) return NSOrderedDescending; 51 | return NSOrderedSame; 52 | } 53 | 54 | @end 55 | 56 | 57 | 58 | @implementation YYTextRange { 59 | YYTextPosition *_start; 60 | YYTextPosition *_end; 61 | } 62 | 63 | - (instancetype)init { 64 | self = [super init]; 65 | if (!self) return nil; 66 | _start = [YYTextPosition positionWithOffset:0]; 67 | _end = [YYTextPosition positionWithOffset:0]; 68 | return self; 69 | } 70 | 71 | - (YYTextPosition *)start { 72 | return _start; 73 | } 74 | 75 | - (YYTextPosition *)end { 76 | return _end; 77 | } 78 | 79 | - (BOOL)isEmpty { 80 | return _start.offset == _end.offset; 81 | } 82 | 83 | - (NSRange)asRange { 84 | return NSMakeRange(_start.offset, _end.offset - _start.offset); 85 | } 86 | 87 | + (instancetype)rangeWithRange:(NSRange)range { 88 | return [self rangeWithRange:range affinity:YYTextAffinityForward]; 89 | } 90 | 91 | + (instancetype)rangeWithRange:(NSRange)range affinity:(YYTextAffinity)affinity { 92 | YYTextPosition *start = [YYTextPosition positionWithOffset:range.location affinity:affinity]; 93 | YYTextPosition *end = [YYTextPosition positionWithOffset:range.location + range.length affinity:affinity]; 94 | return [self rangeWithStart:start end:end]; 95 | } 96 | 97 | + (instancetype)rangeWithStart:(YYTextPosition *)start end:(YYTextPosition *)end { 98 | if (!start || !end) return nil; 99 | if ([start compare:end] == NSOrderedDescending) { 100 | YY_SWAP(start, end); 101 | } 102 | YYTextRange *range = [YYTextRange new]; 103 | range->_start = start; 104 | range->_end = end; 105 | return range; 106 | } 107 | 108 | + (instancetype)defaultRange { 109 | return [self new]; 110 | } 111 | 112 | - (instancetype)copyWithZone:(NSZone *)zone { 113 | return [self.class rangeWithStart:_start end:_end]; 114 | } 115 | 116 | - (NSString *)description { 117 | return [NSString stringWithFormat:@"<%@: %p> (%@, %@)%@", self.class, self, @(_start.offset), @(_end.offset - _start.offset), _end.affinity == YYTextAffinityForward ? @"F":@"B"]; 118 | } 119 | 120 | - (NSUInteger)hash { 121 | return (sizeof(NSUInteger) == 8 ? OSSwapInt64(_start.hash) : OSSwapInt32(_start.hash)) + _end.hash; 122 | } 123 | 124 | - (BOOL)isEqual:(YYTextRange *)object { 125 | if (!object) return NO; 126 | return [_start isEqual:object.start] && [_end isEqual:object.end]; 127 | } 128 | 129 | @end 130 | 131 | 132 | 133 | @implementation YYTextSelectionRect 134 | 135 | @synthesize rect = _rect; 136 | @synthesize writingDirection = _writingDirection; 137 | @synthesize containsStart = _containsStart; 138 | @synthesize containsEnd = _containsEnd; 139 | @synthesize isVertical = _isVertical; 140 | 141 | - (id)copyWithZone:(NSZone *)zone { 142 | YYTextSelectionRect *one = [self.class new]; 143 | one.rect = _rect; 144 | one.writingDirection = _writingDirection; 145 | one.containsStart = _containsStart; 146 | one.containsEnd = _containsEnd; 147 | one.isVertical = _isVertical; 148 | return one; 149 | } 150 | 151 | @end 152 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/Component/YYTextKeyboardManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextKeyboardManager.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/6/3. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | System keyboard transition information. 18 | Use -[YYTextKeyboardManager convertRect:toView:] to convert frame to specified view. 19 | */ 20 | typedef struct { 21 | BOOL fromVisible; ///< Keyboard visible before transition. 22 | BOOL toVisible; ///< Keyboard visible after transition. 23 | CGRect fromFrame; ///< Keyboard frame before transition. 24 | CGRect toFrame; ///< Keyboard frame after transition. 25 | NSTimeInterval animationDuration; ///< Keyboard transition animation duration. 26 | UIViewAnimationCurve animationCurve; ///< Keyboard transition animation curve. 27 | UIViewAnimationOptions animationOption; ///< Keybaord transition animation option. 28 | } YYTextKeyboardTransition; 29 | 30 | 31 | /** 32 | The YYTextKeyboardObserver protocol defines the method you can use 33 | to receive system keyboard change information. 34 | */ 35 | @protocol YYTextKeyboardObserver 36 | @optional 37 | - (void)keyboardChangedWithTransition:(YYTextKeyboardTransition)transition; 38 | @end 39 | 40 | 41 | /** 42 | A YYTextKeyboardManager object lets you get the system keyboard information, 43 | and track the keyboard visible/frame/transition. 44 | 45 | @discussion You should access this class in main thread. 46 | Compatible: iPhone/iPad with iOS6/7/8/9. 47 | */ 48 | @interface YYTextKeyboardManager : NSObject 49 | 50 | - (instancetype)init UNAVAILABLE_ATTRIBUTE; 51 | + (instancetype)new UNAVAILABLE_ATTRIBUTE; 52 | 53 | /// Get the default manager (returns nil in App Extension). 54 | + (nullable instancetype)defaultManager; 55 | 56 | /// Get the keyboard window. nil if there's no keyboard window. 57 | @property (nullable, nonatomic, readonly) UIWindow *keyboardWindow; 58 | 59 | /// Get the keyboard view. nil if there's no keyboard view. 60 | @property (nullable, nonatomic, readonly) UIView *keyboardView; 61 | 62 | /// Whether the keyboard is visible. 63 | @property (nonatomic, readonly, getter=isKeyboardVisible) BOOL keyboardVisible; 64 | 65 | /// Get the keyboard frame. CGRectNull if there's no keyboard view. 66 | /// Use convertRect:toView: to convert frame to specified view. 67 | @property (nonatomic, readonly) CGRect keyboardFrame; 68 | 69 | 70 | /** 71 | Add an observer to manager to get keyboard change information. 72 | This method makes a weak reference to the observer. 73 | 74 | @param observer An observer. 75 | This method will do nothing if the observer is nil, or already added. 76 | */ 77 | - (void)addObserver:(id)observer; 78 | 79 | /** 80 | Remove an observer from manager. 81 | 82 | @param observer An observer. 83 | This method will do nothing if the observer is nil, or not in manager. 84 | */ 85 | - (void)removeObserver:(id)observer; 86 | 87 | /** 88 | Convert rect to specified view or window. 89 | 90 | @param rect The frame rect. 91 | @param view A specified view or window (pass nil to convert for main window). 92 | @return The converted rect in specifeid view. 93 | */ 94 | - (CGRect)convertRect:(CGRect)rect toView:(nullable UIView *)view; 95 | 96 | @end 97 | 98 | NS_ASSUME_NONNULL_END 99 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/Component/YYTextLine.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextLine.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/3/10. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | #if __has_include() 16 | #import 17 | #else 18 | #import "YYTextAttribute.h" 19 | #endif 20 | 21 | @class YYTextRunGlyphRange; 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | /** 26 | A text line object wrapped `CTLineRef`, see `YYTextLayout` for more. 27 | */ 28 | @interface YYTextLine : NSObject 29 | 30 | + (instancetype)lineWithCTLine:(CTLineRef)CTLine position:(CGPoint)position vertical:(BOOL)isVertical; 31 | 32 | @property (nonatomic) NSUInteger index; ///< line index 33 | @property (nonatomic) NSUInteger row; ///< line row 34 | @property (nullable, nonatomic, strong) NSArray *> *verticalRotateRange; ///< Run rotate range 35 | 36 | @property (nonatomic, readonly) CTLineRef CTLine; ///< CoreText line 37 | @property (nonatomic, readonly) NSRange range; ///< string range 38 | @property (nonatomic, readonly) BOOL vertical; ///< vertical form 39 | 40 | @property (nonatomic, readonly) CGRect bounds; ///< bounds (ascent + descent) 41 | @property (nonatomic, readonly) CGSize size; ///< bounds.size 42 | @property (nonatomic, readonly) CGFloat width; ///< bounds.size.width 43 | @property (nonatomic, readonly) CGFloat height; ///< bounds.size.height 44 | @property (nonatomic, readonly) CGFloat top; ///< bounds.origin.y 45 | @property (nonatomic, readonly) CGFloat bottom; ///< bounds.origin.y + bounds.size.height 46 | @property (nonatomic, readonly) CGFloat left; ///< bounds.origin.x 47 | @property (nonatomic, readonly) CGFloat right; ///< bounds.origin.x + bounds.size.width 48 | 49 | @property (nonatomic) CGPoint position; ///< baseline position 50 | @property (nonatomic, readonly) CGFloat ascent; ///< line ascent 51 | @property (nonatomic, readonly) CGFloat descent; ///< line descent 52 | @property (nonatomic, readonly) CGFloat leading; ///< line leading 53 | @property (nonatomic, readonly) CGFloat lineWidth; ///< line width 54 | @property (nonatomic, readonly) CGFloat trailingWhitespaceWidth; 55 | 56 | @property (nullable, nonatomic, readonly) NSArray *attachments; ///< YYTextAttachment 57 | @property (nullable, nonatomic, readonly) NSArray *attachmentRanges; ///< NSRange(NSValue) 58 | @property (nullable, nonatomic, readonly) NSArray *attachmentRects; ///< CGRect(NSValue) 59 | 60 | @end 61 | 62 | 63 | typedef NS_ENUM(NSUInteger, YYTextRunGlyphDrawMode) { 64 | /// No rotate. 65 | YYTextRunGlyphDrawModeHorizontal = 0, 66 | 67 | /// Rotate vertical for single glyph. 68 | YYTextRunGlyphDrawModeVerticalRotate = 1, 69 | 70 | /// Rotate vertical for single glyph, and move the glyph to a better position, 71 | /// such as fullwidth punctuation. 72 | YYTextRunGlyphDrawModeVerticalRotateMove = 2, 73 | }; 74 | 75 | /** 76 | A range in CTRun, used for vertical form. 77 | */ 78 | @interface YYTextRunGlyphRange : NSObject 79 | @property (nonatomic) NSRange glyphRangeInRun; 80 | @property (nonatomic) YYTextRunGlyphDrawMode drawMode; 81 | + (instancetype)rangeWithRange:(NSRange)range drawMode:(YYTextRunGlyphDrawMode)mode; 82 | @end 83 | 84 | NS_ASSUME_NONNULL_END 85 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/Component/YYTextMagnifier.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextMagnifier.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/25. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | #import 16 | #else 17 | #import "YYTextAttribute.h" 18 | #endif 19 | 20 | NS_ASSUME_NONNULL_BEGIN 21 | 22 | /// Magnifier type 23 | typedef NS_ENUM(NSInteger, YYTextMagnifierType) { 24 | YYTextMagnifierTypeCaret, ///< Circular magnifier 25 | YYTextMagnifierTypeRanged, ///< Round rectangle magnifier 26 | }; 27 | 28 | /** 29 | A magnifier view which can be displayed in `YYTextEffectWindow`. 30 | 31 | @discussion Use `magnifierWithType:` to create instance. 32 | Typically, you should not use this class directly. 33 | */ 34 | @interface YYTextMagnifier : UIView 35 | 36 | /// Create a mangifier with the specified type. @param type The magnifier type. 37 | + (id)magnifierWithType:(YYTextMagnifierType)type; 38 | 39 | @property (nonatomic, readonly) YYTextMagnifierType type; ///< Type of magnifier 40 | @property (nonatomic, readonly) CGSize fitSize; ///< The 'best' size for magnifier view. 41 | @property (nonatomic, readonly) CGSize snapshotSize; ///< The 'best' snapshot image size for magnifier. 42 | @property (nullable, nonatomic, strong) UIImage *snapshot; ///< The image in magnifier (readwrite). 43 | 44 | @property (nullable, nonatomic, weak) UIView *hostView; ///< The coordinate based view. 45 | @property (nonatomic) CGPoint hostCaptureCenter; ///< The snapshot capture center in `hostView`. 46 | @property (nonatomic) CGPoint hostPopoverCenter; ///< The popover center in `hostView`. 47 | @property (nonatomic) BOOL hostVerticalForm; ///< The host view is vertical form. 48 | @property (nonatomic) BOOL captureDisabled; ///< A hint for `YYTextEffectWindow` to disable capture. 49 | @property (nonatomic) BOOL captureFadeAnimation; ///< Show fade animation when the snapshot image changed. 50 | @end 51 | 52 | NS_ASSUME_NONNULL_END 53 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/Component/YYTextSelectionView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextSelectionView.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/25. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | #import 16 | #import 17 | #else 18 | #import "YYTextAttribute.h" 19 | #import "YYTextInput.h" 20 | #endif 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | /** 25 | A single dot view. The frame should be foursquare. 26 | Change the background color for display. 27 | 28 | @discussion Typically, you should not use this class directly. 29 | */ 30 | @interface YYSelectionGrabberDot : UIView 31 | /// Dont't access this property. It was used by `YYTextEffectWindow`. 32 | @property (nonatomic, strong) UIView *mirror; 33 | @end 34 | 35 | 36 | /** 37 | A grabber (stick with a dot). 38 | 39 | @discussion Typically, you should not use this class directly. 40 | */ 41 | @interface YYSelectionGrabber : UIView 42 | 43 | @property (nonatomic, readonly) YYSelectionGrabberDot *dot; ///< the dot view 44 | @property (nonatomic) YYTextDirection dotDirection; ///< don't support composite direction 45 | @property (nullable, nonatomic, strong) UIColor *color; ///< tint color, default is nil 46 | 47 | @end 48 | 49 | 50 | /** 51 | The selection view for text edit and select. 52 | 53 | @discussion Typically, you should not use this class directly. 54 | */ 55 | @interface YYTextSelectionView : UIView 56 | 57 | @property (nullable, nonatomic, weak) UIView *hostView; ///< the holder view 58 | @property (nullable, nonatomic, strong) UIColor *color; ///< the tint color 59 | @property (nonatomic, getter = isCaretBlinks) BOOL caretBlinks; ///< whether the caret is blinks 60 | @property (nonatomic, getter = isCaretVisible) BOOL caretVisible; ///< whether the caret is visible 61 | @property (nonatomic, getter = isVerticalForm) BOOL verticalForm; ///< weather the text view is vertical form 62 | 63 | @property (nonatomic) CGRect caretRect; ///< caret rect (width==0 or height==0) 64 | @property (nullable, nonatomic, copy) NSArray *selectionRects; ///< default is nil 65 | 66 | @property (nonatomic, readonly) UIView *caretView; 67 | @property (nonatomic, readonly) YYSelectionGrabber *startGrabber; 68 | @property (nonatomic, readonly) YYSelectionGrabber *endGrabber; 69 | 70 | - (BOOL)isGrabberContainsPoint:(CGPoint)point; 71 | - (BOOL)isStartGrabberContainsPoint:(CGPoint)point; 72 | - (BOOL)isEndGrabberContainsPoint:(CGPoint)point; 73 | - (BOOL)isCaretContainsPoint:(CGPoint)point; 74 | - (BOOL)isSelectionRectsContainsPoint:(CGPoint)point; 75 | 76 | @end 77 | 78 | NS_ASSUME_NONNULL_END 79 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/String/NSParagraphStyle+YYText.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSParagraphStyle+YYText.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/7. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `NSParagraphStyle` to work with CoreText. 18 | */ 19 | @interface NSParagraphStyle (YYText) 20 | 21 | /** 22 | Creates a new NSParagraphStyle object from the CoreText Style. 23 | 24 | @param CTStyle CoreText Paragraph Style. 25 | 26 | @return a new NSParagraphStyle 27 | */ 28 | + (nullable NSParagraphStyle *)styleWithCTStyle:(CTParagraphStyleRef)CTStyle; 29 | 30 | /** 31 | Creates and returns a CoreText Paragraph Style. (need call CFRelease() after used) 32 | */ 33 | - (nullable CTParagraphStyleRef)CTStyle CF_RETURNS_RETAINED; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/String/UIPasteboard+YYText.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIPasteboard+YYText.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/2. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Extend UIPasteboard to support image and attributed string. 18 | */ 19 | @interface UIPasteboard (YYText) 20 | 21 | @property (nullable, nonatomic, copy) NSData *PNGData; ///< PNG file data 22 | @property (nullable, nonatomic, copy) NSData *JPEGData; ///< JPEG file data 23 | @property (nullable, nonatomic, copy) NSData *GIFData; ///< GIF file data 24 | @property (nullable, nonatomic, copy) NSData *WEBPData; ///< WebP file data 25 | @property (nullable, nonatomic, copy) NSData *imageData; ///< image file data 26 | 27 | /// Attributed string, 28 | /// Set this attributed will also set the string property which is copy from the attributed string. 29 | /// If the attributed string contains one or more image, it will also set the `images` property. 30 | @property (nullable, nonatomic, copy) NSAttributedString *attributedString; 31 | 32 | @end 33 | 34 | 35 | /// The name identifying the attributed string in pasteboard. 36 | UIKIT_EXTERN NSString *const YYPasteboardTypeAttributedString; 37 | 38 | /// The UTI Type identifying WebP data in pasteboard. 39 | UIKIT_EXTERN NSString *const YYUTTypeWEBP; 40 | 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/String/YYTextArchiver.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextArchiver.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/3/16. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | A subclass of `NSKeyedArchiver` which implement `NSKeyedArchiverDelegate` protocol. 18 | 19 | The archiver can encode the object which contains 20 | CGColor/CGImage/CTRunDelegateRef/.. (such as NSAttributedString). 21 | */ 22 | @interface YYTextArchiver : NSKeyedArchiver 23 | @end 24 | 25 | /** 26 | A subclass of `NSKeyedUnarchiver` which implement `NSKeyedUnarchiverDelegate` 27 | protocol. The unarchiver can decode the data which is encoded by 28 | `YYTextArchiver` or `NSKeyedArchiver`. 29 | */ 30 | @interface YYTextUnarchiver : NSKeyedUnarchiver 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/String/YYTextParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextParser.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/3/6. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | The YYTextParser protocol declares the required method for YYTextView and YYLabel 18 | to modify the text during editing. 19 | 20 | You can implement this protocol to add code highlighting or emoticon replacement for 21 | YYTextView and YYLabel. See `YYTextSimpleMarkdownParser` and `YYTextSimpleEmoticonParser` for example. 22 | */ 23 | @protocol YYTextParser 24 | @required 25 | /** 26 | When text is changed in YYTextView or YYLabel, this method will be called. 27 | 28 | @param text The original attributed string. This method may parse the text and 29 | change the text attributes or content. 30 | 31 | @param selectedRange Current selected range in `text`. 32 | This method should correct the range if the text content is changed. If there's 33 | no selected range (such as YYLabel), this value is NULL. 34 | 35 | @return If the 'text' is modified in this method, returns `YES`, otherwise returns `NO`. 36 | */ 37 | - (BOOL)parseText:(nullable NSMutableAttributedString *)text selectedRange:(nullable NSRangePointer)selectedRange; 38 | @end 39 | 40 | 41 | 42 | /** 43 | A simple markdown parser. 44 | 45 | It'a very simple markdown parser, you can use this parser to highlight some 46 | small piece of markdown text. 47 | 48 | This markdown parser use regular expression to parse text, slow and weak. 49 | If you want to write a better parser, try these projests: 50 | https://github.com/NimbusKit/markdown 51 | https://github.com/dreamwieber/AttributedMarkdown 52 | https://github.com/indragiek/CocoaMarkdown 53 | 54 | Or you can use lex/yacc to generate your custom parser. 55 | */ 56 | @interface YYTextSimpleMarkdownParser : NSObject 57 | @property (nonatomic) CGFloat fontSize; ///< default is 14 58 | @property (nonatomic) CGFloat headerFontSize; ///< default is 20 59 | 60 | @property (nullable, nonatomic, strong) UIColor *textColor; 61 | @property (nullable, nonatomic, strong) UIColor *controlTextColor; 62 | @property (nullable, nonatomic, strong) UIColor *headerTextColor; 63 | @property (nullable, nonatomic, strong) UIColor *inlineTextColor; 64 | @property (nullable, nonatomic, strong) UIColor *codeTextColor; 65 | @property (nullable, nonatomic, strong) UIColor *linkTextColor; 66 | 67 | - (void)setColorWithBrightTheme; ///< reset the color properties to pre-defined value. 68 | - (void)setColorWithDarkTheme; ///< reset the color properties to pre-defined value. 69 | @end 70 | 71 | 72 | 73 | /** 74 | A simple emoticon parser. 75 | 76 | Use this parser to map some specified piece of string to image emoticon. 77 | Example: "Hello :smile:" -> "Hello 😀" 78 | 79 | It can also be used to extend the "unicode emoticon". 80 | */ 81 | @interface YYTextSimpleEmoticonParser : NSObject 82 | 83 | /** 84 | The custom emoticon mapper. 85 | The key is a specified plain string, such as @":smile:". 86 | The value is a UIImage which will replace the specified plain string in text. 87 | */ 88 | @property (nullable, copy) NSDictionary *emoticonMapper; 89 | @end 90 | 91 | NS_ASSUME_NONNULL_END 92 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/String/YYTextRubyAnnotation.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextRubyAnnotation.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/24. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | /** 18 | Wrapper for CTRubyAnnotationRef. 19 | 20 | Example: 21 | 22 | YYTextRubyAnnotation *ruby = [YYTextRubyAnnotation new]; 23 | ruby.textBefore = @"zhù yīn"; 24 | CTRubyAnnotationRef ctRuby = ruby.CTRubyAnnotation; 25 | if (ctRuby) { 26 | /// add to attributed string 27 | CFRelease(ctRuby); 28 | } 29 | 30 | */ 31 | @interface YYTextRubyAnnotation : NSObject 32 | 33 | /// Specifies how the ruby text and the base text should be aligned relative to each other. 34 | @property (nonatomic) CTRubyAlignment alignment; 35 | 36 | /// Specifies how the ruby text can overhang adjacent characters. 37 | @property (nonatomic) CTRubyOverhang overhang; 38 | 39 | /// Specifies the size of the annotation text as a percent of the size of the base text. 40 | @property (nonatomic) CGFloat sizeFactor; 41 | 42 | 43 | /// The ruby text is positioned before the base text; 44 | /// i.e. above horizontal text and to the right of vertical text. 45 | @property (nullable, nonatomic, copy) NSString *textBefore; 46 | 47 | /// The ruby text is positioned after the base text; 48 | /// i.e. below horizontal text and to the left of vertical text. 49 | @property (nullable, nonatomic, copy) NSString *textAfter; 50 | 51 | /// The ruby text is positioned to the right of the base text whether it is horizontal or vertical. 52 | /// This is the way that Bopomofo annotations are attached to Chinese text in Taiwan. 53 | @property (nullable, nonatomic, copy) NSString *textInterCharacter; 54 | 55 | /// The ruby text follows the base text with no special styling. 56 | @property (nullable, nonatomic, copy) NSString *textInline; 57 | 58 | 59 | /** 60 | Create a ruby object from CTRuby object. 61 | 62 | @param ctRuby A CTRuby object. 63 | 64 | @return A ruby object, or nil when an error occurs. 65 | */ 66 | + (instancetype)rubyWithCTRubyRef:(CTRubyAnnotationRef)ctRuby NS_AVAILABLE_IOS(8_0); 67 | 68 | /** 69 | Create a CTRuby object from the instance. 70 | 71 | @return A new CTRuby object, or NULL when an error occurs. 72 | The returned value should be release after used. 73 | */ 74 | - (nullable CTRubyAnnotationRef)CTRubyAnnotation CF_RETURNS_RETAINED NS_AVAILABLE_IOS(8_0); 75 | 76 | @end 77 | 78 | NS_ASSUME_NONNULL_END 79 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/String/YYTextRubyAnnotation.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextRubyAnnotation.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/24. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYTextRubyAnnotation.h" 13 | 14 | @implementation YYTextRubyAnnotation 15 | 16 | - (instancetype)init { 17 | self = super.init; 18 | self.alignment = kCTRubyAlignmentAuto; 19 | self.overhang = kCTRubyOverhangAuto; 20 | self.sizeFactor = 0.5; 21 | return self; 22 | } 23 | 24 | + (instancetype)rubyWithCTRubyRef:(CTRubyAnnotationRef)ctRuby { 25 | if (!ctRuby) return nil; 26 | YYTextRubyAnnotation *one = [self new]; 27 | one.alignment = CTRubyAnnotationGetAlignment(ctRuby); 28 | one.overhang = CTRubyAnnotationGetOverhang(ctRuby); 29 | one.sizeFactor = CTRubyAnnotationGetSizeFactor(ctRuby); 30 | one.textBefore = (__bridge NSString *)(CTRubyAnnotationGetTextForPosition(ctRuby, kCTRubyPositionBefore)); 31 | one.textAfter = (__bridge NSString *)(CTRubyAnnotationGetTextForPosition(ctRuby, kCTRubyPositionAfter)); 32 | one.textInterCharacter = (__bridge NSString *)(CTRubyAnnotationGetTextForPosition(ctRuby, kCTRubyPositionInterCharacter)); 33 | one.textInline = (__bridge NSString *)(CTRubyAnnotationGetTextForPosition(ctRuby, kCTRubyPositionInline)); 34 | return one; 35 | } 36 | 37 | - (CTRubyAnnotationRef)CTRubyAnnotation CF_RETURNS_RETAINED { 38 | if (((long)CTRubyAnnotationCreate + 1) == 1) return NULL; // system not support 39 | 40 | CFStringRef text[kCTRubyPositionCount]; 41 | text[kCTRubyPositionBefore] = (__bridge CFStringRef)(_textBefore); 42 | text[kCTRubyPositionAfter] = (__bridge CFStringRef)(_textAfter); 43 | text[kCTRubyPositionInterCharacter] = (__bridge CFStringRef)(_textInterCharacter); 44 | text[kCTRubyPositionInline] = (__bridge CFStringRef)(_textInline); 45 | CTRubyAnnotationRef ruby = CTRubyAnnotationCreate(_alignment, _overhang, _sizeFactor, text); 46 | return ruby; 47 | } 48 | 49 | - (id)copyWithZone:(NSZone *)zone { 50 | YYTextRubyAnnotation *one = [self.class new]; 51 | one.alignment = _alignment; 52 | one.overhang = _overhang; 53 | one.sizeFactor = _sizeFactor; 54 | one.textBefore = _textBefore; 55 | one.textAfter = _textAfter; 56 | one.textInterCharacter = _textInterCharacter; 57 | one.textInline = _textInline; 58 | return one; 59 | } 60 | 61 | - (void)encodeWithCoder:(NSCoder *)aCoder { 62 | [aCoder encodeObject:@(_alignment) forKey:@"alignment"]; 63 | [aCoder encodeObject:@(_overhang) forKey:@"overhang"]; 64 | [aCoder encodeObject:@(_sizeFactor) forKey:@"sizeFactor"]; 65 | [aCoder encodeObject:_textBefore forKey:@"textBefore"]; 66 | [aCoder encodeObject:_textAfter forKey:@"textAfter"]; 67 | [aCoder encodeObject:_textInterCharacter forKey:@"textInterCharacter"]; 68 | [aCoder encodeObject:_textInline forKey:@"textInline"]; 69 | } 70 | 71 | - (id)initWithCoder:(NSCoder *)aDecoder { 72 | self = [self init]; 73 | _alignment = ((NSNumber *)[aDecoder decodeObjectForKey:@"alignment"]).intValue; 74 | _overhang = ((NSNumber *)[aDecoder decodeObjectForKey:@"overhang"]).intValue; 75 | _sizeFactor = ((NSNumber *)[aDecoder decodeObjectForKey:@"sizeFactor"]).intValue; 76 | _textBefore = [aDecoder decodeObjectForKey:@"textBefore"]; 77 | _textAfter = [aDecoder decodeObjectForKey:@"textAfter"]; 78 | _textInterCharacter = [aDecoder decodeObjectForKey:@"textInterCharacter"]; 79 | _textInline = [aDecoder decodeObjectForKey:@"textInline"]; 80 | return self; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/String/YYTextRunDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextRunDelegate.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/14. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | /** 18 | Wrapper for CTRunDelegateRef. 19 | 20 | Example: 21 | 22 | YYTextRunDelegate *delegate = [YYTextRunDelegate new]; 23 | delegate.ascent = 20; 24 | delegate.descent = 4; 25 | delegate.width = 20; 26 | CTRunDelegateRef ctRunDelegate = delegate.CTRunDelegate; 27 | if (ctRunDelegate) { 28 | /// add to attributed string 29 | CFRelease(ctRunDelegate); 30 | } 31 | 32 | */ 33 | @interface YYTextRunDelegate : NSObject 34 | 35 | /** 36 | Creates and returns the CTRunDelegate. 37 | 38 | @discussion You need call CFRelease() after used. 39 | The CTRunDelegateRef has a strong reference to this YYTextRunDelegate object. 40 | In CoreText, use CTRunDelegateGetRefCon() to get this YYTextRunDelegate object. 41 | 42 | @return The CTRunDelegate object. 43 | */ 44 | - (nullable CTRunDelegateRef)CTRunDelegate CF_RETURNS_RETAINED; 45 | 46 | /** 47 | Additional information about the the run delegate. 48 | */ 49 | @property (nullable, nonatomic, strong) NSDictionary *userInfo; 50 | 51 | /** 52 | The typographic ascent of glyphs in the run. 53 | */ 54 | @property (nonatomic) CGFloat ascent; 55 | 56 | /** 57 | The typographic descent of glyphs in the run. 58 | */ 59 | @property (nonatomic) CGFloat descent; 60 | 61 | /** 62 | The typographic width of glyphs in the run. 63 | */ 64 | @property (nonatomic) CGFloat width; 65 | 66 | @end 67 | 68 | NS_ASSUME_NONNULL_END 69 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/String/YYTextRunDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextRunDelegate.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/14. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYTextRunDelegate.h" 13 | 14 | static void DeallocCallback(void *ref) { 15 | YYTextRunDelegate *self = (__bridge_transfer YYTextRunDelegate *)(ref); 16 | self = nil; // release 17 | } 18 | 19 | static CGFloat GetAscentCallback(void *ref) { 20 | YYTextRunDelegate *self = (__bridge YYTextRunDelegate *)(ref); 21 | return self.ascent; 22 | } 23 | 24 | static CGFloat GetDecentCallback(void *ref) { 25 | YYTextRunDelegate *self = (__bridge YYTextRunDelegate *)(ref); 26 | return self.descent; 27 | } 28 | 29 | static CGFloat GetWidthCallback(void *ref) { 30 | YYTextRunDelegate *self = (__bridge YYTextRunDelegate *)(ref); 31 | return self.width; 32 | } 33 | 34 | @implementation YYTextRunDelegate 35 | 36 | - (CTRunDelegateRef)CTRunDelegate CF_RETURNS_RETAINED { 37 | CTRunDelegateCallbacks callbacks; 38 | callbacks.version = kCTRunDelegateCurrentVersion; 39 | callbacks.dealloc = DeallocCallback; 40 | callbacks.getAscent = GetAscentCallback; 41 | callbacks.getDescent = GetDecentCallback; 42 | callbacks.getWidth = GetWidthCallback; 43 | return CTRunDelegateCreate(&callbacks, (__bridge_retained void *)(self.copy)); 44 | } 45 | 46 | - (void)encodeWithCoder:(NSCoder *)aCoder { 47 | [aCoder encodeObject:@(_ascent) forKey:@"ascent"]; 48 | [aCoder encodeObject:@(_descent) forKey:@"descent"]; 49 | [aCoder encodeObject:@(_width) forKey:@"width"]; 50 | [aCoder encodeObject:_userInfo forKey:@"userInfo"]; 51 | } 52 | 53 | - (id)initWithCoder:(NSCoder *)aDecoder { 54 | self = [super init]; 55 | _ascent = ((NSNumber *)[aDecoder decodeObjectForKey:@"ascent"]).floatValue; 56 | _descent = ((NSNumber *)[aDecoder decodeObjectForKey:@"descent"]).floatValue; 57 | _width = ((NSNumber *)[aDecoder decodeObjectForKey:@"width"]).floatValue; 58 | _userInfo = [aDecoder decodeObjectForKey:@"userInfo"]; 59 | return self; 60 | } 61 | 62 | - (id)copyWithZone:(NSZone *)zone { 63 | typeof(self) one = [self.class new]; 64 | one.ascent = self.ascent; 65 | one.descent = self.descent; 66 | one.width = self.width; 67 | one.userInfo = self.userInfo; 68 | return one; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Text/String/YYTextUtilities.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTextUtilities.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/6. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYTextUtilities.h" 13 | 14 | NSCharacterSet *YYTextVerticalFormRotateCharacterSet() { 15 | static NSMutableCharacterSet *set; 16 | static dispatch_once_t onceToken; 17 | dispatch_once(&onceToken, ^{ 18 | set = [NSMutableCharacterSet new]; 19 | [set addCharactersInRange:NSMakeRange(0x1100, 256)]; // Hangul Jamo 20 | [set addCharactersInRange:NSMakeRange(0x2460, 160)]; // Enclosed Alphanumerics 21 | [set addCharactersInRange:NSMakeRange(0x2600, 256)]; // Miscellaneous Symbols 22 | [set addCharactersInRange:NSMakeRange(0x2700, 192)]; // Dingbats 23 | [set addCharactersInRange:NSMakeRange(0x2E80, 128)]; // CJK Radicals Supplement 24 | [set addCharactersInRange:NSMakeRange(0x2F00, 224)]; // Kangxi Radicals 25 | [set addCharactersInRange:NSMakeRange(0x2FF0, 16)]; // Ideographic Description Characters 26 | [set addCharactersInRange:NSMakeRange(0x3000, 64)]; // CJK Symbols and Punctuation 27 | [set removeCharactersInRange:NSMakeRange(0x3008, 10)]; 28 | [set removeCharactersInRange:NSMakeRange(0x3014, 12)]; 29 | [set addCharactersInRange:NSMakeRange(0x3040, 96)]; // Hiragana 30 | [set addCharactersInRange:NSMakeRange(0x30A0, 96)]; // Katakana 31 | [set addCharactersInRange:NSMakeRange(0x3100, 48)]; // Bopomofo 32 | [set addCharactersInRange:NSMakeRange(0x3130, 96)]; // Hangul Compatibility Jamo 33 | [set addCharactersInRange:NSMakeRange(0x3190, 16)]; // Kanbun 34 | [set addCharactersInRange:NSMakeRange(0x31A0, 32)]; // Bopomofo Extended 35 | [set addCharactersInRange:NSMakeRange(0x31C0, 48)]; // CJK Strokes 36 | [set addCharactersInRange:NSMakeRange(0x31F0, 16)]; // Katakana Phonetic Extensions 37 | [set addCharactersInRange:NSMakeRange(0x3200, 256)]; // Enclosed CJK Letters and Months 38 | [set addCharactersInRange:NSMakeRange(0x3300, 256)]; // CJK Compatibility 39 | [set addCharactersInRange:NSMakeRange(0x3400, 2582)]; // CJK Unified Ideographs Extension A 40 | [set addCharactersInRange:NSMakeRange(0x4E00, 20941)]; // CJK Unified Ideographs 41 | [set addCharactersInRange:NSMakeRange(0xAC00, 11172)]; // Hangul Syllables 42 | [set addCharactersInRange:NSMakeRange(0xD7B0, 80)]; // Hangul Jamo Extended-B 43 | [set addCharactersInString:@""]; // U+F8FF (Private Use Area) 44 | [set addCharactersInRange:NSMakeRange(0xF900, 512)]; // CJK Compatibility Ideographs 45 | [set addCharactersInRange:NSMakeRange(0xFE10, 16)]; // Vertical Forms 46 | [set addCharactersInRange:NSMakeRange(0xFF00, 240)]; // Halfwidth and Fullwidth Forms 47 | [set addCharactersInRange:NSMakeRange(0x1F200, 256)]; // Enclosed Ideographic Supplement 48 | [set addCharactersInRange:NSMakeRange(0x1F300, 768)]; // Enclosed Ideographic Supplement 49 | [set addCharactersInRange:NSMakeRange(0x1F600, 80)]; // Emoticons (Emoji) 50 | [set addCharactersInRange:NSMakeRange(0x1F680, 128)]; // Transport and Map Symbols 51 | 52 | // See http://unicode-table.com/ for more information. 53 | }); 54 | return set; 55 | } 56 | 57 | NSCharacterSet *YYTextVerticalFormRotateAndMoveCharacterSet() { 58 | static NSMutableCharacterSet *set; 59 | static dispatch_once_t onceToken; 60 | dispatch_once(&onceToken, ^{ 61 | set = [NSMutableCharacterSet new]; 62 | [set addCharactersInString:@",。、."]; 63 | }); 64 | return set; 65 | } 66 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYAsyncLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYAsyncLayer.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/11. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | @class YYAsyncLayerDisplayTask; 16 | 17 | NS_ASSUME_NONNULL_BEGIN 18 | 19 | /** 20 | The YYAsyncLayer class is a subclass of CALayer used for render contents asynchronously. 21 | 22 | @discussion When the layer need update it's contents, it will ask the delegate 23 | for a async display task to render the contents in a background queue. 24 | */ 25 | @interface YYAsyncLayer : CALayer 26 | /// Whether the render code is executed in background. Default is YES. 27 | @property BOOL displaysAsynchronously; 28 | @end 29 | 30 | 31 | /** 32 | The YYAsyncLayer's delegate protocol. The delegate of the YYAsyncLayer (typically a UIView) 33 | must implements the method in this protocol. 34 | */ 35 | @protocol YYAsyncLayerDelegate 36 | @required 37 | /// This method is called to return a new display task when the layer's contents need update. 38 | - (YYAsyncLayerDisplayTask *)newAsyncDisplayTask; 39 | @end 40 | 41 | 42 | /** 43 | A display task used by YYAsyncLayer to render the contents in background queue. 44 | */ 45 | @interface YYAsyncLayerDisplayTask : NSObject 46 | 47 | /** 48 | This block will be called before the asynchronous drawing begins. 49 | It will be called on the main thread. 50 | 51 | @param layer The layer. 52 | */ 53 | @property (nullable, nonatomic, copy) void (^willDisplay)(CALayer *layer); 54 | 55 | /** 56 | This block is called to draw the layer's contents. 57 | 58 | @discussion This block may be called on main thread or background thread, 59 | so is should be thread-safe. 60 | 61 | @param context A new bitmap content created by layer. 62 | @param size The content size (typically same as layer's bound size). 63 | @param isCancelled If this block returns `YES`, the method should cancel the 64 | drawing process and return as quickly as possible. 65 | */ 66 | @property (nullable, nonatomic, copy) void (^display)(CGContextRef context, CGSize size, BOOL(^isCancelled)(void)); 67 | 68 | /** 69 | This block will be called after the asynchronous drawing finished. 70 | It will be called on the main thread. 71 | 72 | @param layer The layer. 73 | @param finished If the draw process is cancelled, it's `NO`, otherwise it's `YES`; 74 | */ 75 | @property (nullable, nonatomic, copy) void (^didDisplay)(CALayer *layer, BOOL finished); 76 | 77 | @end 78 | 79 | NS_ASSUME_NONNULL_END 80 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYDispatchQueuePool.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYDispatchQueueManager.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/7/18. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #ifndef YYDispatchQueuePool_h 15 | #define YYDispatchQueuePool_h 16 | 17 | NS_ASSUME_NONNULL_BEGIN 18 | 19 | /** 20 | A dispatch queue pool holds multiple serial queues. 21 | Use this class to control queue's thread count (instead of concurrent queue). 22 | */ 23 | @interface YYDispatchQueuePool : NSObject 24 | - (instancetype)init UNAVAILABLE_ATTRIBUTE; 25 | + (instancetype)new UNAVAILABLE_ATTRIBUTE; 26 | 27 | /** 28 | Creates and returns a dispatch queue pool. 29 | @param name The name of the pool. 30 | @param queueCount Maxmium queue count, should in range (1, 32). 31 | @param qos Queue quality of service (QOS). 32 | @return A new pool, or nil if an error occurs. 33 | */ 34 | - (instancetype)initWithName:(nullable NSString *)name queueCount:(NSUInteger)queueCount qos:(NSQualityOfService)qos; 35 | 36 | /// Pool's name. 37 | @property (nullable, nonatomic, readonly) NSString *name; 38 | 39 | /// Get a serial queue from pool. 40 | - (dispatch_queue_t)queue; 41 | 42 | + (instancetype)defaultPoolForQOS:(NSQualityOfService)qos; 43 | 44 | @end 45 | 46 | /// Get a serial queue from global queue pool with a specified qos. 47 | extern dispatch_queue_t YYDispatchQueueGetForQOS(NSQualityOfService qos); 48 | 49 | NS_ASSUME_NONNULL_END 50 | 51 | #endif -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYGestureRecognizer.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYGestureRecognizer.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/26. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /// State of the gesture 17 | typedef NS_ENUM(NSUInteger, YYGestureRecognizerState) { 18 | YYGestureRecognizerStateBegan, ///< gesture start 19 | YYGestureRecognizerStateMoved, ///< gesture moved 20 | YYGestureRecognizerStateEnded, ///< gesture end 21 | YYGestureRecognizerStateCancelled, ///< gesture cancel 22 | }; 23 | 24 | /** 25 | A simple UIGestureRecognizer subclass for receive touch events. 26 | */ 27 | @interface YYGestureRecognizer : UIGestureRecognizer 28 | 29 | @property (nonatomic, readonly) CGPoint startPoint; ///< start point 30 | @property (nonatomic, readonly) CGPoint lastPoint; ///< last move point. 31 | @property (nonatomic, readonly) CGPoint currentPoint; ///< current move point. 32 | 33 | /// The action block invoked by every gesture event. 34 | @property (nullable, nonatomic, copy) void (^action)(YYGestureRecognizer *gesture, YYGestureRecognizerState state); 35 | 36 | /// Cancel the gesture for current touch. 37 | - (void)cancel; 38 | 39 | @end 40 | 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYGestureRecognizer.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYGestureRecognizer.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/26. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYGestureRecognizer.h" 13 | #import 14 | 15 | @implementation YYGestureRecognizer 16 | 17 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 18 | self.state = UIGestureRecognizerStateBegan; 19 | _startPoint = [(UITouch *)[touches anyObject] locationInView:self.view]; 20 | _lastPoint = _currentPoint; 21 | _currentPoint = _startPoint; 22 | if (_action) _action(self, YYGestureRecognizerStateBegan); 23 | } 24 | 25 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 26 | UITouch *touch = (UITouch *)[touches anyObject]; 27 | CGPoint currentPoint = [touch locationInView:self.view]; 28 | self.state = UIGestureRecognizerStateChanged; 29 | _currentPoint = currentPoint; 30 | if (_action) _action(self, YYGestureRecognizerStateMoved); 31 | _lastPoint = _currentPoint; 32 | } 33 | 34 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 35 | self.state = UIGestureRecognizerStateEnded; 36 | if (_action) _action(self, YYGestureRecognizerStateEnded); 37 | } 38 | 39 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 40 | self.state = UIGestureRecognizerStateCancelled; 41 | if (_action) _action(self, YYGestureRecognizerStateCancelled); 42 | } 43 | 44 | - (void)reset { 45 | self.state = UIGestureRecognizerStatePossible; 46 | } 47 | 48 | - (void)cancel { 49 | if (self.state == UIGestureRecognizerStateBegan || self.state == UIGestureRecognizerStateChanged) { 50 | self.state = UIGestureRecognizerStateCancelled; 51 | if (_action) _action(self, YYGestureRecognizerStateCancelled); 52 | } 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYReachability.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYReachability.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/6. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | #import 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | typedef NS_ENUM(NSUInteger, YYReachabilityStatus) { 19 | YYReachabilityStatusNone = 0, ///< Not Reachable 20 | YYReachabilityStatusWWAN = 1, ///< Reachable via WWAN (2G/3G/4G) 21 | YYReachabilityStatusWiFi = 2, ///< Reachable via WiFi 22 | }; 23 | 24 | typedef NS_ENUM(NSUInteger, YYReachabilityWWANStatus) { 25 | YYReachabilityWWANStatusNone = 0, ///< Not Reachable vis WWAN 26 | YYReachabilityWWANStatus2G = 2, ///< Reachable via 2G (GPRS/EDGE) 10~100Kbps 27 | YYReachabilityWWANStatus3G = 3, ///< Reachable via 3G (WCDMA/HSDPA/...) 1~10Mbps 28 | YYReachabilityWWANStatus4G = 4, ///< Reachable via 4G (eHRPD/LTE) 100Mbps 29 | }; 30 | 31 | 32 | /** 33 | `YYReachability` can used to monitor the network status of an iOS device. 34 | */ 35 | @interface YYReachability : NSObject 36 | 37 | @property (nonatomic, readonly) SCNetworkReachabilityFlags flags; ///< Current flags. 38 | @property (nonatomic, readonly) YYReachabilityStatus status; ///< Current status. 39 | @property (nonatomic, readonly) YYReachabilityWWANStatus wwanStatus NS_AVAILABLE_IOS(7_0); ///< Current WWAN status. 40 | @property (nonatomic, readonly, getter=isReachable) BOOL reachable; ///< Current reachable status. 41 | 42 | /// Notify block which will be called on main thread when network changed. 43 | @property (nullable, nonatomic, copy) void (^notifyBlock)(YYReachability *reachability); 44 | 45 | /// Create an object to check the reachability of the default route. 46 | + (instancetype)reachability; 47 | 48 | /// Create an object to check the reachability of the local WI-FI. 49 | + (instancetype)reachabilityForLocalWifi DEPRECATED_MSG_ATTRIBUTE("unnecessary and potentially harmful"); 50 | 51 | /// Create an object to check the reachability of a given host name. 52 | + (nullable instancetype)reachabilityWithHostname:(NSString *)hostname; 53 | 54 | /// Create an object to check the reachability of a given IP address 55 | /// @param hostAddress You may pass `struct sockaddr_in` for IPv4 address or `struct sockaddr_in6` for IPv6 address. 56 | + (nullable instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress; 57 | 58 | @end 59 | 60 | NS_ASSUME_NONNULL_END 61 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYSentinel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYSentinel.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/13. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | YYSentinel is a thread safe incrementing counter. 18 | It may be used in some multi-threaded situation. 19 | */ 20 | @interface YYSentinel : NSObject 21 | 22 | /// Returns the current value of the counter. 23 | @property (readonly) int32_t value; 24 | 25 | /// Increase the value atomically. 26 | /// @return The new value. 27 | - (int32_t)increase; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYSentinel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYSentinel.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/13. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYSentinel.h" 13 | #import 14 | 15 | @implementation YYSentinel { 16 | int32_t _value; 17 | } 18 | 19 | - (int32_t)value { 20 | return _value; 21 | } 22 | 23 | - (int32_t)increase { 24 | #pragma clang diagnostic push 25 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 26 | return OSAtomicIncrement32(&_value); 27 | #pragma clang diagnostic pop 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYThreadSafeArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYThreadSafeArray.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/21. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | /** 15 | A simple implementation of thread safe mutable array. 16 | 17 | @discussion Generally, access performance is lower than NSMutableArray, 18 | but higher than using @synchronized, NSLock, or pthread_mutex_t. 19 | 20 | @discussion It's also compatible with the custom methods in `NSArray(YYAdd)` 21 | and `NSMutableArray(YYAdd)` 22 | 23 | @warning Fast enumerate(for..in) and enumerator is not thread safe, 24 | use enumerate using block instead. When enumerate or sort with block/callback, 25 | do *NOT* send message to the array inside the block/callback. 26 | */ 27 | @interface YYThreadSafeArray : NSMutableArray 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYThreadSafeDictionary.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYThreadSafeDictionary.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/21. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | /** 15 | A simple implementation of thread safe mutable dictionary. 16 | 17 | @discussion Generally, access performance is lower than NSMutableDictionary, 18 | but higher than using @synchronized, NSLock, or pthread_mutex_t. 19 | 20 | @discussion It's also compatible with the custom methods in `NSDictionary(YYAdd)` 21 | and `NSMutableDictionary(YYAdd)` 22 | 23 | @warning Fast enumerate(for...in) and enumerator is not thread safe, 24 | use enumerate using block instead. When enumerate or sort with block/callback, 25 | do *NOT* send message to the dictionary inside the block/callback. 26 | */ 27 | @interface YYThreadSafeDictionary : NSMutableDictionary 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYTimer.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTimer.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/7. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | YYTimer is a thread-safe timer based on GCD. It has similar API with `NSTimer`. 18 | YYTimer object differ from NSTimer in a few ways: 19 | 20 | * It use GCD to produce timer tick, and won't be affected by runLoop. 21 | * It make a weak reference to the target, so it can avoid retain cycles. 22 | * It always fire on main thread. 23 | 24 | */ 25 | @interface YYTimer : NSObject 26 | 27 | + (YYTimer *)timerWithTimeInterval:(NSTimeInterval)interval 28 | target:(id)target 29 | selector:(SEL)selector 30 | repeats:(BOOL)repeats; 31 | 32 | - (instancetype)initWithFireTime:(NSTimeInterval)start 33 | interval:(NSTimeInterval)interval 34 | target:(id)target 35 | selector:(SEL)selector 36 | repeats:(BOOL)repeats NS_DESIGNATED_INITIALIZER; 37 | 38 | @property (readonly) BOOL repeats; 39 | @property (readonly) NSTimeInterval timeInterval; 40 | @property (readonly, getter=isValid) BOOL valid; 41 | 42 | - (void)invalidate; 43 | 44 | - (void)fire; 45 | 46 | @end 47 | 48 | NS_ASSUME_NONNULL_END 49 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYTimer.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTimer.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/2/7. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYTimer.h" 13 | #import 14 | 15 | #define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \ 16 | __VA_ARGS__; \ 17 | dispatch_semaphore_signal(_lock); 18 | 19 | 20 | @implementation YYTimer { 21 | BOOL _valid; 22 | NSTimeInterval _timeInterval; 23 | BOOL _repeats; 24 | __weak id _target; 25 | SEL _selector; 26 | dispatch_source_t _source; 27 | dispatch_semaphore_t _lock; 28 | } 29 | 30 | + (YYTimer *)timerWithTimeInterval:(NSTimeInterval)interval 31 | target:(id)target 32 | selector:(SEL)selector 33 | repeats:(BOOL)repeats { 34 | return [[self alloc] initWithFireTime:interval interval:interval target:target selector:selector repeats:repeats]; 35 | } 36 | 37 | - (instancetype)init { 38 | @throw [NSException exceptionWithName:@"YYTimer init error" reason:@"Use the designated initializer to init." userInfo:nil]; 39 | return [self initWithFireTime:0 interval:0 target:self selector:@selector(invalidate) repeats:NO]; 40 | } 41 | 42 | - (instancetype)initWithFireTime:(NSTimeInterval)start 43 | interval:(NSTimeInterval)interval 44 | target:(id)target 45 | selector:(SEL)selector 46 | repeats:(BOOL)repeats { 47 | self = [super init]; 48 | _repeats = repeats; 49 | _timeInterval = interval; 50 | _valid = YES; 51 | _target = target; 52 | _selector = selector; 53 | 54 | __weak typeof(self) _self = self; 55 | _lock = dispatch_semaphore_create(1); 56 | _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); 57 | dispatch_source_set_timer(_source, dispatch_time(DISPATCH_TIME_NOW, (start * NSEC_PER_SEC)), (interval * NSEC_PER_SEC), 0); 58 | dispatch_source_set_event_handler(_source, ^{[_self fire];}); 59 | dispatch_resume(_source); 60 | return self; 61 | } 62 | 63 | - (void)invalidate { 64 | dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); 65 | if (_valid) { 66 | dispatch_source_cancel(_source); 67 | _source = NULL; 68 | _target = nil; 69 | _valid = NO; 70 | } 71 | dispatch_semaphore_signal(_lock); 72 | } 73 | 74 | - (void)fire { 75 | #pragma clang diagnostic push 76 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 77 | dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); 78 | id target = _target; 79 | if (!_repeats || !target) { 80 | dispatch_semaphore_signal(_lock); 81 | [self invalidate]; 82 | } else { 83 | dispatch_semaphore_signal(_lock); 84 | [target performSelector:_selector withObject:self]; 85 | } 86 | #pragma clang diagnostic pop 87 | } 88 | 89 | - (BOOL)repeats { 90 | LOCK(BOOL repeat = _repeats); return repeat; 91 | } 92 | 93 | - (NSTimeInterval)timeInterval { 94 | LOCK(NSTimeInterval t = _timeInterval) return t; 95 | } 96 | 97 | - (BOOL)isValid { 98 | LOCK(BOOL valid = _valid) return valid; 99 | } 100 | 101 | - (void)dealloc { 102 | [self invalidate]; 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYTransaction.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTransaction.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/18. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | YYTransaction let you perform a selector once before current runloop sleep. 18 | */ 19 | @interface YYTransaction : NSObject 20 | 21 | /** 22 | Creates and returns a transaction with a specified target and selector. 23 | 24 | @param target A specified target, the target is retained until runloop end. 25 | @param selector A selector for target. 26 | 27 | @return A new transaction, or nil if an error occurs. 28 | */ 29 | + (YYTransaction *)transactionWithTarget:(id)target selector:(SEL)selector; 30 | 31 | /** 32 | Commit the trancaction to main runloop. 33 | 34 | @discussion It will perform the selector on the target once before main runloop's 35 | current loop sleep. If the same transaction (same target and same selector) has 36 | already commit to runloop in this loop, this method do nothing. 37 | */ 38 | - (void)commit; 39 | 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYTransaction.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTransaction.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/18. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYTransaction.h" 13 | 14 | 15 | @interface YYTransaction() 16 | @property (nonatomic, strong) id target; 17 | @property (nonatomic, assign) SEL selector; 18 | @end 19 | 20 | static NSMutableSet *transactionSet = nil; 21 | 22 | static void YYRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { 23 | if (transactionSet.count == 0) return; 24 | NSSet *currentSet = transactionSet; 25 | transactionSet = [NSMutableSet new]; 26 | [currentSet enumerateObjectsUsingBlock:^(YYTransaction *transaction, BOOL *stop) { 27 | #pragma clang diagnostic push 28 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 29 | [transaction.target performSelector:transaction.selector]; 30 | #pragma clang diagnostic pop 31 | }]; 32 | } 33 | 34 | static void YYTransactionSetup() { 35 | static dispatch_once_t onceToken; 36 | dispatch_once(&onceToken, ^{ 37 | transactionSet = [NSMutableSet new]; 38 | CFRunLoopRef runloop = CFRunLoopGetMain(); 39 | CFRunLoopObserverRef observer; 40 | 41 | observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(), 42 | kCFRunLoopBeforeWaiting | kCFRunLoopExit, 43 | true, // repeat 44 | 0xFFFFFF, // after CATransaction(2000000) 45 | YYRunLoopObserverCallBack, NULL); 46 | CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes); 47 | CFRelease(observer); 48 | }); 49 | } 50 | 51 | 52 | @implementation YYTransaction 53 | 54 | + (YYTransaction *)transactionWithTarget:(id)target selector:(SEL)selector{ 55 | if (!target || !selector) return nil; 56 | YYTransaction *t = [YYTransaction new]; 57 | t.target = target; 58 | t.selector = selector; 59 | return t; 60 | } 61 | 62 | - (void)commit { 63 | if (!_target || !_selector) return; 64 | YYTransactionSetup(); 65 | [transactionSet addObject:self]; 66 | } 67 | 68 | - (NSUInteger)hash { 69 | long v1 = (long)((void *)_selector); 70 | long v2 = (long)_target; 71 | return v1 ^ v2; 72 | } 73 | 74 | - (BOOL)isEqual:(id)object { 75 | if (self == object) return YES; 76 | if (![object isMemberOfClass:self.class]) return NO; 77 | YYTransaction *other = object; 78 | return other.selector == _selector && other.target == _target; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYWeakProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYWeakProxy.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/18. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | A proxy used to hold a weak object. 18 | It can be used to avoid retain cycles, such as the target in NSTimer or CADisplayLink. 19 | 20 | sample code: 21 | 22 | @implementation MyView { 23 | NSTimer *_timer; 24 | } 25 | 26 | - (void)initTimer { 27 | YYWeakProxy *proxy = [YYWeakProxy proxyWithTarget:self]; 28 | _timer = [NSTimer timerWithTimeInterval:0.1 target:proxy selector:@selector(tick:) userInfo:nil repeats:YES]; 29 | } 30 | 31 | - (void)tick:(NSTimer *)timer {...} 32 | @end 33 | */ 34 | @interface YYWeakProxy : NSProxy 35 | 36 | /** 37 | The proxy target. 38 | */ 39 | @property (nullable, nonatomic, weak, readonly) id target; 40 | 41 | /** 42 | Creates a new weak proxy for target. 43 | 44 | @param target Target object. 45 | 46 | @return A new proxy object. 47 | */ 48 | - (instancetype)initWithTarget:(id)target; 49 | 50 | /** 51 | Creates a new weak proxy for target. 52 | 53 | @param target Target object. 54 | 55 | @return A new proxy object. 56 | */ 57 | + (instancetype)proxyWithTarget:(id)target; 58 | 59 | @end 60 | 61 | NS_ASSUME_NONNULL_END 62 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/YYKit/Utility/YYWeakProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYWeakProxy.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/18. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYWeakProxy.h" 13 | 14 | 15 | @implementation YYWeakProxy 16 | 17 | - (instancetype)initWithTarget:(id)target { 18 | _target = target; 19 | return self; 20 | } 21 | 22 | + (instancetype)proxyWithTarget:(id)target { 23 | return [[YYWeakProxy alloc] initWithTarget:target]; 24 | } 25 | 26 | - (id)forwardingTargetForSelector:(SEL)selector { 27 | return _target; 28 | } 29 | 30 | - (void)forwardInvocation:(NSInvocation *)invocation { 31 | void *null = NULL; 32 | [invocation setReturnValue:&null]; 33 | } 34 | 35 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 36 | return [NSObject instanceMethodSignatureForSelector:@selector(init)]; 37 | } 38 | 39 | - (BOOL)respondsToSelector:(SEL)aSelector { 40 | return [_target respondsToSelector:aSelector]; 41 | } 42 | 43 | - (BOOL)isEqual:(id)object { 44 | return [_target isEqual:object]; 45 | } 46 | 47 | - (NSUInteger)hash { 48 | return [_target hash]; 49 | } 50 | 51 | - (Class)superclass { 52 | return [_target superclass]; 53 | } 54 | 55 | - (Class)class { 56 | return [_target class]; 57 | } 58 | 59 | - (BOOL)isKindOfClass:(Class)aClass { 60 | return [_target isKindOfClass:aClass]; 61 | } 62 | 63 | - (BOOL)isMemberOfClass:(Class)aClass { 64 | return [_target isMemberOfClass:aClass]; 65 | } 66 | 67 | - (BOOL)conformsToProtocol:(Protocol *)aProtocol { 68 | return [_target conformsToProtocol:aProtocol]; 69 | } 70 | 71 | - (BOOL)isProxy { 72 | return YES; 73 | } 74 | 75 | - (NSString *)description { 76 | return [_target description]; 77 | } 78 | 79 | - (NSString *)debugDescription { 80 | return [_target debugDescription]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/ar.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/ar.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UILabel"; text = "Label"; ObjectID = "56U-0r-1bF"; */ 3 | "56U-0r-1bF.text" = "Label"; 4 | 5 | /* Class = "UILabel"; text = "Xib布局,炫彩流动,超出范围省略显示"; ObjectID = "5IF-wz-Gkv"; */ 6 | "5IF-wz-Gkv.text" = "Xib布局,炫彩流动,超出范围省略显示"; 7 | 8 | /* Class = "UILabel"; text = "Xib布局,炫彩流动,超出范围自动滚动"; ObjectID = "Lgz-a1-oJn"; */ 9 | "Lgz-a1-oJn.text" = "Xib布局,炫彩流动,超出范围自动滚动"; 10 | 11 | /* Class = "UILabel"; text = "Label"; ObjectID = "XDX-x9-Ydi"; */ 12 | "XDX-x9-Ydi.text" = "Label"; 13 | -------------------------------------------------------------------------------- /JKRShimmeringLabel/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JKRShimmeringLabel 4 | // 5 | // Created by 胡怀刈 on 2023/4/11. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | -------------------------------------------------------------------------------- /LTR.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joker-388/JKRShimmeringLabel/d613b3d2ce70c5bebe70017a192cbfe19a20742f/LTR.GIF -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JKRShimmeringLabel 2 | 3 | ## 特征 4 | 5 | [![preview](https://github.com/Joker-388/JKRShimmeringLabel/blob/main/LTR.GIF)](https://www.jianshu.com/p/d81725130936?v=1681198297299)  6 |
7 | 8 | [![preview](https://github.com/Joker-388/JKRShimmeringLabel/blob/main/RTL.GIF)](https://www.jianshu.com/p/d81725130936?v=1681198297299)  9 |
10 | 11 | 1. 支持炫彩字 12 | 2. 支持炫彩流动字 13 | 3. 支持超出显示范围自动滚动文本 14 | 4. 支持RTL下的对称显示和滚动 15 | 5. 支持Frame布局 16 | 6. 支持Xib和StoryBoard内使用 17 | 7. 支持AutoLayout布局 18 | 19 | ## 使用 20 | 21 | 和原生UILabel一样用,只需要设置mask属性(一张彩色的图片遮罩)即可。 22 | 23 | ### 原有项目的UILabel替换 24 | 25 | 因为JKRAutoScrollLabel和JKRShimmeringLabel本身就是继承UILabel,可以直接把原有项目的UILabel类,替换成JKRAutoScrollLabel或JKRShimmeringLabel即可。 26 | 27 | ### JKRAutoScrollLabel 28 | 29 | 超出范围自动滚动的Lable,需要设置attributedText,不能设置text。要同时支持流动彩字,设置mask即可。不需要彩色可以不设置mask,只有自动滚动的特性。 30 | 31 | ``` 32 | // Frame布局,字体支持炫彩闪动,同时超出显示范围自动滚动 33 | NSMutableAttributedString *textForFrameAttr = [[NSMutableAttributedString alloc] initWithString:@"我是滚动测试文本Frame布局,看看我的效果" attributes:@{NSForegroundColorAttributeName: UIColorHex(FFFFFF), NSFontAttributeName: [UIFont systemFontOfSize:19 weight:UIFontWeightBold]}]; 34 | self.autoScrollLabelForFrame = [[JKRAutoScrollLabel alloc] initWithFrame:CGRectMake(isRTL ? kScreenWidth - 10 - 300 : 10, CGRectGetMaxY(title0.frame) + 10, 300, 24)]; 35 | // 滚动文本需要设置 attributedText 才能生效 36 | self.autoScrollLabelForFrame.attributedText = textForFrameAttr; 37 | // 设置文字颜色的mask图片遮罩,如果不需要字体炫彩,不设置即可 38 | self.autoScrollLabelForFrame.mask = [self maskImage]; 39 | [self.view addSubview:self.autoScrollLabelForFrame]; 40 | ``` 41 | 42 | ### JKRShimmeringLabel 43 | 44 | 支持流动彩字,设置mask即可,如果还需要超出范围自动滚动,需要使用JKRAutoScrollLabel。 45 | 46 | ``` 47 | // Frame布局,字体支持炫彩闪动 48 | self.shimmerLabelForFrame = [[JKRShimmeringLabel alloc] initWithFrame:CGRectMake(isRTL ? kScreenWidth - 10 - 300 : 10, CGRectGetMaxY(title1.frame) + 10, 300, 24)]; 49 | self.shimmerLabelForFrame.text = @"我是彩色不滚动文本Frame布局,看看我的效果"; 50 | self.shimmerLabelForFrame.font = [UIFont systemFontOfSize:19]; 51 | // 设置文字颜色的mask图片遮罩,如果不需要字体炫彩,不设置即可 52 | self.shimmerLabelForFrame.mask = [self maskImage]; 53 | [self.view addSubview:self.shimmerLabelForFrame]; 54 | ``` 55 | 56 | ### Xib使用 57 | 58 | 控件支持xib和autolayout的场景,和UILabel一样设置约束即可,自动滚动和彩色动画,会自动支持。只需要正常配置约束,然后设置mask彩色遮罩即可。 59 | 60 | 同时,因为JKRShimmeringLabel和JKRAutoScrollLabel本身就是继承UILabel的,所以UILabel在Xib中的文本自动填充宽度、约束优先级等等特性,也都可以正常使用。 -------------------------------------------------------------------------------- /RTL.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joker-388/JKRShimmeringLabel/d613b3d2ce70c5bebe70017a192cbfe19a20742f/RTL.GIF --------------------------------------------------------------------------------