├── .gitignore ├── .travis.yml ├── Framework ├── Info.plist └── YYCategories.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ └── contents.xcworkspacedata │ └── xcshareddata │ └── xcschemes │ └── YYCategories.xcscheme ├── LICENSE ├── README.md ├── YYCategories.podspec └── YYCategories ├── 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 ├── YYCategories.h └── YYCategoriesMacro.h /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcuserstate 27 | *.xcscmblueprint 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | 35 | # CocoaPods 36 | # 37 | # We recommend against adding the Pods directory to your .gitignore. However 38 | # you should judge for yourself, the pros and cons are mentioned at: 39 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 40 | # 41 | # Pods/ 42 | 43 | # Carthage 44 | # 45 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 46 | # Carthage/Checkouts 47 | 48 | Carthage/Build 49 | 50 | # fastlane 51 | # 52 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 53 | # screenshots whenever they are needed. 54 | # For more information about the recommended setup visit: 55 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 56 | 57 | fastlane/report.xml 58 | fastlane/Preview.html 59 | fastlane/screenshots 60 | fastlane/test_output 61 | 62 | # Code Injection 63 | # 64 | # After new code Injection tools there's a generated folder /iOSInjectionProject 65 | # https://github.com/johnno1962/injectionforxcode 66 | 67 | iOSInjectionProject/ 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8 3 | xcode_project: Framework/YYCategories.xcodeproj 4 | xcode_scheme: YYCategories 5 | 6 | before_install: 7 | - env 8 | - xcodebuild -version 9 | - xcodebuild -showsdks 10 | - xcpretty --version 11 | 12 | script: 13 | - set -o pipefail 14 | - xcodebuild clean build -project "$TRAVIS_XCODE_PROJECT" -scheme "$TRAVIS_XCODE_SCHEME" | xcpretty 15 | -------------------------------------------------------------------------------- /Framework/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.4 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Framework/YYCategories.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Framework/YYCategories.xcodeproj/xcshareddata/xcschemes/YYCategories.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ibireme 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | YYCategories 2 | ============== 3 | 4 | [![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/ibireme/YYCategories/master/LICENSE)  5 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)  6 | [![CocoaPods](http://img.shields.io/cocoapods/v/YYCategories.svg?style=flat)](http://cocoapods.org/pods/YYCategories)  7 | [![CocoaPods](http://img.shields.io/cocoapods/p/YYCategories.svg?style=flat)](http://cocoadocs.org/docsets/YYCategories)  8 | [![Support](https://img.shields.io/badge/support-iOS%206%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/)  9 | [![Build Status](https://travis-ci.org/ibireme/YYCategories.svg?branch=master)](https://travis-ci.org/ibireme/YYCategories) 10 | 11 | A set of useful categories for Foundation and UIKit.
12 | (It's a component of [YYKit](https://github.com/ibireme/YYKit)) 13 | 14 | Documentation 15 | ============== 16 | 17 | You can build and install docset use `Docset` scheme in Xcode, `appledoc` need to be pre-installed. 18 | Or your can read the [Documentation](http://github.ibireme.com/doc/YYCategories/index.html) online. 19 | 20 | 21 | Installation 22 | ============== 23 | 24 | ### CocoaPods 25 | 26 | 1. Add `pod 'YYCategories'` to your Podfile. 27 | 2. Run `pod install` or `pod update`. 28 | 3. Import \. 29 | 30 | 31 | ### Carthage 32 | 33 | 1. Add `github "ibireme/YYCategories"` to your Cartfile. 34 | 2. Run `carthage update --platform ios` and add the framework to your project. 35 | 3. Import \. 36 | 37 | 38 | ### Manually 39 | 40 | 1. Download all the files in the YYCategories subdirectory. 41 | 2. Add the source files to your Xcode project. 42 | 3. Add `-fno-objc-arc` compiler flag to `NSObject+YYAddForARC.m` and `NSThread+YYAdd.m`. 43 | 4. Link with required frameworks: 44 | * UIKit 45 | * CoreGraphics 46 | * QuartzCore 47 | * Accelerate 48 | * ImageIO 49 | * CoreText 50 | * CoreFoundation 51 | * libz 52 | 5. Import `YYCategories.h`. 53 | 54 | 55 | Documentation 56 | ============== 57 | Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYCategories/).
58 | You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc). 59 | 60 | 61 | Requirements 62 | ============== 63 | This library requires `iOS 6.0+` and `Xcode 8.0+`. 64 | 65 | 66 | Notice 67 | ============== 68 | I want to use the APIs as if it was provided by system, so I don't add prefix in 69 | these categories. This may cause some potential problems(such as conflict with other libraries), so if you just need some pieces of code 70 | in this project, pick them out and don't import the whole library. 71 | 72 | 73 | License 74 | ============== 75 | YYCategories is provided under the MIT license. See LICENSE file for details. 76 | 77 | 78 | 79 | 80 |

