├── .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 | [](https://raw.githubusercontent.com/ibireme/YYCategories/master/LICENSE)
5 | [](https://github.com/Carthage/Carthage)
6 | [](http://cocoapods.org/pods/YYCategories)
7 | [](http://cocoadocs.org/docsets/YYCategories)
8 | [](https://www.apple.com/nl/ios/)
9 | [](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 |
--------------------------------------------------------------------------------