81 | --- 82 | 中文介绍 83 | ============== 84 | 功能丰富的 Category 类型工具库。
85 | (该项目是 [YYKit](https://github.com/ibireme/YYKit) 组件之一) 86 | 87 | 文档 88 | ============== 89 | 90 | 你可以用 `Docset` scheme 来生成文档 (需要预先安装 appledoc),或者[在线查看](http://github.ibireme.com/doc/YYCategories/index.html)。 91 | 92 | 93 | 安装 94 | ============== 95 | 96 | ### CocoaPods 97 | 98 | 1. 在 Podfile 中添加 `pod 'YYCategories'`。 99 | 2. 执行 `pod install` 或 `pod update`。 100 | 3. 导入 \。 101 | 102 | 103 | ### Carthage 104 | 105 | 1. 在 Cartfile 中添加 `github "ibireme/YYCategories"`。 106 | 2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。 107 | 3. 导入 \。 108 | 109 | 110 | ### 手动安装 111 | 112 | 1. 下载 YYCategories 文件夹内的所有内容。 113 | 2. 将 YYCategories 内的源文件添加(拖放)到你的工程。 114 | 3. 为 `NSObject+YYAddForARC.m` 和 `NSThread+YYAdd.m` 添加编译参数 `-fno-objc-arc`。 115 | 4. 链接以下 frameworks: 116 | * UIKit 117 | * CoreGraphics 118 | * QuartzCore 119 | * Accelerate 120 | * ImageIO 121 | * CoreText 122 | * CoreFoundation 123 | * libz 124 | 5. 导入 `YYCategories.h`。 125 | 126 | 127 | 文档 128 | ============== 129 | 你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYCategories/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。 130 | 131 | 系统要求 132 | ============== 133 | 该项目最低支持 `iOS 6.0` 和 `Xcode 8.0`。 134 | 135 | 136 | 注意 137 | ============== 138 | 我希望调用 API 时,有着和调用系统自带 API 一样的体验,所以我并没有为 Category 方法添加前缀。我已经用工具扫描过这个项目中的 API,确保没有对系统 API 产生影响。我知道没有前缀的 Category 可能会带来麻烦(比如可能和其他某些类库产生冲突),所以如果你只需要其中少量代码,那最好将那段代码取出来,而不是导入整个库。 139 | 140 | 141 | 许可证 142 | ============== 143 | YYCategories 使用 MIT 许可证,详情见 LICENSE 文件。 144 | 145 | 146 | -------------------------------------------------------------------------------- /YYCategories.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'YYCategories' 3 | s.summary = 'A manager to get iOS keyboard views, frames and transform.' 4 | s.version = '1.0.4' 5 | s.license = { :type => 'MIT', :file => 'LICENSE' } 6 | s.authors = { 'ibireme' => 'ibireme@gmail.com' } 7 | s.social_media_url = 'http://blog.ibireme.com' 8 | s.homepage = 'https://github.com/ibireme/YYCategories' 9 | s.platform = :ios, '6.0' 10 | s.ios.deployment_target = '6.0' 11 | s.source = { :git => 'https://github.com/ibireme/YYCategories.git', :tag => s.version.to_s } 12 | 13 | s.requires_arc = true 14 | s.source_files = 'YYCategories/**/*.{h,m}' 15 | s.public_header_files = 'YYCategories/**/*.{h}' 16 | 17 | non_arc_files = 'YYCategories/Foundation/NSObject+YYAddForARC.{h,m}', 'YYCategories/Foundation/NSThread+YYAdd.{h,m}' 18 | s.ios.exclude_files = non_arc_files 19 | s.subspec 'no-arc' do |sna| 20 | sna.requires_arc = false 21 | sna.source_files = non_arc_files 22 | end 23 | 24 | s.libraries = 'z' 25 | s.frameworks = 'UIKit', 'CoreFoundation' ,'QuartzCore', 'CoreGraphics', 'CoreImage', 'CoreText', 'ImageIO', 'Accelerate' 26 | 27 | end 28 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSArray+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+YYAdd.h 3 | // YYCategories 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 | Provide some some common method for `NSArray`. 18 | */ 19 | @interface NSArray (YYAdd) 20 | 21 | /** 22 | Creates and returns an array from a specified property list data. 23 | 24 | @param plist A property list data whose root object is an array. 25 | @return A new array created from the plist data, or nil if an error occurs. 26 | */ 27 | + (nullable NSArray *)arrayWithPlistData:(NSData *)plist; 28 | 29 | /** 30 | Creates and returns an array from a specified property list xml string. 31 | 32 | @param plist A property list xml string whose root object is an array. 33 | @return A new array created from the plist string, or nil if an error occurs. 34 | */ 35 | + (nullable NSArray *)arrayWithPlistString:(NSString *)plist; 36 | 37 | /** 38 | Serialize the array to a binary property list data. 39 | 40 | @return A bplist data, or nil if an error occurs. 41 | */ 42 | - (nullable NSData *)plistData; 43 | 44 | /** 45 | Serialize the array to a xml property list string. 46 | 47 | @return A plist xml string, or nil if an error occurs. 48 | */ 49 | - (nullable NSString *)plistString; 50 | 51 | /** 52 | Returns the object located at a random index. 53 | 54 | @return The object in the array with a random index value. 55 | If the array is empty, returns nil. 56 | */ 57 | - (nullable id)randomObject; 58 | 59 | /** 60 | Returns the object located at index, or return nil when out of bounds. 61 | It's similar to `objectAtIndex:`, but it never throw exception. 62 | 63 | @param index The object located at index. 64 | */ 65 | - (nullable id)objectOrNilAtIndex:(NSUInteger)index; 66 | 67 | /** 68 | Convert object to json string. return nil if an error occurs. 69 | NSString/NSNumber/NSDictionary/NSArray 70 | */ 71 | - (nullable NSString *)jsonStringEncoded; 72 | 73 | /** 74 | Convert object to json string formatted. return nil if an error occurs. 75 | */ 76 | - (nullable NSString *)jsonPrettyStringEncoded; 77 | 78 | @end 79 | 80 | 81 | /** 82 | Provide some some common method for `NSMutableArray`. 83 | */ 84 | @interface NSMutableArray (YYAdd) 85 | 86 | /** 87 | Creates and returns an array from a specified property list data. 88 | 89 | @param plist A property list data whose root object is an array. 90 | @return A new array created from the plist data, or nil if an error occurs. 91 | */ 92 | + (nullable NSMutableArray *)arrayWithPlistData:(NSData *)plist; 93 | 94 | /** 95 | Creates and returns an array from a specified property list xml string. 96 | 97 | @param plist A property list xml string whose root object is an array. 98 | @return A new array created from the plist string, or nil if an error occurs. 99 | */ 100 | + (nullable NSMutableArray *)arrayWithPlistString:(NSString *)plist; 101 | 102 | /** 103 | Removes the object with the lowest-valued index in the array. 104 | If the array is empty, this method has no effect. 105 | 106 | @discussion Apple has implemented this method, but did not make it public. 107 | Override for safe. 108 | */ 109 | - (void)removeFirstObject; 110 | 111 | /** 112 | Removes the object with the highest-valued index in the array. 113 | If the array is empty, this method has no effect. 114 | 115 | @discussion Apple's implementation said it raises an NSRangeException if the 116 | array is empty, but in fact nothing will happen. Override for safe. 117 | */ 118 | - (void)removeLastObject; 119 | 120 | /** 121 | Removes and returns the object with the lowest-valued index in the array. 122 | If the array is empty, it just returns nil. 123 | 124 | @return The first object, or nil. 125 | */ 126 | - (nullable id)popFirstObject; 127 | 128 | /** 129 | Removes and returns the object with the highest-valued index in the array. 130 | If the array is empty, it just returns nil. 131 | 132 | @return The first object, or nil. 133 | */ 134 | - (nullable id)popLastObject; 135 | 136 | /** 137 | Inserts a given object at the end of the array. 138 | 139 | @param anObject The object to add to the end of the array's content. 140 | This value must not be nil. Raises an NSInvalidArgumentException if anObject is nil. 141 | */ 142 | - (void)appendObject:(id)anObject; 143 | 144 | /** 145 | Inserts a given object at the beginning of the array. 146 | 147 | @param anObject The object to add to the end of the array's content. 148 | This value must not be nil. Raises an NSInvalidArgumentException if anObject is nil. 149 | */ 150 | - (void)prependObject:(id)anObject; 151 | 152 | /** 153 | Adds the objects contained in another given array to the end of the receiving 154 | array's content. 155 | 156 | @param objects An array of objects to add to the end of the receiving array's 157 | content. If the objects is empty or nil, this method has no effect. 158 | */ 159 | - (void)appendObjects:(NSArray *)objects; 160 | 161 | /** 162 | Adds the objects contained in another given array to the beginnin of the receiving 163 | array's content. 164 | 165 | @param objects An array of objects to add to the beginning of the receiving array's 166 | content. If the objects is empty or nil, this method has no effect. 167 | */ 168 | - (void)prependObjects:(NSArray *)objects; 169 | 170 | /** 171 | Adds the objects contained in another given array at the index of the receiving 172 | array's content. 173 | 174 | @param objects An array of objects to add to the receiving array's 175 | content. If the objects is empty or nil, this method has no effect. 176 | 177 | @param index The index in the array at which to insert objects. This value must 178 | not be greater than the count of elements in the array. Raises an 179 | NSRangeException if index is greater than the number of elements in the array. 180 | */ 181 | - (void)insertObjects:(NSArray *)objects atIndex:(NSUInteger)index; 182 | 183 | /** 184 | Reverse the index of object in this array. 185 | Example: Before @[ @1, @2, @3 ], After @[ @3, @2, @1 ]. 186 | */ 187 | - (void)reverse; 188 | 189 | /** 190 | Sort the object in this array randomly. 191 | */ 192 | - (void)shuffle; 193 | 194 | @end 195 | 196 | NS_ASSUME_NONNULL_END 197 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSArray+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+YYAdd.m 3 | // YYCategories 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 "NSArray+YYAdd.h" 13 | #import "YYCategoriesMacro.h" 14 | #import "NSData+YYAdd.h" 15 | 16 | YYSYNTH_DUMMY_CLASS(NSArray_YYAdd) 17 | 18 | 19 | @implementation NSArray (YYAdd) 20 | 21 | + (NSArray *)arrayWithPlistData:(NSData *)plist { 22 | if (!plist) return nil; 23 | NSArray *array = [NSPropertyListSerialization propertyListWithData:plist options:NSPropertyListImmutable format:NULL error:NULL]; 24 | if ([array isKindOfClass:[NSArray class]]) return array; 25 | return nil; 26 | } 27 | 28 | + (NSArray *)arrayWithPlistString:(NSString *)plist { 29 | if (!plist) return nil; 30 | NSData* data = [plist dataUsingEncoding:NSUTF8StringEncoding]; 31 | return [self arrayWithPlistData:data]; 32 | } 33 | 34 | - (NSData *)plistData { 35 | return [NSPropertyListSerialization dataWithPropertyList:self format:NSPropertyListBinaryFormat_v1_0 options:kNilOptions error:NULL]; 36 | } 37 | 38 | - (NSString *)plistString { 39 | NSData *xmlData = [NSPropertyListSerialization dataWithPropertyList:self format:NSPropertyListXMLFormat_v1_0 options:kNilOptions error:NULL]; 40 | if (xmlData) return xmlData.utf8String; 41 | return nil; 42 | } 43 | 44 | - (id)randomObject { 45 | if (self.count) { 46 | return self[arc4random_uniform((u_int32_t)self.count)]; 47 | } 48 | return nil; 49 | } 50 | 51 | - (id)objectOrNilAtIndex:(NSUInteger)index { 52 | return index < self.count ? self[index] : nil; 53 | } 54 | 55 | - (NSString *)jsonStringEncoded { 56 | if ([NSJSONSerialization isValidJSONObject:self]) { 57 | NSError *error; 58 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:0 error:&error]; 59 | NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 60 | return json; 61 | } 62 | return nil; 63 | } 64 | 65 | - (NSString *)jsonPrettyStringEncoded { 66 | if ([NSJSONSerialization isValidJSONObject:self]) { 67 | NSError *error; 68 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error]; 69 | NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 70 | return json; 71 | } 72 | return nil; 73 | } 74 | 75 | @end 76 | 77 | 78 | 79 | @implementation NSMutableArray (YYAdd) 80 | 81 | + (NSMutableArray *)arrayWithPlistData:(NSData *)plist { 82 | if (!plist) return nil; 83 | NSMutableArray *array = [NSPropertyListSerialization propertyListWithData:plist options:NSPropertyListMutableContainersAndLeaves format:NULL error:NULL]; 84 | if ([array isKindOfClass:[NSMutableArray class]]) return array; 85 | return nil; 86 | } 87 | 88 | + (NSMutableArray *)arrayWithPlistString:(NSString *)plist { 89 | if (!plist) return nil; 90 | NSData* data = [plist dataUsingEncoding:NSUTF8StringEncoding]; 91 | return [self arrayWithPlistData:data]; 92 | } 93 | 94 | - (void)removeFirstObject { 95 | if (self.count) { 96 | [self removeObjectAtIndex:0]; 97 | } 98 | } 99 | 100 | #pragma clang diagnostic push 101 | #pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" 102 | - (void)removeLastObject { 103 | if (self.count) { 104 | [self removeObjectAtIndex:self.count - 1]; 105 | } 106 | } 107 | 108 | #pragma clang diagnostic pop 109 | 110 | 111 | - (id)popFirstObject { 112 | id obj = nil; 113 | if (self.count) { 114 | obj = self.firstObject; 115 | [self removeFirstObject]; 116 | } 117 | return obj; 118 | } 119 | 120 | - (id)popLastObject { 121 | id obj = nil; 122 | if (self.count) { 123 | obj = self.lastObject; 124 | [self removeLastObject]; 125 | } 126 | return obj; 127 | } 128 | 129 | - (void)appendObject:(id)anObject { 130 | [self addObject:anObject]; 131 | } 132 | 133 | - (void)prependObject:(id)anObject { 134 | [self insertObject:anObject atIndex:0]; 135 | } 136 | 137 | - (void)appendObjects:(NSArray *)objects { 138 | if (!objects) return; 139 | [self addObjectsFromArray:objects]; 140 | } 141 | 142 | - (void)prependObjects:(NSArray *)objects { 143 | if (!objects) return; 144 | NSUInteger i = 0; 145 | for (id obj in objects) { 146 | [self insertObject:obj atIndex:i++]; 147 | } 148 | } 149 | 150 | - (void)insertObjects:(NSArray *)objects atIndex:(NSUInteger)index { 151 | NSUInteger i = index; 152 | for (id obj in objects) { 153 | [self insertObject:obj atIndex:i++]; 154 | } 155 | } 156 | 157 | - (void)reverse { 158 | NSUInteger count = self.count; 159 | int mid = floor(count / 2.0); 160 | for (NSUInteger i = 0; i < mid; i++) { 161 | [self exchangeObjectAtIndex:i withObjectAtIndex:(count - (i + 1))]; 162 | } 163 | } 164 | 165 | - (void)shuffle { 166 | for (NSUInteger i = self.count; i > 1; i--) { 167 | [self exchangeObjectAtIndex:(i - 1) 168 | withObjectAtIndex:arc4random_uniform((u_int32_t)i)]; 169 | } 170 | } 171 | 172 | @end 173 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSBundle+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+YYAdd.h 3 | // YYCategories 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 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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSBundle+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSData+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+YYAdd.h 3 | // YYCategories 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 | Provide hash, encrypt, encode and some common method for `NSData`. 18 | */ 19 | @interface NSData (YYAdd) 20 | 21 | #pragma mark - Hash 22 | ///============================================================================= 23 | /// @name Hash 24 | ///============================================================================= 25 | 26 | /** 27 | Returns a lowercase NSString for md2 hash. 28 | */ 29 | - (NSString *)md2String; 30 | 31 | /** 32 | Returns an NSData for md2 hash. 33 | */ 34 | - (NSData *)md2Data; 35 | 36 | /** 37 | Returns a lowercase NSString for md4 hash. 38 | */ 39 | - (NSString *)md4String; 40 | 41 | /** 42 | Returns an NSData for md4 hash. 43 | */ 44 | - (NSData *)md4Data; 45 | 46 | /** 47 | Returns a lowercase NSString for md5 hash. 48 | */ 49 | - (NSString *)md5String; 50 | 51 | /** 52 | Returns an NSData for md5 hash. 53 | */ 54 | - (NSData *)md5Data; 55 | 56 | /** 57 | Returns a lowercase NSString for sha1 hash. 58 | */ 59 | - (NSString *)sha1String; 60 | 61 | /** 62 | Returns an NSData for sha1 hash. 63 | */ 64 | - (NSData *)sha1Data; 65 | 66 | /** 67 | Returns a lowercase NSString for sha224 hash. 68 | */ 69 | - (NSString *)sha224String; 70 | 71 | /** 72 | Returns an NSData for sha224 hash. 73 | */ 74 | - (NSData *)sha224Data; 75 | 76 | /** 77 | Returns a lowercase NSString for sha256 hash. 78 | */ 79 | - (NSString *)sha256String; 80 | 81 | /** 82 | Returns an NSData for sha256 hash. 83 | */ 84 | - (NSData *)sha256Data; 85 | 86 | /** 87 | Returns a lowercase NSString for sha384 hash. 88 | */ 89 | - (NSString *)sha384String; 90 | 91 | /** 92 | Returns an NSData for sha384 hash. 93 | */ 94 | - (NSData *)sha384Data; 95 | 96 | /** 97 | Returns a lowercase NSString for sha512 hash. 98 | */ 99 | - (NSString *)sha512String; 100 | 101 | /** 102 | Returns an NSData for sha512 hash. 103 | */ 104 | - (NSData *)sha512Data; 105 | 106 | /** 107 | Returns a lowercase NSString for hmac using algorithm md5 with key. 108 | @param key The hmac key. 109 | */ 110 | - (NSString *)hmacMD5StringWithKey:(NSString *)key; 111 | 112 | /** 113 | Returns an NSData for hmac using algorithm md5 with key. 114 | @param key The hmac key. 115 | */ 116 | - (NSData *)hmacMD5DataWithKey:(NSData *)key; 117 | 118 | /** 119 | Returns a lowercase NSString for hmac using algorithm sha1 with key. 120 | @param key The hmac key. 121 | */ 122 | - (NSString *)hmacSHA1StringWithKey:(NSString *)key; 123 | 124 | /** 125 | Returns an NSData for hmac using algorithm sha1 with key. 126 | @param key The hmac key. 127 | */ 128 | - (NSData *)hmacSHA1DataWithKey:(NSData *)key; 129 | 130 | /** 131 | Returns a lowercase NSString for hmac using algorithm sha224 with key. 132 | @param key The hmac key. 133 | */ 134 | - (NSString *)hmacSHA224StringWithKey:(NSString *)key; 135 | 136 | /** 137 | Returns an NSData for hmac using algorithm sha224 with key. 138 | @param key The hmac key. 139 | */ 140 | - (NSData *)hmacSHA224DataWithKey:(NSData *)key; 141 | 142 | /** 143 | Returns a lowercase NSString for hmac using algorithm sha256 with key. 144 | @param key The hmac key. 145 | */ 146 | - (NSString *)hmacSHA256StringWithKey:(NSString *)key; 147 | 148 | /** 149 | Returns an NSData for hmac using algorithm sha256 with key. 150 | @param key The hmac key. 151 | */ 152 | - (NSData *)hmacSHA256DataWithKey:(NSData *)key; 153 | 154 | /** 155 | Returns a lowercase NSString for hmac using algorithm sha384 with key. 156 | @param key The hmac key. 157 | */ 158 | - (NSString *)hmacSHA384StringWithKey:(NSString *)key; 159 | 160 | /** 161 | Returns an NSData for hmac using algorithm sha384 with key. 162 | @param key The hmac key. 163 | */ 164 | - (NSData *)hmacSHA384DataWithKey:(NSData *)key; 165 | 166 | /** 167 | Returns a lowercase NSString for hmac using algorithm sha512 with key. 168 | @param key The hmac key. 169 | */ 170 | - (NSString *)hmacSHA512StringWithKey:(NSString *)key; 171 | 172 | /** 173 | Returns an NSData for hmac using algorithm sha512 with key. 174 | @param key The hmac key. 175 | */ 176 | - (NSData *)hmacSHA512DataWithKey:(NSData *)key; 177 | 178 | /** 179 | Returns a lowercase NSString for crc32 hash. 180 | */ 181 | - (NSString *)crc32String; 182 | 183 | /** 184 | Returns crc32 hash. 185 | */ 186 | - (uint32_t)crc32; 187 | 188 | 189 | #pragma mark - Encrypt and Decrypt 190 | ///============================================================================= 191 | /// @name Encrypt and Decrypt 192 | ///============================================================================= 193 | 194 | /** 195 | Returns an encrypted NSData using AES. 196 | 197 | @param key A key length of 16, 24 or 32 (128, 192 or 256bits). 198 | 199 | @param iv An initialization vector length of 16(128bits). 200 | Pass nil when you don't want to use iv. 201 | 202 | @return An NSData encrypted, or nil if an error occurs. 203 | */ 204 | - (nullable NSData *)aes256EncryptWithKey:(NSData *)key iv:(nullable NSData *)iv; 205 | 206 | /** 207 | Returns an decrypted NSData using AES. 208 | 209 | @param key A key length of 16, 24 or 32 (128, 192 or 256bits). 210 | 211 | @param iv An initialization vector length of 16(128bits). 212 | Pass nil when you don't want to use iv. 213 | 214 | @return An NSData decrypted, or nil if an error occurs. 215 | */ 216 | - (nullable NSData *)aes256DecryptWithkey:(NSData *)key iv:(nullable NSData *)iv; 217 | 218 | 219 | #pragma mark - Encode and decode 220 | ///============================================================================= 221 | /// @name Encode and decode 222 | ///============================================================================= 223 | 224 | /** 225 | Returns string decoded in UTF8. 226 | */ 227 | - (nullable NSString *)utf8String; 228 | 229 | /** 230 | Returns a uppercase NSString in HEX. 231 | */ 232 | - (nullable NSString *)hexString; 233 | 234 | /** 235 | Returns an NSData from hex string. 236 | 237 | @param hexString The hex string which is case insensitive. 238 | 239 | @return a new NSData, or nil if an error occurs. 240 | */ 241 | + (nullable NSData *)dataWithHexString:(NSString *)hexString; 242 | 243 | /** 244 | Returns an NSString for base64 encoded. 245 | */ 246 | - (nullable NSString *)base64EncodedString; 247 | 248 | /** 249 | Returns an NSData from base64 encoded string. 250 | 251 | @warning This method has been implemented in iOS7. 252 | 253 | @param base64EncodedString The encoded string. 254 | */ 255 | + (nullable NSData *)dataWithBase64EncodedString:(NSString *)base64EncodedString; 256 | 257 | /** 258 | Returns an NSDictionary or NSArray for decoded self. 259 | Returns nil if an error occurs. 260 | */ 261 | - (nullable id)jsonValueDecoded; 262 | 263 | 264 | #pragma mark - Inflate and deflate 265 | ///============================================================================= 266 | /// @name Inflate and deflate 267 | ///============================================================================= 268 | 269 | /** 270 | Decompress data from gzip data. 271 | @return Inflated data. 272 | */ 273 | - (nullable NSData *)gzipInflate; 274 | 275 | /** 276 | Comperss data to gzip in default compresssion level. 277 | @return Deflated data. 278 | */ 279 | - (nullable NSData *)gzipDeflate; 280 | 281 | /** 282 | Decompress data from zlib-compressed data. 283 | @return Inflated data. 284 | */ 285 | - (nullable NSData *)zlibInflate; 286 | 287 | /** 288 | Comperss data to zlib-compressed in default compresssion level. 289 | @return Deflated data. 290 | */ 291 | - (nullable NSData *)zlibDeflate; 292 | 293 | 294 | #pragma mark - Others 295 | ///============================================================================= 296 | /// @name Others 297 | ///============================================================================= 298 | 299 | /** 300 | Create data from the file in main bundle (similar to [UIImage imageNamed:]). 301 | 302 | @param name The file name (in main bundle). 303 | 304 | @return A new data create from the file. 305 | */ 306 | + (nullable NSData *)dataNamed:(NSString *)name; 307 | 308 | @end 309 | 310 | NS_ASSUME_NONNULL_END 311 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSDate+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+YYAdd.h 3 | // YYCategories 4 | // 5 | // Created by ibireme on 13/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 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provides extensions for `NSDate`. 18 | */ 19 | @interface NSDate (YYAdd) 20 | 21 | #pragma mark - Component Properties 22 | ///============================================================================= 23 | /// @name Component Properties 24 | ///============================================================================= 25 | 26 | @property (nonatomic, readonly) NSInteger year; ///< Year component 27 | @property (nonatomic, readonly) NSInteger month; ///< Month component (1~12) 28 | @property (nonatomic, readonly) NSInteger day; ///< Day component (1~31) 29 | @property (nonatomic, readonly) NSInteger hour; ///< Hour component (0~23) 30 | @property (nonatomic, readonly) NSInteger minute; ///< Minute component (0~59) 31 | @property (nonatomic, readonly) NSInteger second; ///< Second component (0~59) 32 | @property (nonatomic, readonly) NSInteger nanosecond; ///< Nanosecond component 33 | @property (nonatomic, readonly) NSInteger weekday; ///< Weekday component (1~7, first day is based on user setting) 34 | @property (nonatomic, readonly) NSInteger weekdayOrdinal; ///< WeekdayOrdinal component 35 | @property (nonatomic, readonly) NSInteger weekOfMonth; ///< WeekOfMonth component (1~5) 36 | @property (nonatomic, readonly) NSInteger weekOfYear; ///< WeekOfYear component (1~53) 37 | @property (nonatomic, readonly) NSInteger yearForWeekOfYear; ///< YearForWeekOfYear component 38 | @property (nonatomic, readonly) NSInteger quarter; ///< Quarter component 39 | @property (nonatomic, readonly) BOOL isLeapMonth; ///< Weather the month is leap month 40 | @property (nonatomic, readonly) BOOL isLeapYear; ///< Weather the year is leap year 41 | @property (nonatomic, readonly) BOOL isToday; ///< Weather date is today (based on current locale) 42 | @property (nonatomic, readonly) BOOL isYesterday; ///< Weather date is yesterday (based on current locale) 43 | 44 | #pragma mark - Date modify 45 | ///============================================================================= 46 | /// @name Date modify 47 | ///============================================================================= 48 | 49 | /** 50 | Returns a date representing the receiver date shifted later by the provided number of years. 51 | 52 | @param years Number of years to add. 53 | @return Date modified by the number of desired years. 54 | */ 55 | - (nullable NSDate *)dateByAddingYears:(NSInteger)years; 56 | 57 | /** 58 | Returns a date representing the receiver date shifted later by the provided number of months. 59 | 60 | @param months Number of months to add. 61 | @return Date modified by the number of desired months. 62 | */ 63 | - (nullable NSDate *)dateByAddingMonths:(NSInteger)months; 64 | 65 | /** 66 | Returns a date representing the receiver date shifted later by the provided number of weeks. 67 | 68 | @param weeks Number of weeks to add. 69 | @return Date modified by the number of desired weeks. 70 | */ 71 | - (nullable NSDate *)dateByAddingWeeks:(NSInteger)weeks; 72 | 73 | /** 74 | Returns a date representing the receiver date shifted later by the provided number of days. 75 | 76 | @param days Number of days to add. 77 | @return Date modified by the number of desired days. 78 | */ 79 | - (nullable NSDate *)dateByAddingDays:(NSInteger)days; 80 | 81 | /** 82 | Returns a date representing the receiver date shifted later by the provided number of hours. 83 | 84 | @param hours Number of hours to add. 85 | @return Date modified by the number of desired hours. 86 | */ 87 | - (nullable NSDate *)dateByAddingHours:(NSInteger)hours; 88 | 89 | /** 90 | Returns a date representing the receiver date shifted later by the provided number of minutes. 91 | 92 | @param minutes Number of minutes to add. 93 | @return Date modified by the number of desired minutes. 94 | */ 95 | - (nullable NSDate *)dateByAddingMinutes:(NSInteger)minutes; 96 | 97 | /** 98 | Returns a date representing the receiver date shifted later by the provided number of seconds. 99 | 100 | @param seconds Number of seconds to add. 101 | @return Date modified by the number of desired seconds. 102 | */ 103 | - (nullable NSDate *)dateByAddingSeconds:(NSInteger)seconds; 104 | 105 | 106 | #pragma mark - Date Format 107 | ///============================================================================= 108 | /// @name Date Format 109 | ///============================================================================= 110 | 111 | /** 112 | Returns a formatted string representing this date. 113 | see http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns 114 | for format description. 115 | 116 | @param format String representing the desired date format. 117 | e.g. @"yyyy-MM-dd HH:mm:ss" 118 | 119 | @return NSString representing the formatted date string. 120 | */ 121 | - (nullable NSString *)stringWithFormat:(NSString *)format; 122 | 123 | /** 124 | Returns a formatted string representing this date. 125 | see http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns 126 | for format description. 127 | 128 | @param format String representing the desired date format. 129 | e.g. @"yyyy-MM-dd HH:mm:ss" 130 | 131 | @param timeZone Desired time zone. 132 | 133 | @param locale Desired locale. 134 | 135 | @return NSString representing the formatted date string. 136 | */ 137 | - (nullable NSString *)stringWithFormat:(NSString *)format 138 | timeZone:(nullable NSTimeZone *)timeZone 139 | locale:(nullable NSLocale *)locale; 140 | 141 | /** 142 | Returns a string representing this date in ISO8601 format. 143 | e.g. "2010-07-09T16:13:30+12:00" 144 | 145 | @return NSString representing the formatted date string in ISO8601. 146 | */ 147 | - (nullable NSString *)stringWithISOFormat; 148 | 149 | /** 150 | Returns a date parsed from given string interpreted using the format. 151 | 152 | @param dateString The string to parse. 153 | @param format The string's date format. 154 | 155 | @return A date representation of string interpreted using the format. 156 | If can not parse the string, returns nil. 157 | */ 158 | + (nullable NSDate *)dateWithString:(NSString *)dateString format:(NSString *)format; 159 | 160 | /** 161 | Returns a date parsed from given string interpreted using the format. 162 | 163 | @param dateString The string to parse. 164 | @param format The string's date format. 165 | @param timeZone The time zone, can be nil. 166 | @param locale The locale, can be nil. 167 | 168 | @return A date representation of string interpreted using the format. 169 | If can not parse the string, returns nil. 170 | */ 171 | + (nullable NSDate *)dateWithString:(NSString *)dateString 172 | format:(NSString *)format 173 | timeZone:(nullable NSTimeZone *)timeZone 174 | locale:(nullable NSLocale *)locale; 175 | 176 | /** 177 | Returns a date parsed from given string interpreted using the ISO8601 format. 178 | 179 | @param dateString The date string in ISO8601 format. e.g. "2010-07-09T16:13:30+12:00" 180 | 181 | @return A date representation of string interpreted using the format. 182 | If can not parse the string, returns nil. 183 | */ 184 | + (nullable NSDate *)dateWithISOFormatString:(NSString *)dateString; 185 | 186 | @end 187 | 188 | NS_ASSUME_NONNULL_END 189 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSDate+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+YYAdd.m 3 | // YYCategories 4 | // 5 | // Created by ibireme on 13/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 "NSDate+YYAdd.h" 13 | #import "YYCategoriesMacro.h" 14 | #import 15 | 16 | YYSYNTH_DUMMY_CLASS(NSDate_YYAdd) 17 | 18 | 19 | @implementation NSDate (YYAdd) 20 | 21 | - (NSInteger)year { 22 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitYear fromDate:self] year]; 23 | } 24 | 25 | - (NSInteger)month { 26 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitMonth fromDate:self] month]; 27 | } 28 | 29 | - (NSInteger)day { 30 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitDay fromDate:self] day]; 31 | } 32 | 33 | - (NSInteger)hour { 34 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitHour fromDate:self] hour]; 35 | } 36 | 37 | - (NSInteger)minute { 38 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitMinute fromDate:self] minute]; 39 | } 40 | 41 | - (NSInteger)second { 42 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitSecond fromDate:self] second]; 43 | } 44 | 45 | - (NSInteger)nanosecond { 46 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitSecond fromDate:self] nanosecond]; 47 | } 48 | 49 | - (NSInteger)weekday { 50 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitWeekday fromDate:self] weekday]; 51 | } 52 | 53 | - (NSInteger)weekdayOrdinal { 54 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitWeekdayOrdinal fromDate:self] weekdayOrdinal]; 55 | } 56 | 57 | - (NSInteger)weekOfMonth { 58 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitWeekOfMonth fromDate:self] weekOfMonth]; 59 | } 60 | 61 | - (NSInteger)weekOfYear { 62 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitWeekOfYear fromDate:self] weekOfYear]; 63 | } 64 | 65 | - (NSInteger)yearForWeekOfYear { 66 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitYearForWeekOfYear fromDate:self] yearForWeekOfYear]; 67 | } 68 | 69 | - (NSInteger)quarter { 70 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitQuarter fromDate:self] quarter]; 71 | } 72 | 73 | - (BOOL)isLeapMonth { 74 | return [[[NSCalendar currentCalendar] components:NSCalendarUnitQuarter fromDate:self] isLeapMonth]; 75 | } 76 | 77 | - (BOOL)isLeapYear { 78 | NSUInteger year = self.year; 79 | return ((year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0))); 80 | } 81 | 82 | - (BOOL)isToday { 83 | if (fabs(self.timeIntervalSinceNow) >= 60 * 60 * 24) return NO; 84 | return [NSDate new].day == self.day; 85 | } 86 | 87 | - (BOOL)isYesterday { 88 | NSDate *added = [self dateByAddingDays:1]; 89 | return [added isToday]; 90 | } 91 | 92 | - (NSDate *)dateByAddingYears:(NSInteger)years { 93 | NSCalendar *calendar = [NSCalendar currentCalendar]; 94 | NSDateComponents *components = [[NSDateComponents alloc] init]; 95 | [components setYear:years]; 96 | return [calendar dateByAddingComponents:components toDate:self options:0]; 97 | } 98 | 99 | - (NSDate *)dateByAddingMonths:(NSInteger)months { 100 | NSCalendar *calendar = [NSCalendar currentCalendar]; 101 | NSDateComponents *components = [[NSDateComponents alloc] init]; 102 | [components setMonth:months]; 103 | return [calendar dateByAddingComponents:components toDate:self options:0]; 104 | } 105 | 106 | - (NSDate *)dateByAddingWeeks:(NSInteger)weeks { 107 | NSCalendar *calendar = [NSCalendar currentCalendar]; 108 | NSDateComponents *components = [[NSDateComponents alloc] init]; 109 | [components setWeekOfYear:weeks]; 110 | return [calendar dateByAddingComponents:components toDate:self options:0]; 111 | } 112 | 113 | - (NSDate *)dateByAddingDays:(NSInteger)days { 114 | NSTimeInterval aTimeInterval = [self timeIntervalSinceReferenceDate] + 86400 * days; 115 | NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval]; 116 | return newDate; 117 | } 118 | 119 | - (NSDate *)dateByAddingHours:(NSInteger)hours { 120 | NSTimeInterval aTimeInterval = [self timeIntervalSinceReferenceDate] + 3600 * hours; 121 | NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval]; 122 | return newDate; 123 | } 124 | 125 | - (NSDate *)dateByAddingMinutes:(NSInteger)minutes { 126 | NSTimeInterval aTimeInterval = [self timeIntervalSinceReferenceDate] + 60 * minutes; 127 | NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval]; 128 | return newDate; 129 | } 130 | 131 | - (NSDate *)dateByAddingSeconds:(NSInteger)seconds { 132 | NSTimeInterval aTimeInterval = [self timeIntervalSinceReferenceDate] + seconds; 133 | NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval]; 134 | return newDate; 135 | } 136 | 137 | - (NSString *)stringWithFormat:(NSString *)format { 138 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 139 | [formatter setDateFormat:format]; 140 | [formatter setLocale:[NSLocale currentLocale]]; 141 | return [formatter stringFromDate:self]; 142 | } 143 | 144 | - (NSString *)stringWithFormat:(NSString *)format timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale { 145 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 146 | [formatter setDateFormat:format]; 147 | if (timeZone) [formatter setTimeZone:timeZone]; 148 | if (locale) [formatter setLocale:locale]; 149 | return [formatter stringFromDate:self]; 150 | } 151 | 152 | - (NSString *)stringWithISOFormat { 153 | static NSDateFormatter *formatter = nil; 154 | static dispatch_once_t onceToken; 155 | dispatch_once(&onceToken, ^{ 156 | formatter = [[NSDateFormatter alloc] init]; 157 | formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; 158 | formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; 159 | }); 160 | return [formatter stringFromDate:self]; 161 | } 162 | 163 | + (NSDate *)dateWithString:(NSString *)dateString format:(NSString *)format { 164 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 165 | [formatter setDateFormat:format]; 166 | return [formatter dateFromString:dateString]; 167 | } 168 | 169 | + (NSDate *)dateWithString:(NSString *)dateString format:(NSString *)format timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale { 170 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 171 | [formatter setDateFormat:format]; 172 | if (timeZone) [formatter setTimeZone:timeZone]; 173 | if (locale) [formatter setLocale:locale]; 174 | return [formatter dateFromString:dateString]; 175 | } 176 | 177 | + (NSDate *)dateWithISOFormatString:(NSString *)dateString { 178 | static NSDateFormatter *formatter = nil; 179 | static dispatch_once_t onceToken; 180 | dispatch_once(&onceToken, ^{ 181 | formatter = [[NSDateFormatter alloc] init]; 182 | formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; 183 | formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; 184 | }); 185 | return [formatter dateFromString:dateString]; 186 | } 187 | 188 | @end 189 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSDictionary+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+YYAdd.h 3 | // YYCategories 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 | Provide some some common method for `NSDictionary`. 18 | */ 19 | @interface NSDictionary (YYAdd) 20 | 21 | #pragma mark - Dictionary Convertor 22 | ///============================================================================= 23 | /// @name Dictionary Convertor 24 | ///============================================================================= 25 | 26 | /** 27 | Creates and returns a dictionary from a specified property list data. 28 | 29 | @param plist A property list data whose root object is a dictionary. 30 | @return A new dictionary created from the plist data, or nil if an error occurs. 31 | */ 32 | + (nullable NSDictionary *)dictionaryWithPlistData:(NSData *)plist; 33 | 34 | /** 35 | Creates and returns a dictionary from a specified property list xml string. 36 | 37 | @param plist A property list xml string whose root object is a dictionary. 38 | @return A new dictionary created from the plist string, or nil if an error occurs. 39 | 40 | @discussion Apple has implemented this method, but did not make it public. 41 | */ 42 | + (nullable NSDictionary *)dictionaryWithPlistString:(NSString *)plist; 43 | 44 | /** 45 | Serialize the dictionary to a binary property list data. 46 | 47 | @return A bplist data, or nil if an error occurs. 48 | 49 | @discussion Apple has implemented this method, but did not make it public. 50 | */ 51 | - (nullable NSData *)plistData; 52 | 53 | /** 54 | Serialize the dictionary to a xml property list string. 55 | 56 | @return A plist xml string, or nil if an error occurs. 57 | */ 58 | - (nullable NSString *)plistString; 59 | 60 | /** 61 | Returns a new array containing the dictionary's keys sorted. 62 | The keys should be NSString, and they will be sorted ascending. 63 | 64 | @return A new array containing the dictionary's keys, 65 | or an empty array if the dictionary has no entries. 66 | */ 67 | - (NSArray *)allKeysSorted; 68 | 69 | /** 70 | Returns a new array containing the dictionary's values sorted by keys. 71 | 72 | The order of the values in the array is defined by keys. 73 | The keys should be NSString, and they will be sorted ascending. 74 | 75 | @return A new array containing the dictionary's values sorted by keys, 76 | or an empty array if the dictionary has no entries. 77 | */ 78 | - (NSArray *)allValuesSortedByKeys; 79 | 80 | /** 81 | Returns a BOOL value tells if the dictionary has an object for key. 82 | 83 | @param key The key. 84 | */ 85 | - (BOOL)containsObjectForKey:(id)key; 86 | 87 | /** 88 | Returns a new dictionary containing the entries for keys. 89 | If the keys is empty or nil, it just returns an empty dictionary. 90 | 91 | @param keys The keys. 92 | @return The entries for the keys. 93 | */ 94 | - (NSDictionary *)entriesForKeys:(NSArray *)keys; 95 | 96 | /** 97 | Convert dictionary to json string. return nil if an error occurs. 98 | */ 99 | - (nullable NSString *)jsonStringEncoded; 100 | 101 | /** 102 | Convert dictionary to json string formatted. return nil if an error occurs. 103 | */ 104 | - (nullable NSString *)jsonPrettyStringEncoded; 105 | 106 | /** 107 | Try to parse an XML and wrap it into a dictionary. 108 | If you just want to get some value from a small xml, try this. 109 | 110 | example XML: "link" 111 | example Return: @{@"_name":@"config", @"a":{@"_text":@"link",@"href":@"test.com"}} 112 | 113 | @param xmlDataOrString XML in NSData or NSString format. 114 | @return Return a new dictionary, or nil if an error occurs. 115 | */ 116 | + (nullable NSDictionary *)dictionaryWithXML:(id)xmlDataOrString; 117 | 118 | #pragma mark - Dictionary Value Getter 119 | ///============================================================================= 120 | /// @name Dictionary Value Getter 121 | ///============================================================================= 122 | 123 | - (BOOL)boolValueForKey:(NSString *)key default:(BOOL)def; 124 | 125 | - (char)charValueForKey:(NSString *)key default:(char)def; 126 | - (unsigned char)unsignedCharValueForKey:(NSString *)key default:(unsigned char)def; 127 | 128 | - (short)shortValueForKey:(NSString *)key default:(short)def; 129 | - (unsigned short)unsignedShortValueForKey:(NSString *)key default:(unsigned short)def; 130 | 131 | - (int)intValueForKey:(NSString *)key default:(int)def; 132 | - (unsigned int)unsignedIntValueForKey:(NSString *)key default:(unsigned int)def; 133 | 134 | - (long)longValueForKey:(NSString *)key default:(long)def; 135 | - (unsigned long)unsignedLongValueForKey:(NSString *)key default:(unsigned long)def; 136 | 137 | - (long long)longLongValueForKey:(NSString *)key default:(long long)def; 138 | - (unsigned long long)unsignedLongLongValueForKey:(NSString *)key default:(unsigned long long)def; 139 | 140 | - (float)floatValueForKey:(NSString *)key default:(float)def; 141 | - (double)doubleValueForKey:(NSString *)key default:(double)def; 142 | 143 | - (NSInteger)integerValueForKey:(NSString *)key default:(NSInteger)def; 144 | - (NSUInteger)unsignedIntegerValueForKey:(NSString *)key default:(NSUInteger)def; 145 | 146 | - (nullable NSNumber *)numberValueForKey:(NSString *)key default:(nullable NSNumber *)def; 147 | - (nullable NSString *)stringValueForKey:(NSString *)key default:(nullable NSString *)def; 148 | 149 | @end 150 | 151 | 152 | 153 | /** 154 | Provide some some common method for `NSMutableDictionary`. 155 | */ 156 | @interface NSMutableDictionary (YYAdd) 157 | 158 | /** 159 | Creates and returns a dictionary from a specified property list data. 160 | 161 | @param plist A property list data whose root object is a dictionary. 162 | @return A new dictionary created from the plist data, or nil if an error occurs. 163 | 164 | @discussion Apple has implemented this method, but did not make it public. 165 | */ 166 | + (nullable NSMutableDictionary *)dictionaryWithPlistData:(NSData *)plist; 167 | 168 | /** 169 | Creates and returns a dictionary from a specified property list xml string. 170 | 171 | @param plist A property list xml string whose root object is a dictionary. 172 | @return A new dictionary created from the plist string, or nil if an error occurs. 173 | */ 174 | + (nullable NSMutableDictionary *)dictionaryWithPlistString:(NSString *)plist; 175 | 176 | 177 | /** 178 | Removes and returns the value associated with a given key. 179 | 180 | @param aKey The key for which to return and remove the corresponding value. 181 | @return The value associated with aKey, or nil if no value is associated with aKey. 182 | */ 183 | - (nullable id)popObjectForKey:(id)aKey; 184 | 185 | /** 186 | Returns a new dictionary containing the entries for keys, and remove these 187 | entries from reciever. If the keys is empty or nil, it just returns an 188 | empty dictionary. 189 | 190 | @param keys The keys. 191 | @return The entries for the keys. 192 | */ 193 | - (NSDictionary *)popEntriesForKeys:(NSArray *)keys; 194 | 195 | @end 196 | 197 | NS_ASSUME_NONNULL_END 198 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSKeyedUnarchiver+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSKeyedUnarchiver+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSKeyedUnarchiver+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSKeyedUnarchiver+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSNotificationCenter+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+YYAdd.h 3 | // YYCategories 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 synchronized; 25 | otherwise, is posted asynchronized. 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 synchronized; otherwise, is posted asynchronized. 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 synchronized; otherwise, is posted asynchronized. 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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSNotificationCenter+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSNumber+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSNumber+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | NSNumber *num = dic[str]; 39 | if (num != nil) { 40 | if (num == (id)[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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSObject+YYAddForARC.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYAddForARC.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSObject+YYAddForARC.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYAddForARC.m 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSObject+YYAddForKVO.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYAddForKVO.h 3 | // YYCategories 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 34 | block:(void (^)(id _Nonnull obj, id _Nonnull oldVal, id _Nonnull newVal))block; 35 | 36 | /** 37 | Stops all blocks (associated by `addObserverBlockForKeyPath:block:`) from 38 | receiving change notifications for the property specified by a given key-path 39 | relative to the receiver, and release these blocks. 40 | 41 | @param keyPath A key-path, relative to the receiver, for which blocks is 42 | registered to receive KVO change notifications. 43 | */ 44 | - (void)removeObserverBlocksForKeyPath:(NSString*)keyPath; 45 | 46 | /** 47 | Stops all blocks (associated by `addObserverBlockForKeyPath:block:`) from 48 | receiving change notifications, and release these blocks. 49 | */ 50 | - (void)removeObserverBlocks; 51 | 52 | @end 53 | 54 | NS_ASSUME_NONNULL_END 55 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSObject+YYAddForKVO.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYAddForKVO.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSThread+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSThread+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSThread+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSThread+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSTimer+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/Foundation/NSTimer+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/Quartz/CALayer+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/Quartz/CALayer+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+YYAdd.m 3 | // YYCategories 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 "CALayer+YYAdd.h" 13 | #import "YYCategoriesMacro.h" 14 | #import "YYCGUtilities.h" 15 | 16 | YYSYNTH_DUMMY_CLASS(CALayer_YYAdd) 17 | 18 | 19 | @implementation CALayer (YYAdd) 20 | 21 | - (UIImage *)snapshotImage { 22 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0); 23 | CGContextRef context = UIGraphicsGetCurrentContext(); 24 | [self renderInContext:context]; 25 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 26 | UIGraphicsEndImageContext(); 27 | return image; 28 | } 29 | 30 | - (NSData *)snapshotPDF { 31 | CGRect bounds = self.bounds; 32 | NSMutableData* data = [NSMutableData data]; 33 | CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData((__bridge CFMutableDataRef)data); 34 | CGContextRef context = CGPDFContextCreate(consumer, &bounds, NULL); 35 | CGDataConsumerRelease(consumer); 36 | if (!context) return nil; 37 | CGPDFContextBeginPage(context, NULL); 38 | CGContextTranslateCTM(context, 0, bounds.size.height); 39 | CGContextScaleCTM(context, 1.0, -1.0); 40 | [self renderInContext:context]; 41 | CGPDFContextEndPage(context); 42 | CGPDFContextClose(context); 43 | CGContextRelease(context); 44 | return data; 45 | } 46 | 47 | - (void)setLayerShadow:(UIColor*)color offset:(CGSize)offset radius:(CGFloat)radius { 48 | self.shadowColor = color.CGColor; 49 | self.shadowOffset = offset; 50 | self.shadowRadius = radius; 51 | self.shadowOpacity = 1; 52 | self.shouldRasterize = YES; 53 | self.rasterizationScale = [UIScreen mainScreen].scale; 54 | } 55 | 56 | - (void)removeAllSublayers { 57 | while (self.sublayers.count) { 58 | [self.sublayers.lastObject removeFromSuperlayer]; 59 | } 60 | } 61 | 62 | - (CGFloat)left { 63 | return self.frame.origin.x; 64 | } 65 | 66 | - (void)setLeft:(CGFloat)x { 67 | CGRect frame = self.frame; 68 | frame.origin.x = x; 69 | self.frame = frame; 70 | } 71 | 72 | - (CGFloat)top { 73 | return self.frame.origin.y; 74 | } 75 | 76 | - (void)setTop:(CGFloat)y { 77 | CGRect frame = self.frame; 78 | frame.origin.y = y; 79 | self.frame = frame; 80 | } 81 | 82 | - (CGFloat)right { 83 | return self.frame.origin.x + self.frame.size.width; 84 | } 85 | 86 | - (void)setRight:(CGFloat)right { 87 | CGRect frame = self.frame; 88 | frame.origin.x = right - frame.size.width; 89 | self.frame = frame; 90 | } 91 | 92 | - (CGFloat)bottom { 93 | return self.frame.origin.y + self.frame.size.height; 94 | } 95 | 96 | - (void)setBottom:(CGFloat)bottom { 97 | CGRect frame = self.frame; 98 | frame.origin.y = bottom - frame.size.height; 99 | self.frame = frame; 100 | } 101 | 102 | - (CGFloat)width { 103 | return self.frame.size.width; 104 | } 105 | 106 | - (void)setWidth:(CGFloat)width { 107 | CGRect frame = self.frame; 108 | frame.size.width = width; 109 | self.frame = frame; 110 | } 111 | 112 | - (CGFloat)height { 113 | return self.frame.size.height; 114 | } 115 | 116 | - (void)setHeight:(CGFloat)height { 117 | CGRect frame = self.frame; 118 | frame.size.height = height; 119 | self.frame = frame; 120 | } 121 | 122 | - (CGPoint)center { 123 | return CGPointMake(self.frame.origin.x + self.frame.size.width * 0.5, 124 | self.frame.origin.y + self.frame.size.height * 0.5); 125 | } 126 | 127 | - (void)setCenter:(CGPoint)center { 128 | CGRect frame = self.frame; 129 | frame.origin.x = center.x - frame.size.width * 0.5; 130 | frame.origin.y = center.y - frame.size.height * 0.5; 131 | self.frame = frame; 132 | } 133 | 134 | - (CGFloat)centerX { 135 | return self.frame.origin.x + self.frame.size.width * 0.5; 136 | } 137 | 138 | - (void)setCenterX:(CGFloat)centerX { 139 | CGRect frame = self.frame; 140 | frame.origin.x = centerX - frame.size.width * 0.5; 141 | self.frame = frame; 142 | } 143 | 144 | - (CGFloat)centerY { 145 | return self.frame.origin.y + self.frame.size.height * 0.5; 146 | } 147 | 148 | - (void)setCenterY:(CGFloat)centerY { 149 | CGRect frame = self.frame; 150 | frame.origin.y = centerY - frame.size.height * 0.5; 151 | self.frame = frame; 152 | } 153 | 154 | - (CGPoint)origin { 155 | return self.frame.origin; 156 | } 157 | 158 | - (void)setOrigin:(CGPoint)origin { 159 | CGRect frame = self.frame; 160 | frame.origin = origin; 161 | self.frame = frame; 162 | } 163 | 164 | - (CGSize)frameSize { 165 | return self.frame.size; 166 | } 167 | 168 | - (void)setFrameSize:(CGSize)size { 169 | CGRect frame = self.frame; 170 | frame.size = size; 171 | self.frame = frame; 172 | } 173 | 174 | - (CGFloat)transformRotation { 175 | NSNumber *v = [self valueForKeyPath:@"transform.rotation"]; 176 | return v.doubleValue; 177 | } 178 | 179 | - (void)setTransformRotation:(CGFloat)v { 180 | [self setValue:@(v) forKeyPath:@"transform.rotation"]; 181 | } 182 | 183 | - (CGFloat)transformRotationX { 184 | NSNumber *v = [self valueForKeyPath:@"transform.rotation.x"]; 185 | return v.doubleValue; 186 | } 187 | 188 | - (void)setTransformRotationX:(CGFloat)v { 189 | [self setValue:@(v) forKeyPath:@"transform.rotation.x"]; 190 | } 191 | 192 | - (CGFloat)transformRotationY { 193 | NSNumber *v = [self valueForKeyPath:@"transform.rotation.y"]; 194 | return v.doubleValue; 195 | } 196 | 197 | - (void)setTransformRotationY:(CGFloat)v { 198 | [self setValue:@(v) forKeyPath:@"transform.rotation.y"]; 199 | } 200 | 201 | - (CGFloat)transformRotationZ { 202 | NSNumber *v = [self valueForKeyPath:@"transform.rotation.z"]; 203 | return v.doubleValue; 204 | } 205 | 206 | - (void)setTransformRotationZ:(CGFloat)v { 207 | [self setValue:@(v) forKeyPath:@"transform.rotation.z"]; 208 | } 209 | 210 | - (CGFloat)transformScaleX { 211 | NSNumber *v = [self valueForKeyPath:@"transform.scale.x"]; 212 | return v.doubleValue; 213 | } 214 | 215 | - (void)setTransformScaleX:(CGFloat)v { 216 | [self setValue:@(v) forKeyPath:@"transform.scale.x"]; 217 | } 218 | 219 | - (CGFloat)transformScaleY { 220 | NSNumber *v = [self valueForKeyPath:@"transform.scale.y"]; 221 | return v.doubleValue; 222 | } 223 | 224 | - (void)setTransformScaleY:(CGFloat)v { 225 | [self setValue:@(v) forKeyPath:@"transform.scale.y"]; 226 | } 227 | 228 | - (CGFloat)transformScaleZ { 229 | NSNumber *v = [self valueForKeyPath:@"transform.scale.z"]; 230 | return v.doubleValue; 231 | } 232 | 233 | - (void)setTransformScaleZ:(CGFloat)v { 234 | [self setValue:@(v) forKeyPath:@"transform.scale.z"]; 235 | } 236 | 237 | - (CGFloat)transformScale { 238 | NSNumber *v = [self valueForKeyPath:@"transform.scale"]; 239 | return v.doubleValue; 240 | } 241 | 242 | - (void)setTransformScale:(CGFloat)v { 243 | [self setValue:@(v) forKeyPath:@"transform.scale"]; 244 | } 245 | 246 | - (CGFloat)transformTranslationX { 247 | NSNumber *v = [self valueForKeyPath:@"transform.translation.x"]; 248 | return v.doubleValue; 249 | } 250 | 251 | - (void)setTransformTranslationX:(CGFloat)v { 252 | [self setValue:@(v) forKeyPath:@"transform.translation.x"]; 253 | } 254 | 255 | - (CGFloat)transformTranslationY { 256 | NSNumber *v = [self valueForKeyPath:@"transform.translation.y"]; 257 | return v.doubleValue; 258 | } 259 | 260 | - (void)setTransformTranslationY:(CGFloat)v { 261 | [self setValue:@(v) forKeyPath:@"transform.translation.y"]; 262 | } 263 | 264 | - (CGFloat)transformTranslationZ { 265 | NSNumber *v = [self valueForKeyPath:@"transform.translation.z"]; 266 | return v.doubleValue; 267 | } 268 | 269 | - (void)setTransformTranslationZ:(CGFloat)v { 270 | [self setValue:@(v) forKeyPath:@"transform.translation.z"]; 271 | } 272 | 273 | - (CGFloat)transformDepth { 274 | return self.transform.m34; 275 | } 276 | 277 | - (void)setTransformDepth:(CGFloat)v { 278 | CATransform3D d = self.transform; 279 | d.m34 = v; 280 | self.transform = d; 281 | } 282 | 283 | - (UIViewContentMode)contentMode { 284 | return YYCAGravityToUIViewContentMode(self.contentsGravity); 285 | } 286 | 287 | - (void)setContentMode:(UIViewContentMode)contentMode { 288 | self.contentsGravity = YYUIViewContentModeToCAGravity(contentMode); 289 | } 290 | 291 | - (void)addFadeAnimationWithDuration:(NSTimeInterval)duration curve:(UIViewAnimationCurve)curve { 292 | if (duration <= 0) return; 293 | 294 | NSString *mediaFunction; 295 | switch (curve) { 296 | case UIViewAnimationCurveEaseInOut: { 297 | mediaFunction = kCAMediaTimingFunctionEaseInEaseOut; 298 | } break; 299 | case UIViewAnimationCurveEaseIn: { 300 | mediaFunction = kCAMediaTimingFunctionEaseIn; 301 | } break; 302 | case UIViewAnimationCurveEaseOut: { 303 | mediaFunction = kCAMediaTimingFunctionEaseOut; 304 | } break; 305 | case UIViewAnimationCurveLinear: { 306 | mediaFunction = kCAMediaTimingFunctionLinear; 307 | } break; 308 | default: { 309 | mediaFunction = kCAMediaTimingFunctionLinear; 310 | } break; 311 | } 312 | 313 | CATransition *transition = [CATransition animation]; 314 | transition.duration = duration; 315 | transition.timingFunction = [CAMediaTimingFunction functionWithName:mediaFunction]; 316 | transition.type = kCATransitionFade; 317 | [self addAnimation:transition forKey:@"yykit.fade"]; 318 | } 319 | 320 | - (void)removePreviousFadeAnimation { 321 | [self removeAnimationForKey:@"yykit.fade"]; 322 | } 323 | 324 | @end 325 | -------------------------------------------------------------------------------- /YYCategories/Quartz/YYCGUtilities.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYCGUtilities.m 3 | // YYCategories 4 | // 5 | // Created by ibireme on 15/2/28. 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 "YYCGUtilities.h" 13 | #import 14 | #import "UIView+YYAdd.h" 15 | 16 | CGContextRef YYCGContextCreateARGBBitmapContext(CGSize size, BOOL opaque, CGFloat scale) { 17 | size_t width = ceil(size.width * scale); 18 | size_t height = ceil(size.height * scale); 19 | if (width < 1 || height < 1) return NULL; 20 | 21 | //pre-multiplied ARGB, 8-bits per component 22 | CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); 23 | CGImageAlphaInfo alphaInfo = (opaque ? kCGImageAlphaNoneSkipFirst : kCGImageAlphaPremultipliedFirst); 24 | CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, space, kCGBitmapByteOrderDefault | alphaInfo); 25 | CGColorSpaceRelease(space); 26 | if (context) { 27 | CGContextTranslateCTM(context, 0, height); 28 | CGContextScaleCTM(context, scale, -scale); 29 | } 30 | return context; 31 | } 32 | 33 | CGContextRef YYCGContextCreateGrayBitmapContext(CGSize size, CGFloat scale) { 34 | size_t width = ceil(size.width * scale); 35 | size_t height = ceil(size.height * scale); 36 | if (width < 1 || height < 1) return NULL; 37 | 38 | //DeviceGray, 8-bits per component 39 | CGColorSpaceRef space = CGColorSpaceCreateDeviceGray(); 40 | CGImageAlphaInfo alphaInfo = kCGImageAlphaNone; 41 | CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, space, kCGBitmapByteOrderDefault | alphaInfo); 42 | CGColorSpaceRelease(space); 43 | if (context) { 44 | CGContextTranslateCTM(context, 0, height); 45 | CGContextScaleCTM(context, scale, -scale); 46 | } 47 | return context; 48 | } 49 | 50 | CGFloat YYScreenScale(void) { 51 | static CGFloat scale; 52 | static dispatch_once_t onceToken; 53 | dispatch_once(&onceToken, ^{ 54 | scale = [UIScreen mainScreen].scale; 55 | }); 56 | return scale; 57 | } 58 | 59 | CGSize YYScreenSize(void) { 60 | static CGSize size; 61 | static dispatch_once_t onceToken; 62 | dispatch_once(&onceToken, ^{ 63 | size = [UIScreen mainScreen].bounds.size; 64 | if (size.height < size.width) { 65 | CGFloat tmp = size.height; 66 | size.height = size.width; 67 | size.width = tmp; 68 | } 69 | }); 70 | return size; 71 | } 72 | 73 | // return 0 when succeed 74 | static int matrix_invert(__CLPK_integer N, double *matrix) { 75 | __CLPK_integer error = 0; 76 | __CLPK_integer pivot_tmp[6 * 6]; 77 | __CLPK_integer *pivot = pivot_tmp; 78 | double workspace_tmp[6 * 6]; 79 | double *workspace = workspace_tmp; 80 | bool need_free = false; 81 | 82 | if (N > 6) { 83 | need_free = true; 84 | pivot = malloc(N * N * sizeof(__CLPK_integer)); 85 | if (!pivot) return -1; 86 | workspace = malloc(N * sizeof(double)); 87 | if (!workspace) { 88 | free(pivot); 89 | return -1; 90 | } 91 | } 92 | 93 | dgetrf_(&N, &N, matrix, &N, pivot, &error); 94 | 95 | if (error == 0) { 96 | dgetri_(&N, matrix, &N, pivot, workspace, &N, &error); 97 | } 98 | 99 | if (need_free) { 100 | free(pivot); 101 | free(workspace); 102 | } 103 | return error; 104 | } 105 | 106 | CGAffineTransform YYCGAffineTransformGetFromPoints(CGPoint before[_Nullable 3], CGPoint after[_Nullable 3]) { 107 | if (before == NULL || after == NULL) return CGAffineTransformIdentity; 108 | 109 | CGPoint p1, p2, p3, q1, q2, q3; 110 | p1 = before[0]; p2 = before[1]; p3 = before[2]; 111 | q1 = after[0]; q2 = after[1]; q3 = after[2]; 112 | 113 | double A[36]; 114 | A[ 0] = p1.x; A[ 1] = p1.y; A[ 2] = 0; A[ 3] = 0; A[ 4] = 1; A[ 5] = 0; 115 | A[ 6] = 0; A[ 7] = 0; A[ 8] = p1.x; A[ 9] = p1.y; A[10] = 0; A[11] = 1; 116 | A[12] = p2.x; A[13] = p2.y; A[14] = 0; A[15] = 0; A[16] = 1; A[17] = 0; 117 | A[18] = 0; A[19] = 0; A[20] = p2.x; A[21] = p2.y; A[22] = 0; A[23] = 1; 118 | A[24] = p3.x; A[25] = p3.y; A[26] = 0; A[27] = 0; A[28] = 1; A[29] = 0; 119 | A[30] = 0; A[31] = 0; A[32] = p3.x; A[33] = p3.y; A[34] = 0; A[35] = 1; 120 | 121 | int error = matrix_invert(6, A); 122 | if (error) return CGAffineTransformIdentity; 123 | 124 | double B[6]; 125 | B[0] = q1.x; B[1] = q1.y; B[2] = q2.x; B[3] = q2.y; B[4] = q3.x; B[5] = q3.y; 126 | 127 | double M[6]; 128 | M[0] = A[ 0] * B[0] + A[ 1] * B[1] + A[ 2] * B[2] + A[ 3] * B[3] + A[ 4] * B[4] + A[ 5] * B[5]; 129 | M[1] = A[ 6] * B[0] + A[ 7] * B[1] + A[ 8] * B[2] + A[ 9] * B[3] + A[10] * B[4] + A[11] * B[5]; 130 | M[2] = A[12] * B[0] + A[13] * B[1] + A[14] * B[2] + A[15] * B[3] + A[16] * B[4] + A[17] * B[5]; 131 | M[3] = A[18] * B[0] + A[19] * B[1] + A[20] * B[2] + A[21] * B[3] + A[22] * B[4] + A[23] * B[5]; 132 | M[4] = A[24] * B[0] + A[25] * B[1] + A[26] * B[2] + A[27] * B[3] + A[28] * B[4] + A[29] * B[5]; 133 | M[5] = A[30] * B[0] + A[31] * B[1] + A[32] * B[2] + A[33] * B[3] + A[34] * B[4] + A[35] * B[5]; 134 | 135 | CGAffineTransform transform = CGAffineTransformMake(M[0], M[2], M[1], M[3], M[4], M[5]); 136 | return transform; 137 | } 138 | 139 | CGAffineTransform YYCGAffineTransformGetFromViews(UIView *from, UIView *to) { 140 | if (!from || !to) return CGAffineTransformIdentity; 141 | 142 | CGPoint before[3], after[3]; 143 | before[0] = CGPointMake(0, 0); 144 | before[1] = CGPointMake(0, 1); 145 | before[2] = CGPointMake(1, 0); 146 | after[0] = [from convertPoint:before[0] toViewOrWindow:to]; 147 | after[1] = [from convertPoint:before[1] toViewOrWindow:to]; 148 | after[2] = [from convertPoint:before[2] toViewOrWindow:to]; 149 | 150 | return YYCGAffineTransformGetFromPoints(before, after); 151 | } 152 | 153 | UIViewContentMode YYCAGravityToUIViewContentMode(NSString *gravity) { 154 | static NSDictionary *dic; 155 | static dispatch_once_t onceToken; 156 | dispatch_once(&onceToken, ^{ 157 | dic = @{ kCAGravityCenter:@(UIViewContentModeCenter), 158 | kCAGravityTop:@(UIViewContentModeTop), 159 | kCAGravityBottom:@(UIViewContentModeBottom), 160 | kCAGravityLeft:@(UIViewContentModeLeft), 161 | kCAGravityRight:@(UIViewContentModeRight), 162 | kCAGravityTopLeft:@(UIViewContentModeTopLeft), 163 | kCAGravityTopRight:@(UIViewContentModeTopRight), 164 | kCAGravityBottomLeft:@(UIViewContentModeBottomLeft), 165 | kCAGravityBottomRight:@(UIViewContentModeBottomRight), 166 | kCAGravityResize:@(UIViewContentModeScaleToFill), 167 | kCAGravityResizeAspect:@(UIViewContentModeScaleAspectFit), 168 | kCAGravityResizeAspectFill:@(UIViewContentModeScaleAspectFill) }; 169 | }); 170 | if (!gravity) return UIViewContentModeScaleToFill; 171 | return (UIViewContentMode)((NSNumber *)dic[gravity]).integerValue; 172 | } 173 | 174 | NSString *YYUIViewContentModeToCAGravity(UIViewContentMode contentMode) { 175 | switch (contentMode) { 176 | case UIViewContentModeScaleToFill: return kCAGravityResize; 177 | case UIViewContentModeScaleAspectFit: return kCAGravityResizeAspect; 178 | case UIViewContentModeScaleAspectFill: return kCAGravityResizeAspectFill; 179 | case UIViewContentModeRedraw: return kCAGravityResize; 180 | case UIViewContentModeCenter: return kCAGravityCenter; 181 | case UIViewContentModeTop: return kCAGravityTop; 182 | case UIViewContentModeBottom: return kCAGravityBottom; 183 | case UIViewContentModeLeft: return kCAGravityLeft; 184 | case UIViewContentModeRight: return kCAGravityRight; 185 | case UIViewContentModeTopLeft: return kCAGravityTopLeft; 186 | case UIViewContentModeTopRight: return kCAGravityTopRight; 187 | case UIViewContentModeBottomLeft: return kCAGravityBottomLeft; 188 | case UIViewContentModeBottomRight: return kCAGravityBottomRight; 189 | default: return kCAGravityResize; 190 | } 191 | } 192 | 193 | CGRect YYCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode) { 194 | rect = CGRectStandardize(rect); 195 | size.width = size.width < 0 ? -size.width : size.width; 196 | size.height = size.height < 0 ? -size.height : size.height; 197 | CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); 198 | switch (mode) { 199 | case UIViewContentModeScaleAspectFit: 200 | case UIViewContentModeScaleAspectFill: { 201 | if (rect.size.width < 0.01 || rect.size.height < 0.01 || 202 | size.width < 0.01 || size.height < 0.01) { 203 | rect.origin = center; 204 | rect.size = CGSizeZero; 205 | } else { 206 | CGFloat scale; 207 | if (mode == UIViewContentModeScaleAspectFit) { 208 | if (size.width / size.height < rect.size.width / rect.size.height) { 209 | scale = rect.size.height / size.height; 210 | } else { 211 | scale = rect.size.width / size.width; 212 | } 213 | } else { 214 | if (size.width / size.height < rect.size.width / rect.size.height) { 215 | scale = rect.size.width / size.width; 216 | } else { 217 | scale = rect.size.height / size.height; 218 | } 219 | } 220 | size.width *= scale; 221 | size.height *= scale; 222 | rect.size = size; 223 | rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5); 224 | } 225 | } break; 226 | case UIViewContentModeCenter: { 227 | rect.size = size; 228 | rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5); 229 | } break; 230 | case UIViewContentModeTop: { 231 | rect.origin.x = center.x - size.width * 0.5; 232 | rect.size = size; 233 | } break; 234 | case UIViewContentModeBottom: { 235 | rect.origin.x = center.x - size.width * 0.5; 236 | rect.origin.y += rect.size.height - size.height; 237 | rect.size = size; 238 | } break; 239 | case UIViewContentModeLeft: { 240 | rect.origin.y = center.y - size.height * 0.5; 241 | rect.size = size; 242 | } break; 243 | case UIViewContentModeRight: { 244 | rect.origin.y = center.y - size.height * 0.5; 245 | rect.origin.x += rect.size.width - size.width; 246 | rect.size = size; 247 | } break; 248 | case UIViewContentModeTopLeft: { 249 | rect.size = size; 250 | } break; 251 | case UIViewContentModeTopRight: { 252 | rect.origin.x += rect.size.width - size.width; 253 | rect.size = size; 254 | } break; 255 | case UIViewContentModeBottomLeft: { 256 | rect.origin.y += rect.size.height - size.height; 257 | rect.size = size; 258 | } break; 259 | case UIViewContentModeBottomRight: { 260 | rect.origin.x += rect.size.width - size.width; 261 | rect.origin.y += rect.size.height - size.height; 262 | rect.size = size; 263 | } break; 264 | case UIViewContentModeScaleToFill: 265 | case UIViewContentModeRedraw: 266 | default: { 267 | rect = rect; 268 | } 269 | } 270 | return rect; 271 | } 272 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIApplication+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication+YYAdd.h 3 | // YYCategories 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 priated (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 | - (void)incrementNetworkActivityCount; 66 | 67 | /** 68 | Decrements the number of active network requests. 69 | If this number becomes zero after decrementing, this will stop animating the 70 | status bar network activity indicator. 71 | 72 | This method is thread safe. 73 | */ 74 | - (void)decrementNetworkActivityCount; 75 | 76 | 77 | /// Returns YES in App Extension. 78 | + (BOOL)isAppExtension; 79 | 80 | /// Same as sharedApplication, but returns nil in App Extension. 81 | + (nullable UIApplication *)sharedExtensionApplication; 82 | 83 | @end 84 | 85 | NS_ASSUME_NONNULL_END 86 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIApplication+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication+YYAdd.m 3 | // YYCategories 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 "UIApplication+YYAdd.h" 13 | #import "NSArray+YYAdd.h" 14 | #import "NSObject+YYAdd.h" 15 | #import "YYCategoriesMacro.h" 16 | #import "UIDevice+YYAdd.h" 17 | #import 18 | #import 19 | #import 20 | 21 | YYSYNTH_DUMMY_CLASS(UIApplication_YYAdd) 22 | 23 | #define kNetworkIndicatorDelay (1/30.0) 24 | @interface _YYUIApplicationNetworkIndicatorInfo : NSObject 25 | @property (nonatomic, assign) NSInteger count; 26 | @property (nonatomic, strong) NSTimer *timer; 27 | @end 28 | 29 | @implementation _YYUIApplicationNetworkIndicatorInfo 30 | @end 31 | 32 | 33 | @implementation UIApplication (YYAdd) 34 | 35 | - (NSURL *)documentsURL { 36 | return [[[NSFileManager defaultManager] 37 | URLsForDirectory:NSDocumentDirectory 38 | inDomains:NSUserDomainMask] lastObject]; 39 | } 40 | 41 | - (NSString *)documentsPath { 42 | return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; 43 | } 44 | 45 | - (NSURL *)cachesURL { 46 | return [[[NSFileManager defaultManager] 47 | URLsForDirectory:NSCachesDirectory 48 | inDomains:NSUserDomainMask] lastObject]; 49 | } 50 | 51 | - (NSString *)cachesPath { 52 | return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; 53 | } 54 | 55 | - (NSURL *)libraryURL { 56 | return [[[NSFileManager defaultManager] 57 | URLsForDirectory:NSLibraryDirectory 58 | inDomains:NSUserDomainMask] lastObject]; 59 | } 60 | 61 | - (NSString *)libraryPath { 62 | return [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject]; 63 | } 64 | 65 | - (BOOL)isPirated { 66 | if ([[UIDevice currentDevice] isSimulator]) return YES; // Simulator is not from appstore 67 | 68 | if (getgid() <= 10) return YES; // process ID shouldn't be root 69 | 70 | if ([[[NSBundle mainBundle] infoDictionary] objectForKey:@"SignerIdentity"]) { 71 | return YES; 72 | } 73 | 74 | if (![self _yy_fileExistInMainBundle:@"_CodeSignature"]) { 75 | return YES; 76 | } 77 | 78 | if (![self _yy_fileExistInMainBundle:@"SC_Info"]) { 79 | return YES; 80 | } 81 | 82 | //if someone really want to crack your app, this method is useless.. 83 | //you may change this method's name, encrypt the code and do more check.. 84 | return NO; 85 | } 86 | 87 | - (BOOL)_yy_fileExistInMainBundle:(NSString *)name { 88 | NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; 89 | NSString *path = [NSString stringWithFormat:@"%@/%@", bundlePath, name]; 90 | return [[NSFileManager defaultManager] fileExistsAtPath:path]; 91 | } 92 | 93 | - (NSString *)appBundleName { 94 | return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; 95 | } 96 | 97 | - (NSString *)appBundleID { 98 | return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; 99 | } 100 | 101 | - (NSString *)appVersion { 102 | return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; 103 | } 104 | 105 | - (NSString *)appBuildVersion { 106 | return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; 107 | } 108 | 109 | - (BOOL)isBeingDebugged { 110 | size_t size = sizeof(struct kinfo_proc); 111 | struct kinfo_proc info; 112 | int ret = 0, name[4]; 113 | memset(&info, 0, sizeof(struct kinfo_proc)); 114 | 115 | name[0] = CTL_KERN; 116 | name[1] = KERN_PROC; 117 | name[2] = KERN_PROC_PID; name[3] = getpid(); 118 | 119 | if (ret == (sysctl(name, 4, &info, &size, NULL, 0))) { 120 | return ret != 0; 121 | } 122 | return (info.kp_proc.p_flag & P_TRACED) ? YES : NO; 123 | } 124 | 125 | - (int64_t)memoryUsage { 126 | struct task_basic_info info; 127 | mach_msg_type_number_t size = sizeof(info); 128 | kern_return_t kern = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size); 129 | if (kern != KERN_SUCCESS) return -1; 130 | return info.resident_size; 131 | } 132 | 133 | - (float)cpuUsage { 134 | kern_return_t kr; 135 | task_info_data_t tinfo; 136 | mach_msg_type_number_t task_info_count; 137 | 138 | task_info_count = TASK_INFO_MAX; 139 | kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)tinfo, &task_info_count); 140 | if (kr != KERN_SUCCESS) { 141 | return -1; 142 | } 143 | 144 | thread_array_t thread_list; 145 | mach_msg_type_number_t thread_count; 146 | 147 | thread_info_data_t thinfo; 148 | mach_msg_type_number_t thread_info_count; 149 | 150 | thread_basic_info_t basic_info_th; 151 | 152 | kr = task_threads(mach_task_self(), &thread_list, &thread_count); 153 | if (kr != KERN_SUCCESS) { 154 | return -1; 155 | } 156 | 157 | long tot_sec = 0; 158 | long tot_usec = 0; 159 | float tot_cpu = 0; 160 | int j; 161 | 162 | for (j = 0; j < thread_count; j++) { 163 | thread_info_count = THREAD_INFO_MAX; 164 | kr = thread_info(thread_list[j], THREAD_BASIC_INFO, 165 | (thread_info_t)thinfo, &thread_info_count); 166 | if (kr != KERN_SUCCESS) { 167 | return -1; 168 | } 169 | 170 | basic_info_th = (thread_basic_info_t)thinfo; 171 | 172 | if (!(basic_info_th->flags & TH_FLAGS_IDLE)) { 173 | tot_sec = tot_sec + basic_info_th->user_time.seconds + basic_info_th->system_time.seconds; 174 | tot_usec = tot_usec + basic_info_th->system_time.microseconds + basic_info_th->system_time.microseconds; 175 | tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float)TH_USAGE_SCALE; 176 | } 177 | } 178 | 179 | kr = vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t)); 180 | assert(kr == KERN_SUCCESS); 181 | 182 | return tot_cpu; 183 | } 184 | 185 | YYSYNTH_DYNAMIC_PROPERTY_OBJECT(networkActivityInfo, setNetworkActivityInfo, RETAIN_NONATOMIC, _YYUIApplicationNetworkIndicatorInfo *); 186 | 187 | - (void)_delaySetActivity:(NSTimer *)timer { 188 | NSNumber *visiable = timer.userInfo; 189 | if (self.networkActivityIndicatorVisible != visiable.boolValue) { 190 | [self setNetworkActivityIndicatorVisible:visiable.boolValue]; 191 | } 192 | [timer invalidate]; 193 | } 194 | 195 | - (void)_changeNetworkActivityCount:(NSInteger)delta { 196 | @synchronized(self){ 197 | dispatch_async_on_main_queue(^{ 198 | _YYUIApplicationNetworkIndicatorInfo *info = [self networkActivityInfo]; 199 | if (!info) { 200 | info = [_YYUIApplicationNetworkIndicatorInfo new]; 201 | [self setNetworkActivityInfo:info]; 202 | } 203 | NSInteger count = info.count; 204 | count += delta; 205 | info.count = count; 206 | [info.timer invalidate]; 207 | info.timer = [NSTimer timerWithTimeInterval:kNetworkIndicatorDelay target:self selector:@selector(_delaySetActivity:) userInfo:@(info.count > 0) repeats:NO]; 208 | [[NSRunLoop mainRunLoop] addTimer:info.timer forMode:NSRunLoopCommonModes]; 209 | }); 210 | } 211 | } 212 | 213 | - (void)incrementNetworkActivityCount { 214 | [self _changeNetworkActivityCount:1]; 215 | } 216 | 217 | - (void)decrementNetworkActivityCount { 218 | [self _changeNetworkActivityCount:-1]; 219 | } 220 | 221 | + (BOOL)isAppExtension { 222 | static BOOL isAppExtension = NO; 223 | static dispatch_once_t onceToken; 224 | dispatch_once(&onceToken, ^{ 225 | Class cls = NSClassFromString(@"UIApplication"); 226 | if(!cls || ![cls respondsToSelector:@selector(sharedApplication)]) isAppExtension = YES; 227 | if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) isAppExtension = YES; 228 | }); 229 | return isAppExtension; 230 | } 231 | 232 | + (UIApplication *)sharedExtensionApplication { 233 | #pragma clang diagnostic push 234 | #pragma clang diagnostic ignored "-Wundeclared-selector" 235 | return [self isAppExtension] ? nil : [UIApplication performSelector:@selector(sharedApplication)]; 236 | #pragma clang diagnostic pop 237 | } 238 | 239 | @end 240 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIBarButtonItem+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItem+YYAdd.h 3 | // YYCategories 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 33 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIBarButtonItem+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItem+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIBezierPath+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIBezierPath+YYAdd.h 3 | // YYCategories 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 38 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIBezierPath+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIBezierPath+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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) 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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIControl+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIControl+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIDevice+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIDevice+YYAdd.h 3 | // YYCategories 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 `UIDevice`. 18 | */ 19 | @interface UIDevice (YYAdd) 20 | 21 | 22 | #pragma mark - Device Information 23 | ///============================================================================= 24 | /// @name Device Information 25 | ///============================================================================= 26 | 27 | /// Device system version (e.g. 8.1) 28 | + (double)systemVersion; 29 | 30 | /// Whether the device is iPad/iPad mini. 31 | @property (nonatomic, readonly) BOOL isPad; 32 | 33 | /// Whether the device is a simulator. 34 | @property (nonatomic, readonly) BOOL isSimulator; 35 | 36 | /// Whether the device is jailbroken. 37 | @property (nonatomic, readonly) BOOL isJailbroken; 38 | 39 | /// Wherher the device can make phone calls. 40 | @property (nonatomic, readonly) BOOL canMakePhoneCalls NS_EXTENSION_UNAVAILABLE_IOS(""); 41 | 42 | /// The device's machine model. e.g. "iPhone6,1" "iPad4,6" 43 | /// @see http://theiphonewiki.com/wiki/Models 44 | @property (nullable, nonatomic, readonly) NSString *machineModel; 45 | 46 | /// The device's machine model name. e.g. "iPhone 5s" "iPad mini 2" 47 | /// @see http://theiphonewiki.com/wiki/Models 48 | @property (nullable, nonatomic, readonly) NSString *machineModelName; 49 | 50 | /// The System's startup time. 51 | @property (nonatomic, readonly) NSDate *systemUptime; 52 | 53 | 54 | #pragma mark - Network Information 55 | ///============================================================================= 56 | /// @name Network Information 57 | ///============================================================================= 58 | 59 | /// WIFI IP address of this device (can be nil). e.g. @"192.168.1.111" 60 | @property (nullable, nonatomic, readonly) NSString *ipAddressWIFI; 61 | 62 | /// Cell IP address of this device (can be nil). e.g. @"10.2.2.222" 63 | @property (nullable, nonatomic, readonly) NSString *ipAddressCell; 64 | 65 | 66 | /** 67 | Network traffic type: 68 | 69 | WWAN: Wireless Wide Area Network. 70 | For example: 3G/4G. 71 | 72 | WIFI: Wi-Fi. 73 | 74 | AWDL: Apple Wireless Direct Link (peer-to-peer connection). 75 | For exmaple: AirDrop, AirPlay, GameKit. 76 | */ 77 | typedef NS_OPTIONS(NSUInteger, YYNetworkTrafficType) { 78 | YYNetworkTrafficTypeWWANSent = 1 << 0, 79 | YYNetworkTrafficTypeWWANReceived = 1 << 1, 80 | YYNetworkTrafficTypeWIFISent = 1 << 2, 81 | YYNetworkTrafficTypeWIFIReceived = 1 << 3, 82 | YYNetworkTrafficTypeAWDLSent = 1 << 4, 83 | YYNetworkTrafficTypeAWDLReceived = 1 << 5, 84 | 85 | YYNetworkTrafficTypeWWAN = YYNetworkTrafficTypeWWANSent | YYNetworkTrafficTypeWWANReceived, 86 | YYNetworkTrafficTypeWIFI = YYNetworkTrafficTypeWIFISent | YYNetworkTrafficTypeWIFIReceived, 87 | YYNetworkTrafficTypeAWDL = YYNetworkTrafficTypeAWDLSent | YYNetworkTrafficTypeAWDLReceived, 88 | 89 | YYNetworkTrafficTypeALL = YYNetworkTrafficTypeWWAN | 90 | YYNetworkTrafficTypeWIFI | 91 | YYNetworkTrafficTypeAWDL, 92 | }; 93 | 94 | /** 95 | Get device network traffic bytes. 96 | 97 | @discussion This is a counter since the device's last boot time. 98 | Usage: 99 | 100 | uint64_t bytes = [[UIDevice currentDevice] getNetworkTrafficBytes:YYNetworkTrafficTypeALL]; 101 | NSTimeInterval time = CACurrentMediaTime(); 102 | 103 | uint64_t bytesPerSecond = (bytes - _lastBytes) / (time - _lastTime); 104 | 105 | _lastBytes = bytes; 106 | _lastTime = time; 107 | 108 | 109 | @param types traffic types 110 | @return bytes counter. 111 | */ 112 | - (uint64_t)getNetworkTrafficBytes:(YYNetworkTrafficType)types; 113 | 114 | #pragma mark - Disk Space 115 | ///============================================================================= 116 | /// @name Disk Space 117 | ///============================================================================= 118 | 119 | /// Total disk space in byte. (-1 when error occurs) 120 | @property (nonatomic, readonly) int64_t diskSpace; 121 | 122 | /// Free disk space in byte. (-1 when error occurs) 123 | @property (nonatomic, readonly) int64_t diskSpaceFree; 124 | 125 | /// Used disk space in byte. (-1 when error occurs) 126 | @property (nonatomic, readonly) int64_t diskSpaceUsed; 127 | 128 | 129 | #pragma mark - Memory Information 130 | ///============================================================================= 131 | /// @name Memory Information 132 | ///============================================================================= 133 | 134 | /// Total physical memory in byte. (-1 when error occurs) 135 | @property (nonatomic, readonly) int64_t memoryTotal; 136 | 137 | /// Used (active + inactive + wired) memory in byte. (-1 when error occurs) 138 | @property (nonatomic, readonly) int64_t memoryUsed; 139 | 140 | /// Free memory in byte. (-1 when error occurs) 141 | @property (nonatomic, readonly) int64_t memoryFree; 142 | 143 | /// Acvite memory in byte. (-1 when error occurs) 144 | @property (nonatomic, readonly) int64_t memoryActive; 145 | 146 | /// Inactive memory in byte. (-1 when error occurs) 147 | @property (nonatomic, readonly) int64_t memoryInactive; 148 | 149 | /// Wired memory in byte. (-1 when error occurs) 150 | @property (nonatomic, readonly) int64_t memoryWired; 151 | 152 | /// Purgable memory in byte. (-1 when error occurs) 153 | @property (nonatomic, readonly) int64_t memoryPurgable; 154 | 155 | #pragma mark - CPU Information 156 | ///============================================================================= 157 | /// @name CPU Information 158 | ///============================================================================= 159 | 160 | /// Avaliable CPU processor count. 161 | @property (nonatomic, readonly) NSUInteger cpuCount; 162 | 163 | /// Current CPU usage, 1.0 means 100%. (-1 when error occurs) 164 | @property (nonatomic, readonly) float cpuUsage; 165 | 166 | /// Current CPU usage per processor (array of NSNumber), 1.0 means 100%. (nil when error occurs) 167 | @property (nullable, nonatomic, readonly) NSArray *cpuUsagePerProcessor; 168 | 169 | @end 170 | 171 | NS_ASSUME_NONNULL_END 172 | 173 | #ifndef kSystemVersion 174 | #define kSystemVersion [UIDevice systemVersion] 175 | #endif 176 | 177 | #ifndef kiOS6Later 178 | #define kiOS6Later (kSystemVersion >= 6) 179 | #endif 180 | 181 | #ifndef kiOS7Later 182 | #define kiOS7Later (kSystemVersion >= 7) 183 | #endif 184 | 185 | #ifndef kiOS8Later 186 | #define kiOS8Later (kSystemVersion >= 8) 187 | #endif 188 | 189 | #ifndef kiOS9Later 190 | #define kiOS9Later (kSystemVersion >= 9) 191 | #endif 192 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIFont+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIFont+YYAdd.h 3 | // YYCategories 4 | // 5 | // Created by ibireme on 14/5/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 | #import 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | /** 19 | Provides extensions for `UIFont`. 20 | */ 21 | @interface UIFont (YYAdd) 22 | 23 | #pragma mark - Font Traits 24 | ///============================================================================= 25 | /// @name Font Traits 26 | ///============================================================================= 27 | 28 | @property (nonatomic, readonly) BOOL isBold NS_AVAILABLE_IOS(7_0); ///< Whether the font is bold. 29 | @property (nonatomic, readonly) BOOL isItalic NS_AVAILABLE_IOS(7_0); ///< Whether the font is italic. 30 | @property (nonatomic, readonly) BOOL isMonoSpace NS_AVAILABLE_IOS(7_0); ///< Whether the font is mono space. 31 | @property (nonatomic, readonly) BOOL isColorGlyphs NS_AVAILABLE_IOS(7_0); ///< Whether the font is color glyphs (such as Emoji). 32 | @property (nonatomic, readonly) CGFloat fontWeight NS_AVAILABLE_IOS(7_0); ///< Font weight from -1.0 to 1.0. Regular weight is 0.0. 33 | 34 | /** 35 | Create a bold font from receiver. 36 | @return A bold font, or nil if failed. 37 | */ 38 | - (nullable UIFont *)fontWithBold NS_AVAILABLE_IOS(7_0); 39 | 40 | /** 41 | Create a italic font from receiver. 42 | @return A italic font, or nil if failed. 43 | */ 44 | - (nullable UIFont *)fontWithItalic NS_AVAILABLE_IOS(7_0); 45 | 46 | /** 47 | Create a bold and italic font from receiver. 48 | @return A bold and italic font, or nil if failed. 49 | */ 50 | - (nullable UIFont *)fontWithBoldItalic NS_AVAILABLE_IOS(7_0); 51 | 52 | /** 53 | Create a normal (no bold/italic/...) font from receiver. 54 | @return A normal font, or nil if failed. 55 | */ 56 | - (nullable UIFont *)fontWithNormal NS_AVAILABLE_IOS(7_0); 57 | 58 | #pragma mark - Create font 59 | ///============================================================================= 60 | /// @name Create font 61 | ///============================================================================= 62 | 63 | /** 64 | Creates and returns a font object for the specified CTFontRef. 65 | 66 | @param CTFont CoreText font. 67 | */ 68 | + (nullable UIFont *)fontWithCTFont:(CTFontRef)CTFont; 69 | 70 | /** 71 | Creates and returns a font object for the specified CGFontRef and size. 72 | 73 | @param CGFont CoreGraphic font. 74 | @param size Font size. 75 | */ 76 | + (nullable UIFont *)fontWithCGFont:(CGFontRef)CGFont size:(CGFloat)size; 77 | 78 | /** 79 | Creates and returns the CTFontRef object. (need call CFRelease() after used) 80 | */ 81 | - (nullable CTFontRef)CTFontRef CF_RETURNS_RETAINED; 82 | 83 | /** 84 | Creates and returns the CGFontRef object. (need call CFRelease() after used) 85 | */ 86 | - (nullable CGFontRef)CGFontRef CF_RETURNS_RETAINED; 87 | 88 | 89 | #pragma mark - Load and unload font 90 | ///============================================================================= 91 | /// @name Load and unload font 92 | ///============================================================================= 93 | 94 | /** 95 | Load the font from file path. Support format:TTF,OTF. 96 | If return YES, font can be load use it PostScript Name: [UIFont fontWithName:...] 97 | 98 | @param path font file's full path 99 | */ 100 | + (BOOL)loadFontFromPath:(NSString *)path; 101 | 102 | /** 103 | Unload font from file path. 104 | 105 | @param path font file's full path 106 | */ 107 | + (void)unloadFontFromPath:(NSString *)path; 108 | 109 | /** 110 | Load the font from data. Support format:TTF,OTF. 111 | 112 | @param data Font data. 113 | 114 | @return UIFont object if load succeed, otherwise nil. 115 | */ 116 | + (nullable UIFont *)loadFontFromData:(NSData *)data; 117 | 118 | /** 119 | Unload font which is loaded by loadFontFromData: function. 120 | 121 | @param font the font loaded by loadFontFromData: function 122 | 123 | @return YES if succeed, otherwise NO. 124 | */ 125 | + (BOOL)unloadFontFromData:(UIFont *)font; 126 | 127 | 128 | #pragma mark - Dump font data 129 | ///============================================================================= 130 | /// @name Dump font data 131 | ///============================================================================= 132 | 133 | /** 134 | Serialize and return the font data. 135 | 136 | @param font The font. 137 | 138 | @return data in TTF, or nil if an error occurs. 139 | */ 140 | + (nullable NSData *)dataFromFont:(UIFont *)font; 141 | 142 | /** 143 | Serialize and return the font data. 144 | 145 | @param cgFont The font. 146 | 147 | @return data in TTF, or nil if an error occurs. 148 | */ 149 | + (nullable NSData *)dataFromCGFont:(CGFontRef)cgFont; 150 | 151 | @end 152 | 153 | NS_ASSUME_NONNULL_END 154 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIFont+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIFont+YYAdd.m 3 | // YYCategories 4 | // 5 | // Created by ibireme on 14/5/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 "UIFont+YYAdd.h" 13 | #import "YYCategoriesMacro.h" 14 | 15 | YYSYNTH_DUMMY_CLASS(UIFont_YYAdd) 16 | 17 | 18 | #pragma clang diagnostic push 19 | #pragma clang diagnostic ignored "-Wprotocol" 20 | // Apple has implemented UIFont, but did not make it public. 21 | 22 | @implementation UIFont (YYAdd) 23 | 24 | - (BOOL)isBold { 25 | if (![self respondsToSelector:@selector(fontDescriptor)]) return NO; 26 | return (self.fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) > 0; 27 | } 28 | 29 | - (BOOL)isItalic { 30 | if (![self respondsToSelector:@selector(fontDescriptor)]) return NO; 31 | return (self.fontDescriptor.symbolicTraits & UIFontDescriptorTraitItalic) > 0; 32 | } 33 | 34 | - (BOOL)isMonoSpace { 35 | if (![self respondsToSelector:@selector(fontDescriptor)]) return NO; 36 | return (self.fontDescriptor.symbolicTraits & UIFontDescriptorTraitMonoSpace) > 0; 37 | } 38 | 39 | - (BOOL)isColorGlyphs { 40 | if (![self respondsToSelector:@selector(fontDescriptor)]) return NO; 41 | return (CTFontGetSymbolicTraits((__bridge CTFontRef)self) & kCTFontTraitColorGlyphs) != 0; 42 | } 43 | 44 | - (CGFloat)fontWeight { 45 | NSDictionary *traits = [self.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute]; 46 | return [traits[UIFontWeightTrait] floatValue]; 47 | } 48 | 49 | - (UIFont *)fontWithBold { 50 | if (![self respondsToSelector:@selector(fontDescriptor)]) return nil; 51 | return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold] size:self.pointSize]; 52 | } 53 | 54 | - (UIFont *)fontWithItalic { 55 | if (![self respondsToSelector:@selector(fontDescriptor)]) return nil; 56 | return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic] size:self.pointSize]; 57 | } 58 | 59 | - (UIFont *)fontWithBoldItalic { 60 | if (![self respondsToSelector:@selector(fontDescriptor)]) return nil; 61 | return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic] size:self.pointSize]; 62 | } 63 | 64 | - (UIFont *)fontWithNormal { 65 | if (![self respondsToSelector:@selector(fontDescriptor)]) return nil; 66 | return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:0] size:self.pointSize]; 67 | } 68 | 69 | + (UIFont *)fontWithCTFont:(CTFontRef)CTFont { 70 | if (!CTFont) return nil; 71 | CFStringRef name = CTFontCopyPostScriptName(CTFont); 72 | if (!name) return nil; 73 | CGFloat size = CTFontGetSize(CTFont); 74 | UIFont *font = [self fontWithName:(__bridge NSString *)(name) size:size]; 75 | CFRelease(name); 76 | return font; 77 | } 78 | 79 | + (UIFont *)fontWithCGFont:(CGFontRef)CGFont size:(CGFloat)size { 80 | if (!CGFont) return nil; 81 | CFStringRef name = CGFontCopyPostScriptName(CGFont); 82 | if (!name) return nil; 83 | UIFont *font = [self fontWithName:(__bridge NSString *)(name) size:size]; 84 | CFRelease(name); 85 | return font; 86 | } 87 | 88 | - (CTFontRef)CTFontRef CF_RETURNS_RETAINED { 89 | CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)self.fontName, self.pointSize, NULL); 90 | return font; 91 | } 92 | 93 | - (CGFontRef)CGFontRef CF_RETURNS_RETAINED { 94 | CGFontRef font = CGFontCreateWithFontName((__bridge CFStringRef)self.fontName); 95 | return font; 96 | } 97 | 98 | + (BOOL)loadFontFromPath:(NSString *)path { 99 | NSURL *url = [NSURL fileURLWithPath:path]; 100 | CFErrorRef error; 101 | BOOL suc = CTFontManagerRegisterFontsForURL((__bridge CFTypeRef)url, kCTFontManagerScopeNone, &error); 102 | if (!suc) { 103 | NSLog(@"Failed to load font: %@", error); 104 | } 105 | return suc; 106 | } 107 | 108 | + (void)unloadFontFromPath:(NSString *)path { 109 | NSURL *url = [NSURL fileURLWithPath:path]; 110 | CTFontManagerUnregisterFontsForURL((__bridge CFTypeRef)url, kCTFontManagerScopeNone, NULL); 111 | } 112 | 113 | + (UIFont *)loadFontFromData:(NSData *)data { 114 | CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); 115 | if (!provider) return nil; 116 | CGFontRef fontRef = CGFontCreateWithDataProvider(provider); 117 | CGDataProviderRelease(provider); 118 | if (!fontRef) return nil; 119 | 120 | CFErrorRef errorRef; 121 | BOOL suc = CTFontManagerRegisterGraphicsFont(fontRef, &errorRef); 122 | if (!suc) { 123 | CFRelease(fontRef); 124 | NSLog(@"%@", errorRef); 125 | return nil; 126 | } else { 127 | CFStringRef fontName = CGFontCopyPostScriptName(fontRef); 128 | UIFont *font = [UIFont fontWithName:(__bridge NSString *)(fontName) size:[UIFont systemFontSize]]; 129 | if (fontName) CFRelease(fontName); 130 | CGFontRelease(fontRef); 131 | return font; 132 | } 133 | } 134 | 135 | + (BOOL)unloadFontFromData:(UIFont *)font { 136 | CGFontRef fontRef = CGFontCreateWithFontName((__bridge CFStringRef)font.fontName); 137 | if (!fontRef) return NO; 138 | CFErrorRef errorRef; 139 | BOOL suc = CTFontManagerUnregisterGraphicsFont(fontRef, &errorRef); 140 | CFRelease(fontRef); 141 | if (!suc) NSLog(@"%@", errorRef); 142 | return suc; 143 | } 144 | 145 | + (NSData *)dataFromFont:(UIFont *)font { 146 | CGFontRef cgFont = font.CGFontRef; 147 | NSData *data = [self dataFromCGFont:cgFont]; 148 | CGFontRelease(cgFont); 149 | return data; 150 | } 151 | 152 | typedef struct FontHeader { 153 | int32_t fVersion; 154 | uint16_t fNumTables; 155 | uint16_t fSearchRange; 156 | uint16_t fEntrySelector; 157 | uint16_t fRangeShift; 158 | } FontHeader; 159 | 160 | typedef struct TableEntry { 161 | uint32_t fTag; 162 | uint32_t fCheckSum; 163 | uint32_t fOffset; 164 | uint32_t fLength; 165 | } TableEntry; 166 | 167 | static uint32_t CalcTableCheckSum(const uint32_t *table, uint32_t numberOfBytesInTable) { 168 | uint32_t sum = 0; 169 | uint32_t nLongs = (numberOfBytesInTable + 3) / 4; 170 | while (nLongs-- > 0) { 171 | sum += CFSwapInt32HostToBig(*table++); 172 | } 173 | return sum; 174 | } 175 | 176 | //Reference: 177 | //https://github.com/google/skia/blob/master/src%2Fports%2FSkFontHost_mac.cpp 178 | + (NSData *)dataFromCGFont:(CGFontRef)cgFont { 179 | if (!cgFont) return nil; 180 | 181 | CFRetain(cgFont); 182 | 183 | CFArrayRef tags = CGFontCopyTableTags(cgFont); 184 | if (!tags) { 185 | CFRelease(cgFont); 186 | return nil; 187 | } 188 | CFIndex tableCount = CFArrayGetCount(tags); 189 | 190 | size_t *tableSizes = malloc(sizeof(size_t) * tableCount); 191 | memset(tableSizes, 0, sizeof(size_t) * tableCount); 192 | 193 | BOOL containsCFFTable = NO; 194 | 195 | size_t totalSize = sizeof(FontHeader) + sizeof(TableEntry) * tableCount; 196 | 197 | for (CFIndex index = 0; index < tableCount; index++) { 198 | size_t tableSize = 0; 199 | uint32_t aTag = (uint32_t)CFArrayGetValueAtIndex(tags, index); 200 | if (aTag == kCTFontTableCFF && !containsCFFTable) { 201 | containsCFFTable = YES; 202 | } 203 | CFDataRef tableDataRef = CGFontCopyTableForTag(cgFont, aTag); 204 | if (tableDataRef) { 205 | tableSize = CFDataGetLength(tableDataRef); 206 | CFRelease(tableDataRef); 207 | } 208 | totalSize += (tableSize + 3) & ~3; 209 | tableSizes[index] = tableSize; 210 | } 211 | 212 | unsigned char *stream = malloc(totalSize); 213 | memset(stream, 0, totalSize); 214 | char *dataStart = (char *)stream; 215 | char *dataPtr = dataStart; 216 | 217 | // compute font header entries 218 | uint16_t entrySelector = 0; 219 | uint16_t searchRange = 1; 220 | while (searchRange < tableCount >> 1) { 221 | entrySelector++; 222 | searchRange <<= 1; 223 | } 224 | searchRange <<= 4; 225 | 226 | uint16_t rangeShift = (tableCount << 4) - searchRange; 227 | 228 | // write font header (also called sfnt header, offset subtable) 229 | FontHeader *offsetTable = (FontHeader *)dataPtr; 230 | 231 | //OpenType Font contains CFF Table use 'OTTO' as version, and with .otf extension 232 | //otherwise 0001 0000 233 | offsetTable->fVersion = containsCFFTable ? 'OTTO' : CFSwapInt16HostToBig(1); 234 | offsetTable->fNumTables = CFSwapInt16HostToBig((uint16_t)tableCount); 235 | offsetTable->fSearchRange = CFSwapInt16HostToBig((uint16_t)searchRange); 236 | offsetTable->fEntrySelector = CFSwapInt16HostToBig((uint16_t)entrySelector); 237 | offsetTable->fRangeShift = CFSwapInt16HostToBig((uint16_t)rangeShift); 238 | 239 | dataPtr += sizeof(FontHeader); 240 | 241 | // write tables 242 | TableEntry *entry = (TableEntry *)dataPtr; 243 | dataPtr += sizeof(TableEntry) * tableCount; 244 | 245 | for (int index = 0; index < tableCount; ++index) { 246 | uint32_t aTag = (uint32_t)CFArrayGetValueAtIndex(tags, index); 247 | CFDataRef tableDataRef = CGFontCopyTableForTag(cgFont, aTag); 248 | size_t tableSize = CFDataGetLength(tableDataRef); 249 | 250 | memcpy(dataPtr, CFDataGetBytePtr(tableDataRef), tableSize); 251 | 252 | entry->fTag = CFSwapInt32HostToBig((uint32_t)aTag); 253 | entry->fCheckSum = CFSwapInt32HostToBig(CalcTableCheckSum((uint32_t *)dataPtr, (uint32_t)tableSize)); 254 | 255 | uint32_t offset = (uint32_t)dataPtr - (uint32_t)dataStart; 256 | entry->fOffset = CFSwapInt32HostToBig((uint32_t)offset); 257 | entry->fLength = CFSwapInt32HostToBig((uint32_t)tableSize); 258 | dataPtr += (tableSize + 3) & ~3; 259 | ++entry; 260 | CFRelease(tableDataRef); 261 | } 262 | 263 | CFRelease(cgFont); 264 | CFRelease(tags); 265 | free(tableSizes); 266 | NSData *fontData = [NSData dataWithBytesNoCopy:stream length:totalSize freeWhenDone:YES]; 267 | return fontData; 268 | } 269 | 270 | @end 271 | 272 | #pragma clang diagnostic pop 273 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIGestureRecognizer+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIGestureRecognizer+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIGestureRecognizer+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIGestureRecognizer+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIScreen+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScreen+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIScreen+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIScreen+YYAdd.m 3 | // YYCategories 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 "UIScreen+YYAdd.h" 13 | #import "YYCategoriesMacro.h" 14 | #import "UIDevice+YYAdd.h" 15 | 16 | YYSYNTH_DUMMY_CLASS(UIScreen_YYAdd); 17 | 18 | 19 | @implementation UIScreen (YYAdd) 20 | 21 | + (CGFloat)screenScale { 22 | static CGFloat screenScale = 0.0; 23 | static dispatch_once_t onceToken; 24 | dispatch_once(&onceToken, ^{ 25 | if ([NSThread isMainThread]) { 26 | screenScale = [[UIScreen mainScreen] scale]; 27 | } else { 28 | dispatch_sync(dispatch_get_main_queue(), ^{ 29 | screenScale = [[UIScreen mainScreen] scale]; 30 | }); 31 | } 32 | }); 33 | return screenScale; 34 | } 35 | 36 | #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED 37 | - (CGRect)currentBounds { 38 | return [self boundsForOrientation:[[UIApplication sharedApplication] statusBarOrientation]]; 39 | } 40 | #endif 41 | 42 | - (CGRect)boundsForOrientation:(UIInterfaceOrientation)orientation { 43 | CGRect bounds = [self bounds]; 44 | 45 | if (UIInterfaceOrientationIsLandscape(orientation)) { 46 | CGFloat buffer = bounds.size.width; 47 | bounds.size.width = bounds.size.height; 48 | bounds.size.height = buffer; 49 | } 50 | return bounds; 51 | } 52 | 53 | - (CGSize)sizeInPixel { 54 | CGSize size = CGSizeZero; 55 | 56 | if ([[UIScreen mainScreen] isEqual:self]) { 57 | NSString *model = [UIDevice currentDevice].machineModel; 58 | 59 | if ([model hasPrefix:@"iPhone"]) { 60 | if ([model isEqualToString:@"iPhone7,1"]) return CGSizeMake(1080, 1920); 61 | if ([model isEqualToString:@"iPhone8,2"]) return CGSizeMake(1080, 1920); 62 | if ([model isEqualToString:@"iPhone9,2"]) return CGSizeMake(1080, 1920); 63 | if ([model isEqualToString:@"iPhone9,4"]) return CGSizeMake(1080, 1920); 64 | } 65 | if ([model hasPrefix:@"iPad"]) { 66 | if ([model hasPrefix:@"iPad6,7"]) size = CGSizeMake(2048, 2732); 67 | if ([model hasPrefix:@"iPad6,8"]) size = CGSizeMake(2048, 2732); 68 | } 69 | } 70 | 71 | if (CGSizeEqualToSize(size, CGSizeZero)) { 72 | if ([self respondsToSelector:@selector(nativeBounds)]) { 73 | size = self.nativeBounds.size; 74 | } else { 75 | size = self.bounds.size; 76 | size.width *= self.scale; 77 | size.height *= self.scale; 78 | } 79 | if (size.height < size.width) { 80 | CGFloat tmp = size.height; 81 | size.height = size.width; 82 | size.width = tmp; 83 | } 84 | } 85 | return size; 86 | } 87 | 88 | - (CGFloat)pixelsPerInch { 89 | if (![[UIScreen mainScreen] isEqual:self]) { 90 | return 326; 91 | } 92 | 93 | static CGFloat ppi = 0; 94 | static dispatch_once_t one; 95 | dispatch_once(&one, ^{ 96 | NSDictionary *dic = @{ 97 | @"Watch1,1" : @326, //@"Apple Watch 38mm", 98 | @"Watch1,2" : @326, //@"Apple Watch 43mm", 99 | @"Watch2,3" : @326, //@"Apple Watch Series 2 38mm", 100 | @"Watch2,4" : @326, //@"Apple Watch Series 2 42mm", 101 | @"Watch2,6" : @326, //@"Apple Watch Series 1 38mm", 102 | @"Watch2,7" : @326, //@"Apple Watch Series 1 42mm", 103 | 104 | @"iPod1,1" : @163, //@"iPod touch 1", 105 | @"iPod2,1" : @163, //@"iPod touch 2", 106 | @"iPod3,1" : @163, //@"iPod touch 3", 107 | @"iPod4,1" : @326, //@"iPod touch 4", 108 | @"iPod5,1" : @326, //@"iPod touch 5", 109 | @"iPod7,1" : @326, //@"iPod touch 6", 110 | 111 | @"iPhone1,1" : @163, //@"iPhone 1G", 112 | @"iPhone1,2" : @163, //@"iPhone 3G", 113 | @"iPhone2,1" : @163, //@"iPhone 3GS", 114 | @"iPhone3,1" : @326, //@"iPhone 4 (GSM)", 115 | @"iPhone3,2" : @326, //@"iPhone 4", 116 | @"iPhone3,3" : @326, //@"iPhone 4 (CDMA)", 117 | @"iPhone4,1" : @326, //@"iPhone 4S", 118 | @"iPhone5,1" : @326, //@"iPhone 5", 119 | @"iPhone5,2" : @326, //@"iPhone 5", 120 | @"iPhone5,3" : @326, //@"iPhone 5c", 121 | @"iPhone5,4" : @326, //@"iPhone 5c", 122 | @"iPhone6,1" : @326, //@"iPhone 5s", 123 | @"iPhone6,2" : @326, //@"iPhone 5s", 124 | @"iPhone7,1" : @401, //@"iPhone 6 Plus", 125 | @"iPhone7,2" : @326, //@"iPhone 6", 126 | @"iPhone8,1" : @326, //@"iPhone 6s", 127 | @"iPhone8,2" : @401, //@"iPhone 6s Plus", 128 | @"iPhone8,4" : @326, //@"iPhone SE", 129 | @"iPhone9,1" : @326, //@"iPhone 7", 130 | @"iPhone9,2" : @401, //@"iPhone 7 Plus", 131 | @"iPhone9,3" : @326, //@"iPhone 7", 132 | @"iPhone9,4" : @401, //@"iPhone 7 Plus", 133 | 134 | @"iPad1,1" : @132, //@"iPad 1", 135 | @"iPad2,1" : @132, //@"iPad 2 (WiFi)", 136 | @"iPad2,2" : @132, //@"iPad 2 (GSM)", 137 | @"iPad2,3" : @132, //@"iPad 2 (CDMA)", 138 | @"iPad2,4" : @132, //@"iPad 2", 139 | @"iPad2,5" : @264, //@"iPad mini 1", 140 | @"iPad2,6" : @264, //@"iPad mini 1", 141 | @"iPad2,7" : @264, //@"iPad mini 1", 142 | @"iPad3,1" : @324, //@"iPad 3 (WiFi)", 143 | @"iPad3,2" : @324, //@"iPad 3 (4G)", 144 | @"iPad3,3" : @324, //@"iPad 3 (4G)", 145 | @"iPad3,4" : @324, //@"iPad 4", 146 | @"iPad3,5" : @324, //@"iPad 4", 147 | @"iPad3,6" : @324, //@"iPad 4", 148 | @"iPad4,1" : @324, //@"iPad Air", 149 | @"iPad4,2" : @324, //@"iPad Air", 150 | @"iPad4,3" : @324, //@"iPad Air", 151 | @"iPad4,4" : @264, //@"iPad mini 2", 152 | @"iPad4,5" : @264, //@"iPad mini 2", 153 | @"iPad4,6" : @264, //@"iPad mini 2", 154 | @"iPad4,7" : @264, //@"iPad mini 3", 155 | @"iPad4,8" : @264, //@"iPad mini 3", 156 | @"iPad4,9" : @264, //@"iPad mini 3", 157 | @"iPad5,1" : @264, //@"iPad mini 4", 158 | @"iPad5,2" : @264, //@"iPad mini 4", 159 | @"iPad5,3" : @324, //@"iPad Air 2", 160 | @"iPad5,4" : @324, //@"iPad Air 2", 161 | @"iPad6,3" : @324, //@"iPad Pro (9.7 inch)", 162 | @"iPad6,4" : @324, //@"iPad Pro (9.7 inch)", 163 | @"iPad6,7" : @264, //@"iPad Pro (12.9 inch)", 164 | @"iPad6,8" : @264, //@"iPad Pro (12.9 inch)", 165 | }; 166 | NSString *model = [UIDevice currentDevice].machineModel; 167 | if (model) { 168 | ppi = dic[model].doubleValue; 169 | } 170 | if (ppi == 0) ppi = 326; 171 | }); 172 | return ppi; 173 | } 174 | 175 | @end 176 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIScrollView+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIScrollView+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UITableView+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+YYAdd.h 3 | // YYCategories 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 `UITableView`. 18 | */ 19 | @interface UITableView (YYAdd) 20 | 21 | /** 22 | Perform a series of method calls that insert, delete, or select rows and 23 | sections of the receiver. 24 | 25 | @discussion Perform a series of method calls that insert, delete, or select 26 | rows and sections of the table. Call this method if you want 27 | subsequent insertions, deletion, and selection operations (for 28 | example, cellForRowAtIndexPath: and indexPathsForVisibleRows) 29 | to be animated simultaneously. 30 | 31 | @discussion If you do not make the insertion, deletion, and selection calls 32 | inside this block, table attributes such as row count might become 33 | invalid. You should not call reloadData within the block; if you 34 | call this method within the group, you will need to perform any 35 | animations yourself. 36 | 37 | @param block A block combine a series of method calls. 38 | */ 39 | - (void)updateWithBlock:(void (^)(UITableView *tableView))block; 40 | 41 | /** 42 | Scrolls the receiver until a row or section location on the screen. 43 | 44 | @discussion Invoking this method does not cause the delegate to 45 | receive a scrollViewDidScroll: message, as is normal for 46 | programmatically-invoked user interface operations. 47 | 48 | @param row Row index in section. NSNotFound is a valid value for 49 | scrolling to a section with zero rows. 50 | 51 | @param section Section index in table. 52 | 53 | @param scrollPosition A constant that identifies a relative position in the 54 | receiving table view (top, middle, bottom) for row when 55 | scrolling concludes. 56 | 57 | @param animated YES if you want to animate the change in position, 58 | NO if it should be immediate. 59 | */ 60 | - (void)scrollToRow:(NSUInteger)row inSection:(NSUInteger)section atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated; 61 | 62 | /** 63 | Inserts a row in the receiver with an option to animate the insertion. 64 | 65 | @param row Row index in section. 66 | 67 | @param section Section index in table. 68 | 69 | @param animation A constant that either specifies the kind of animation to 70 | perform when inserting the cell or requests no animation. 71 | */ 72 | - (void)insertRow:(NSUInteger)row inSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation; 73 | 74 | /** 75 | Reloads the specified row using a certain animation effect. 76 | 77 | @param row Row index in section. 78 | 79 | @param section Section index in table. 80 | 81 | @param animation A constant that indicates how the reloading is to be animated, 82 | for example, fade out or slide out from the bottom. The animation 83 | constant affects the direction in which both the old and the 84 | new rows slide. For example, if the animation constant is 85 | UITableViewRowAnimationRight, the old rows slide out to the 86 | right and the new cells slide in from the right. 87 | */ 88 | - (void)reloadRow:(NSUInteger)row inSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation; 89 | 90 | /** 91 | Deletes the row with an option to animate the deletion. 92 | 93 | @param row Row index in section. 94 | 95 | @param section Section index in table. 96 | 97 | @param animation A constant that indicates how the deletion is to be animated, 98 | for example, fade out or slide out from the bottom. 99 | */ 100 | - (void)deleteRow:(NSUInteger)row inSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation; 101 | 102 | /** 103 | Inserts the row in the receiver at the locations identified by the indexPath, 104 | with an option to animate the insertion. 105 | 106 | @param indexPath An NSIndexPath object representing a row index and section 107 | index that together identify a row in the table view. 108 | 109 | @param animation A constant that either specifies the kind of animation to 110 | perform when inserting the cell or requests no animation. 111 | */ 112 | - (void)insertRowAtIndexPath:(NSIndexPath *)indexPath withRowAnimation:(UITableViewRowAnimation)animation; 113 | 114 | /** 115 | Reloads the specified row using a certain animation effect. 116 | 117 | @param indexPath An NSIndexPath object representing a row index and section 118 | index that together identify a row in the table view. 119 | 120 | @param animation A constant that indicates how the reloading is to be animated, 121 | for example, fade out or slide out from the bottom. The animation 122 | constant affects the direction in which both the old and the 123 | new rows slide. For example, if the animation constant is 124 | UITableViewRowAnimationRight, the old rows slide out to the 125 | right and the new cells slide in from the right. 126 | */ 127 | - (void)reloadRowAtIndexPath:(NSIndexPath *)indexPath withRowAnimation:(UITableViewRowAnimation)animation; 128 | 129 | /** 130 | Deletes the row specified by an array of index paths, 131 | with an option to animate the deletion. 132 | 133 | @param indexPath An NSIndexPath object representing a row index and section 134 | index that together identify a row in the table view. 135 | 136 | @param animation A constant that indicates how the deletion is to be animated, 137 | for example, fade out or slide out from the bottom. 138 | */ 139 | - (void)deleteRowAtIndexPath:(NSIndexPath *)indexPath withRowAnimation:(UITableViewRowAnimation)animation; 140 | 141 | /** 142 | Inserts a section in the receiver, with an option to animate the insertion. 143 | 144 | @param section An index specifies the section to insert in the receiving 145 | table view. If a section already exists at the specified 146 | index location, it is moved down one index location. 147 | 148 | @param animation A constant that indicates how the insertion is to be animated, 149 | for example, fade in or slide in from the left. 150 | */ 151 | - (void)insertSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation; 152 | 153 | /** 154 | Deletes a section in the receiver, with an option to animate the deletion. 155 | 156 | @param section An index that specifies the sections to delete from the 157 | receiving table view. If a section exists after the specified 158 | index location, it is moved up one index location. 159 | 160 | @param animation A constant that either specifies the kind of animation to 161 | perform when deleting the section or requests no animation. 162 | */ 163 | - (void)deleteSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation; 164 | 165 | /** 166 | Reloads the specified section using a given animation effect. 167 | 168 | @param section An index identifying the section to reload. 169 | 170 | @param animation A constant that indicates how the reloading is to be animated, 171 | for example, fade out or slide out from the bottom. The 172 | animation constant affects the direction in which both the 173 | old and the new section rows slide. For example, if the 174 | animation constant is UITableViewRowAnimationRight, the old 175 | rows slide out to the right and the new cells slide in from the right. 176 | */ 177 | - (void)reloadSection:(NSUInteger)section withRowAnimation:(UITableViewRowAnimation)animation; 178 | 179 | /** 180 | Unselect all rows in tableView. 181 | 182 | @param animated YES to animate the transition, NO to make the transition immediate. 183 | */ 184 | - (void)clearSelectedRowsAnimated:(BOOL)animated; 185 | 186 | @end 187 | 188 | NS_ASSUME_NONNULL_END 189 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UITableView+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UITextField+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UITextField+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+YYAdd.m 3 | // YYCategories 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 "YYCategoriesMacro.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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIView+YYAdd.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+YYAdd.h 3 | // YYCategories 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 | -------------------------------------------------------------------------------- /YYCategories/UIKit/UIView+YYAdd.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+YYAdd.m 3 | // YYCategories 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 "UIView+YYAdd.h" 13 | #import 14 | #import "YYCategoriesMacro.h" 15 | 16 | YYSYNTH_DUMMY_CLASS(UIView_YYAdd) 17 | 18 | 19 | @implementation UIView (YYAdd) 20 | 21 | - (UIImage *)snapshotImage { 22 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0); 23 | [self.layer renderInContext:UIGraphicsGetCurrentContext()]; 24 | UIImage *snap = UIGraphicsGetImageFromCurrentImageContext(); 25 | UIGraphicsEndImageContext(); 26 | return snap; 27 | } 28 | 29 | - (UIImage *)snapshotImageAfterScreenUpdates:(BOOL)afterUpdates { 30 | if (![self respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) { 31 | return [self snapshotImage]; 32 | } 33 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0); 34 | [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:afterUpdates]; 35 | UIImage *snap = UIGraphicsGetImageFromCurrentImageContext(); 36 | UIGraphicsEndImageContext(); 37 | return snap; 38 | } 39 | 40 | - (NSData *)snapshotPDF { 41 | CGRect bounds = self.bounds; 42 | NSMutableData* data = [NSMutableData data]; 43 | CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData((__bridge CFMutableDataRef)data); 44 | CGContextRef context = CGPDFContextCreate(consumer, &bounds, NULL); 45 | CGDataConsumerRelease(consumer); 46 | if (!context) return nil; 47 | CGPDFContextBeginPage(context, NULL); 48 | CGContextTranslateCTM(context, 0, bounds.size.height); 49 | CGContextScaleCTM(context, 1.0, -1.0); 50 | [self.layer renderInContext:context]; 51 | CGPDFContextEndPage(context); 52 | CGPDFContextClose(context); 53 | CGContextRelease(context); 54 | return data; 55 | } 56 | 57 | - (void)setLayerShadow:(UIColor*)color offset:(CGSize)offset radius:(CGFloat)radius { 58 | self.layer.shadowColor = color.CGColor; 59 | self.layer.shadowOffset = offset; 60 | self.layer.shadowRadius = radius; 61 | self.layer.shadowOpacity = 1; 62 | self.layer.shouldRasterize = YES; 63 | self.layer.rasterizationScale = [UIScreen mainScreen].scale; 64 | } 65 | 66 | - (void)removeAllSubviews { 67 | //[self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 68 | while (self.subviews.count) { 69 | [self.subviews.lastObject removeFromSuperview]; 70 | } 71 | } 72 | 73 | 74 | - (UIViewController *)viewController { 75 | for (UIView *view = self; view; view = view.superview) { 76 | UIResponder *nextResponder = [view nextResponder]; 77 | if ([nextResponder isKindOfClass:[UIViewController class]]) { 78 | return (UIViewController *)nextResponder; 79 | } 80 | } 81 | return nil; 82 | } 83 | 84 | - (CGFloat)visibleAlpha { 85 | if ([self isKindOfClass:[UIWindow class]]) { 86 | if (self.hidden) return 0; 87 | return self.alpha; 88 | } 89 | if (!self.window) return 0; 90 | CGFloat alpha = 1; 91 | UIView *v = self; 92 | while (v) { 93 | if (v.hidden) { 94 | alpha = 0; 95 | break; 96 | } 97 | alpha *= v.alpha; 98 | v = v.superview; 99 | } 100 | return alpha; 101 | } 102 | 103 | - (CGPoint)convertPoint:(CGPoint)point toViewOrWindow:(UIView *)view { 104 | if (!view) { 105 | if ([self isKindOfClass:[UIWindow class]]) { 106 | return [((UIWindow *)self) convertPoint:point toWindow:nil]; 107 | } else { 108 | return [self convertPoint:point toView:nil]; 109 | } 110 | } 111 | 112 | UIWindow *from = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window; 113 | UIWindow *to = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window; 114 | if ((!from || !to) || (from == to)) return [self convertPoint:point toView:view]; 115 | point = [self convertPoint:point toView:from]; 116 | point = [to convertPoint:point fromWindow:from]; 117 | point = [view convertPoint:point fromView:to]; 118 | return point; 119 | } 120 | 121 | - (CGPoint)convertPoint:(CGPoint)point fromViewOrWindow:(UIView *)view { 122 | if (!view) { 123 | if ([self isKindOfClass:[UIWindow class]]) { 124 | return [((UIWindow *)self) convertPoint:point fromWindow:nil]; 125 | } else { 126 | return [self convertPoint:point fromView:nil]; 127 | } 128 | } 129 | 130 | UIWindow *from = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window; 131 | UIWindow *to = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window; 132 | if ((!from || !to) || (from == to)) return [self convertPoint:point fromView:view]; 133 | point = [from convertPoint:point fromView:view]; 134 | point = [to convertPoint:point fromWindow:from]; 135 | point = [self convertPoint:point fromView:to]; 136 | return point; 137 | } 138 | 139 | - (CGRect)convertRect:(CGRect)rect toViewOrWindow:(UIView *)view { 140 | if (!view) { 141 | if ([self isKindOfClass:[UIWindow class]]) { 142 | return [((UIWindow *)self) convertRect:rect toWindow:nil]; 143 | } else { 144 | return [self convertRect:rect toView:nil]; 145 | } 146 | } 147 | 148 | UIWindow *from = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window; 149 | UIWindow *to = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window; 150 | if (!from || !to) return [self convertRect:rect toView:view]; 151 | if (from == to) return [self convertRect:rect toView:view]; 152 | rect = [self convertRect:rect toView:from]; 153 | rect = [to convertRect:rect fromWindow:from]; 154 | rect = [view convertRect:rect fromView:to]; 155 | return rect; 156 | } 157 | 158 | - (CGRect)convertRect:(CGRect)rect fromViewOrWindow:(UIView *)view { 159 | if (!view) { 160 | if ([self isKindOfClass:[UIWindow class]]) { 161 | return [((UIWindow *)self) convertRect:rect fromWindow:nil]; 162 | } else { 163 | return [self convertRect:rect fromView:nil]; 164 | } 165 | } 166 | 167 | UIWindow *from = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window; 168 | UIWindow *to = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window; 169 | if ((!from || !to) || (from == to)) return [self convertRect:rect fromView:view]; 170 | rect = [from convertRect:rect fromView:view]; 171 | rect = [to convertRect:rect fromWindow:from]; 172 | rect = [self convertRect:rect fromView:to]; 173 | return rect; 174 | } 175 | 176 | - (CGFloat)left { 177 | return self.frame.origin.x; 178 | } 179 | 180 | - (void)setLeft:(CGFloat)x { 181 | CGRect frame = self.frame; 182 | frame.origin.x = x; 183 | self.frame = frame; 184 | } 185 | 186 | - (CGFloat)top { 187 | return self.frame.origin.y; 188 | } 189 | 190 | - (void)setTop:(CGFloat)y { 191 | CGRect frame = self.frame; 192 | frame.origin.y = y; 193 | self.frame = frame; 194 | } 195 | 196 | - (CGFloat)right { 197 | return self.frame.origin.x + self.frame.size.width; 198 | } 199 | 200 | - (void)setRight:(CGFloat)right { 201 | CGRect frame = self.frame; 202 | frame.origin.x = right - frame.size.width; 203 | self.frame = frame; 204 | } 205 | 206 | - (CGFloat)bottom { 207 | return self.frame.origin.y + self.frame.size.height; 208 | } 209 | 210 | - (void)setBottom:(CGFloat)bottom { 211 | CGRect frame = self.frame; 212 | frame.origin.y = bottom - frame.size.height; 213 | self.frame = frame; 214 | } 215 | 216 | - (CGFloat)width { 217 | return self.frame.size.width; 218 | } 219 | 220 | - (void)setWidth:(CGFloat)width { 221 | CGRect frame = self.frame; 222 | frame.size.width = width; 223 | self.frame = frame; 224 | } 225 | 226 | - (CGFloat)height { 227 | return self.frame.size.height; 228 | } 229 | 230 | - (void)setHeight:(CGFloat)height { 231 | CGRect frame = self.frame; 232 | frame.size.height = height; 233 | self.frame = frame; 234 | } 235 | 236 | - (CGFloat)centerX { 237 | return self.center.x; 238 | } 239 | 240 | - (void)setCenterX:(CGFloat)centerX { 241 | self.center = CGPointMake(centerX, self.center.y); 242 | } 243 | 244 | - (CGFloat)centerY { 245 | return self.center.y; 246 | } 247 | 248 | - (void)setCenterY:(CGFloat)centerY { 249 | self.center = CGPointMake(self.center.x, centerY); 250 | } 251 | 252 | - (CGPoint)origin { 253 | return self.frame.origin; 254 | } 255 | 256 | - (void)setOrigin:(CGPoint)origin { 257 | CGRect frame = self.frame; 258 | frame.origin = origin; 259 | self.frame = frame; 260 | } 261 | 262 | - (CGSize)size { 263 | return self.frame.size; 264 | } 265 | 266 | - (void)setSize:(CGSize)size { 267 | CGRect frame = self.frame; 268 | frame.size = size; 269 | self.frame = frame; 270 | } 271 | 272 | @end 273 | -------------------------------------------------------------------------------- /YYCategories/YYCategories.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYCategories.h 3 | // YYCategories 4 | // 5 | // Created by ibireme on 13/3/29. 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 | FOUNDATION_EXPORT double YYCategoriesVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char YYCategoriesVersionString[]; 17 | #import 18 | #import 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | #import 34 | #import 35 | #import 36 | #import 37 | #import 38 | #import 39 | #import 40 | #import 41 | #import 42 | #import 43 | #import 44 | #import 45 | #import 46 | #import 47 | #import 48 | #else 49 | #import "YYCategoriesMacro.h" 50 | #import "NSObject+YYAdd.h" 51 | #import "NSObject+YYAddForKVO.h" 52 | #import "NSObject+YYAddForARC.h" 53 | #import "NSData+YYAdd.h" 54 | #import "NSString+YYAdd.h" 55 | #import "NSArray+YYAdd.h" 56 | #import "NSDictionary+YYAdd.h" 57 | #import "NSDate+YYAdd.h" 58 | #import "NSNumber+YYAdd.h" 59 | #import "NSNotificationCenter+YYAdd.h" 60 | #import "NSKeyedUnarchiver+YYAdd.h" 61 | #import "NSTimer+YYAdd.h" 62 | #import "NSBundle+YYAdd.h" 63 | #import "NSThread+YYAdd.h" 64 | #import "UIColor+YYAdd.h" 65 | #import "UIImage+YYAdd.h" 66 | #import "UIControl+YYAdd.h" 67 | #import "UIBarButtonItem+YYAdd.h" 68 | #import "UIGestureRecognizer+YYAdd.h" 69 | #import "UIView+YYAdd.h" 70 | #import "UIScrollView+YYAdd.h" 71 | #import "UITableView+YYAdd.h" 72 | #import "UITextField+YYAdd.h" 73 | #import "UIScreen+YYAdd.h" 74 | #import "UIDevice+YYAdd.h" 75 | #import "UIApplication+YYAdd.h" 76 | #import "UIFont+YYAdd.h" 77 | #import "UIBezierPath+YYAdd.h" 78 | #import "CALayer+YYAdd.h" 79 | #import "YYCGUtilities.h" 80 | #endif 81 | 82 | -------------------------------------------------------------------------------- /YYCategories/YYCategoriesMacro.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYCategoriesMacro.h 3 | // YYCategories 4 | // 5 | // Created by ibireme on 13/3/29. 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 | #ifndef YYCategoriesMacro_h 17 | #define YYCategoriesMacro_h 18 | 19 | #ifdef __cplusplus 20 | #define YY_EXTERN_C_BEGIN extern "C" { 21 | #define YY_EXTERN_C_END } 22 | #else 23 | #define YY_EXTERN_C_BEGIN 24 | #define YY_EXTERN_C_END 25 | #endif 26 | 27 | 28 | 29 | YY_EXTERN_C_BEGIN 30 | 31 | #ifndef YY_CLAMP // return the clamped value 32 | #define YY_CLAMP(_x_, _low_, _high_) (((_x_) > (_high_)) ? (_high_) : (((_x_) < (_low_)) ? (_low_) : (_x_))) 33 | #endif 34 | 35 | #ifndef YY_SWAP // swap two value 36 | #define YY_SWAP(_a_, _b_) do { __typeof__(_a_) _tmp_ = (_a_); (_a_) = (_b_); (_b_) = _tmp_; } while (0) 37 | #endif 38 | 39 | 40 | #define YYAssertNil(condition, description, ...) NSAssert(!(condition), (description), ##__VA_ARGS__) 41 | #define YYCAssertNil(condition, description, ...) NSCAssert(!(condition), (description), ##__VA_ARGS__) 42 | 43 | #define YYAssertNotNil(condition, description, ...) NSAssert((condition), (description), ##__VA_ARGS__) 44 | #define YYCAssertNotNil(condition, description, ...) NSCAssert((condition), (description), ##__VA_ARGS__) 45 | 46 | #define YYAssertMainThread() NSAssert([NSThread isMainThread], @"This method must be called on the main thread") 47 | #define YYCAssertMainThread() NSCAssert([NSThread isMainThread], @"This method must be called on the main thread") 48 | 49 | 50 | /** 51 | Add this macro before each category implementation, so we don't have to use 52 | -all_load or -force_load to load object files from static libraries that only 53 | contain categories and no classes. 54 | More info: http://developer.apple.com/library/mac/#qa/qa2006/qa1490.html . 55 | ******************************************************************************* 56 | Example: 57 | YYSYNTH_DUMMY_CLASS(NSString_YYAdd) 58 | */ 59 | #ifndef YYSYNTH_DUMMY_CLASS 60 | #define YYSYNTH_DUMMY_CLASS(_name_) \ 61 | @interface YYSYNTH_DUMMY_CLASS_ ## _name_ : NSObject @end \ 62 | @implementation YYSYNTH_DUMMY_CLASS_ ## _name_ @end 63 | #endif 64 | 65 | 66 | /** 67 | Synthsize a dynamic object property in @implementation scope. 68 | It allows us to add custom properties to existing classes in categories. 69 | 70 | @param association ASSIGN / RETAIN / COPY / RETAIN_NONATOMIC / COPY_NONATOMIC 71 | @warning #import 72 | ******************************************************************************* 73 | Example: 74 | @interface NSObject (MyAdd) 75 | @property (nonatomic, retain) UIColor *myColor; 76 | @end 77 | 78 | #import 79 | @implementation NSObject (MyAdd) 80 | YYSYNTH_DYNAMIC_PROPERTY_OBJECT(myColor, setMyColor, RETAIN, UIColor *) 81 | @end 82 | */ 83 | #ifndef YYSYNTH_DYNAMIC_PROPERTY_OBJECT 84 | #define YYSYNTH_DYNAMIC_PROPERTY_OBJECT(_getter_, _setter_, _association_, _type_) \ 85 | - (void)_setter_ : (_type_)object { \ 86 | [self willChangeValueForKey:@#_getter_]; \ 87 | objc_setAssociatedObject(self, _cmd, object, OBJC_ASSOCIATION_ ## _association_); \ 88 | [self didChangeValueForKey:@#_getter_]; \ 89 | } \ 90 | - (_type_)_getter_ { \ 91 | return objc_getAssociatedObject(self, @selector(_setter_:)); \ 92 | } 93 | #endif 94 | 95 | 96 | /** 97 | Synthsize a dynamic c type property in @implementation scope. 98 | It allows us to add custom properties to existing classes in categories. 99 | 100 | @warning #import 101 | ******************************************************************************* 102 | Example: 103 | @interface NSObject (MyAdd) 104 | @property (nonatomic, retain) CGPoint myPoint; 105 | @end 106 | 107 | #import 108 | @implementation NSObject (MyAdd) 109 | YYSYNTH_DYNAMIC_PROPERTY_CTYPE(myPoint, setMyPoint, CGPoint) 110 | @end 111 | */ 112 | #ifndef YYSYNTH_DYNAMIC_PROPERTY_CTYPE 113 | #define YYSYNTH_DYNAMIC_PROPERTY_CTYPE(_getter_, _setter_, _type_) \ 114 | - (void)_setter_ : (_type_)object { \ 115 | [self willChangeValueForKey:@#_getter_]; \ 116 | NSValue *value = [NSValue value:&object withObjCType:@encode(_type_)]; \ 117 | objc_setAssociatedObject(self, _cmd, value, OBJC_ASSOCIATION_RETAIN); \ 118 | [self didChangeValueForKey:@#_getter_]; \ 119 | } \ 120 | - (type)_getter_ { \ 121 | _type_ cValue = { 0 }; \ 122 | NSValue *value = objc_getAssociatedObject(self, @selector(_setter_:)); \ 123 | [value getValue:&cValue]; \ 124 | return cValue; \ 125 | } 126 | #endif 127 | 128 | /** 129 | Synthsize a weak or strong reference. 130 | 131 | Example: 132 | @weakify(self) 133 | [self doSomething^{ 134 | @strongify(self) 135 | if (!self) return; 136 | ... 137 | }]; 138 | 139 | */ 140 | #ifndef weakify 141 | #if DEBUG 142 | #if __has_feature(objc_arc) 143 | #define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object; 144 | #else 145 | #define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object; 146 | #endif 147 | #else 148 | #if __has_feature(objc_arc) 149 | #define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object; 150 | #else 151 | #define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object; 152 | #endif 153 | #endif 154 | #endif 155 | 156 | #ifndef strongify 157 | #if DEBUG 158 | #if __has_feature(objc_arc) 159 | #define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object; 160 | #else 161 | #define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object; 162 | #endif 163 | #else 164 | #if __has_feature(objc_arc) 165 | #define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object; 166 | #else 167 | #define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object; 168 | #endif 169 | #endif 170 | #endif 171 | 172 | 173 | /** 174 | Convert CFRange to NSRange 175 | @param range CFRange @return NSRange 176 | */ 177 | static inline NSRange YYNSRangeFromCFRange(CFRange range) { 178 | return NSMakeRange(range.location, range.length); 179 | } 180 | 181 | /** 182 | Convert NSRange to CFRange 183 | @param range NSRange @return CFRange 184 | */ 185 | static inline CFRange YYCFRangeFromNSRange(NSRange range) { 186 | return CFRangeMake(range.location, range.length); 187 | } 188 | 189 | /** 190 | Same as CFAutorelease(), compatible for iOS6 191 | @param arg CFObject @return same as input 192 | */ 193 | static inline CFTypeRef YYCFAutorelease(CFTypeRef CF_RELEASES_ARGUMENT arg) { 194 | if (((long)CFAutorelease + 1) != 1) { 195 | return CFAutorelease(arg); 196 | } else { 197 | id __autoreleasing obj = CFBridgingRelease(arg); 198 | return (__bridge CFTypeRef)obj; 199 | } 200 | } 201 | 202 | /** 203 | Profile time cost. 204 | @param block code to benchmark 205 | @param complete code time cost (millisecond) 206 | 207 | Usage: 208 | YYBenchmark(^{ 209 | // code 210 | }, ^(double ms) { 211 | NSLog("time cost: %.2f ms",ms); 212 | }); 213 | 214 | */ 215 | static inline void YYBenchmark(void (^block)(void), void (^complete)(double ms)) { 216 | // version 217 | /* 218 | extern double CACurrentMediaTime (void); 219 | double begin, end, ms; 220 | begin = CACurrentMediaTime(); 221 | block(); 222 | end = CACurrentMediaTime(); 223 | ms = (end - begin) * 1000.0; 224 | complete(ms); 225 | */ 226 | 227 | // version 228 | struct timeval t0, t1; 229 | gettimeofday(&t0, NULL); 230 | block(); 231 | gettimeofday(&t1, NULL); 232 | double ms = (double)(t1.tv_sec - t0.tv_sec) * 1e3 + (double)(t1.tv_usec - t0.tv_usec) * 1e-3; 233 | complete(ms); 234 | } 235 | 236 | static inline NSDate *_YYCompileTime(const char *data, const char *time) { 237 | NSString *timeStr = [NSString stringWithFormat:@"%s %s",data,time]; 238 | NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; 239 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 240 | [formatter setDateFormat:@"MMM dd yyyy HH:mm:ss"]; 241 | [formatter setLocale:locale]; 242 | return [formatter dateFromString:timeStr]; 243 | } 244 | 245 | /** 246 | Get compile timestamp. 247 | @return A new date object set to the compile date and time. 248 | */ 249 | #ifndef YYCompileTime 250 | // use macro to avoid compile warning when use pch file 251 | #define YYCompileTime() _YYCompileTime(__DATE__, __TIME__) 252 | #endif 253 | 254 | /** 255 | Returns a dispatch_time delay from now. 256 | */ 257 | static inline dispatch_time_t dispatch_time_delay(NSTimeInterval second) { 258 | return dispatch_time(DISPATCH_TIME_NOW, (int64_t)(second * NSEC_PER_SEC)); 259 | } 260 | 261 | /** 262 | Returns a dispatch_wall_time delay from now. 263 | */ 264 | static inline dispatch_time_t dispatch_walltime_delay(NSTimeInterval second) { 265 | return dispatch_walltime(DISPATCH_TIME_NOW, (int64_t)(second * NSEC_PER_SEC)); 266 | } 267 | 268 | /** 269 | Returns a dispatch_wall_time from NSDate. 270 | */ 271 | static inline dispatch_time_t dispatch_walltime_date(NSDate *date) { 272 | NSTimeInterval interval; 273 | double second, subsecond; 274 | struct timespec time; 275 | dispatch_time_t milestone; 276 | 277 | interval = [date timeIntervalSince1970]; 278 | subsecond = modf(interval, &second); 279 | time.tv_sec = second; 280 | time.tv_nsec = subsecond * NSEC_PER_SEC; 281 | milestone = dispatch_walltime(&time, 0); 282 | return milestone; 283 | } 284 | 285 | /** 286 | Whether in main queue/thread. 287 | */ 288 | static inline bool dispatch_is_main_queue() { 289 | return pthread_main_np() != 0; 290 | } 291 | 292 | /** 293 | Submits a block for asynchronous execution on a main queue and returns immediately. 294 | */ 295 | static inline void dispatch_async_on_main_queue(void (^block)(void)) { 296 | if (pthread_main_np()) { 297 | block(); 298 | } else { 299 | dispatch_async(dispatch_get_main_queue(), block); 300 | } 301 | } 302 | 303 | /** 304 | Submits a block for execution on a main queue and waits until the block completes. 305 | */ 306 | static inline void dispatch_sync_on_main_queue(void (^block)(void)) { 307 | if (pthread_main_np()) { 308 | block(); 309 | } else { 310 | dispatch_sync(dispatch_get_main_queue(), block); 311 | } 312 | } 313 | 314 | /** 315 | Initialize a pthread mutex. 316 | */ 317 | static inline void pthread_mutex_init_recursive(pthread_mutex_t *mutex, bool recursive) { 318 | #define YYMUTEX_ASSERT_ON_ERROR(x_) do { \ 319 | __unused volatile int res = (x_); \ 320 | assert(res == 0); \ 321 | } while (0) 322 | assert(mutex != NULL); 323 | if (!recursive) { 324 | YYMUTEX_ASSERT_ON_ERROR(pthread_mutex_init(mutex, NULL)); 325 | } else { 326 | pthread_mutexattr_t attr; 327 | YYMUTEX_ASSERT_ON_ERROR(pthread_mutexattr_init (&attr)); 328 | YYMUTEX_ASSERT_ON_ERROR(pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE)); 329 | YYMUTEX_ASSERT_ON_ERROR(pthread_mutex_init (mutex, &attr)); 330 | YYMUTEX_ASSERT_ON_ERROR(pthread_mutexattr_destroy (&attr)); 331 | } 332 | #undef YYMUTEX_ASSERT_ON_ERROR 333 | } 334 | 335 | 336 | YY_EXTERN_C_END 337 | #endif 338 | --------------------------------------------------------------------------------