├── .gitignore ├── LICENSE ├── README.md ├── SUIUtils.podspec ├── SUIUtils ├── Foundation │ ├── NSArray+SUIAdditions.h │ ├── NSArray+SUIAdditions.m │ ├── NSArray+SUISafeAccess.h │ ├── NSArray+SUISafeAccess.m │ ├── NSData+SUIAdditions.h │ ├── NSData+SUIAdditions.m │ ├── NSDate+SUIAdditions.h │ ├── NSDate+SUIAdditions.m │ ├── NSDictionary+SUIAdditions.h │ ├── NSDictionary+SUIAdditions.m │ ├── NSDictionary+SUISafeAccess.h │ ├── NSDictionary+SUISafeAccess.m │ ├── NSIndexPath+SUIAdditions.h │ ├── NSIndexPath+SUIAdditions.m │ ├── NSNumber+SUIAdditions.h │ ├── NSNumber+SUIAdditions.m │ ├── NSObject+SUIAdditions.h │ ├── NSObject+SUIAdditions.m │ ├── NSString+SUIAdditions.h │ ├── NSString+SUIAdditions.m │ ├── NSString+SUICrypto.h │ ├── NSString+SUICrypto.m │ ├── NSString+SUIRegex.h │ └── NSString+SUIRegex.m ├── Helper │ ├── SUITableHelper.h │ ├── SUITableHelper.m │ ├── UITableView+SUIHelper.h │ ├── UITableView+SUIHelper.m │ ├── UITableViewCell+SUIHelper.h │ └── UITableViewCell+SUIHelper.m ├── SUIUtils.h ├── Tool │ ├── SUIMacro.h │ ├── SUITool+Camera.h │ ├── SUITool+Camera.m │ ├── SUITool+Delay.h │ ├── SUITool+Delay.m │ ├── SUITool+FileManager.h │ ├── SUITool+FileManager.m │ ├── SUITool+OpenURL.h │ ├── SUITool+OpenURL.m │ ├── SUITool.h │ └── SUITool.m ├── UIKit │ ├── UIButton+SUIAdditions.h │ ├── UIButton+SUIAdditions.m │ ├── UIControl+SUIAdditions.h │ ├── UIControl+SUIAdditions.m │ ├── UIImage+SUIAdditions.h │ ├── UIImage+SUIAdditions.m │ ├── UILabel+SUIAdditions.h │ ├── UILabel+SUIAdditions.m │ ├── UINavigationController+SUIAdditions.h │ ├── UINavigationController+SUIAdditions.m │ ├── UIScrollView+SUIAdditions.h │ ├── UIScrollView+SUIAdditions.m │ ├── UIStoryboardSegue+SUIAdditions.h │ ├── UIStoryboardSegue+SUIAdditions.m │ ├── UITableViewCell+SUIAdditions.h │ ├── UITableViewCell+SUIAdditions.m │ ├── UITextField+SUIAdditions.h │ ├── UITextField+SUIAdditions.m │ ├── UITextView+SUIAdditions.h │ ├── UITextView+SUIAdditions.m │ ├── UIView+SUIAdditions.h │ ├── UIView+SUIAdditions.m │ ├── UIViewController+SUIAdditions.h │ └── UIViewController+SUIAdditions.m └── UITableView+FDTemplateLayoutCell │ ├── UITableView+FDIndexPathHeightCache.h │ ├── UITableView+FDIndexPathHeightCache.m │ ├── UITableView+FDKeyedHeightCache.h │ ├── UITableView+FDKeyedHeightCache.m │ ├── UITableView+FDTemplateLayoutCell.h │ ├── UITableView+FDTemplateLayoutCell.m │ ├── UITableView+FDTemplateLayoutCellDebug.h │ └── UITableView+FDTemplateLayoutCellDebug.m └── SUIUtilsDemo ├── .DS_Store ├── SUIUtilsDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── SUIUtilsDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── SUICategoryDemo │ ├── SUICategoryDemo.storyboard │ ├── SUICategoryRootVC.h │ ├── SUICategoryRootVC.m │ ├── SUICategorySecondCell.h │ ├── SUICategorySecondCell.m │ ├── SUICategorySecondCell.xib │ ├── SUICategorySecondVC.h │ └── SUICategorySecondVC.m ├── SUIToolDemo │ └── SUIToolDemo.storyboard └── main.m └── SUIUtilsDemoTests ├── Info.plist └── SUIUtilsDemoTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | 28 | # CocoaPods 29 | # 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 33 | # 34 | # Pods/ 35 | 36 | # Carthage 37 | # 38 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 39 | # Carthage/Checkouts 40 | 41 | Carthage/Build 42 | 43 | # fastlane 44 | # 45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 46 | # screenshots whenever they are needed. 47 | # For more information about the recommended setup visit: 48 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 49 | 50 | fastlane/report.xml 51 | fastlane/screenshots 52 | 53 | *.DS_Store 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 randomprocess 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SUIUtils 2 | 3 | A collection of useful Objective-C utilities. 4 | 5 | 6 | # License 7 | 8 | SUIUtils is available under the MIT license. See the LICENSE file for more info. 9 | -------------------------------------------------------------------------------- /SUIUtils.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | 4 | s.name = 'SUIUtils' 5 | s.version = '0.0.2' 6 | s.platform = :ios, '7.0' 7 | s.summary = 'A collection of convenient classes for iOS.' 8 | 9 | s.license = 'MIT' 10 | s.homepage = 'https://github.com/randomprocess/SUIUtils' 11 | s.author = { 'RandomSuio' => 'randomprocess@qq.com' } 12 | s.source = { :git => 'https://github.com/randomprocess/SUIUtils.git', 13 | :tag => s.version.to_s } 14 | 15 | s.requires_arc = true 16 | 17 | s.public_header_files = 'SUIUtils/**/*.h' 18 | s.source_files = 'SUIUtils/SUIUtils.h' 19 | 20 | s.frameworks = 'UIKit', 'Foundation', 'CoreGraphics', 'QuartzCore' 21 | 22 | s.subspec 'Tool' do |ss| 23 | ss.source_files = 'SUIUtils/Tool/*.{h,m}' 24 | end 25 | 26 | s.subspec 'Foundation' do |ss| 27 | ss.dependency 'SUIUtils/Tool' 28 | ss.source_files = 'SUIUtils/Foundation/*.{h,m}' 29 | end 30 | 31 | s.subspec 'UIKit' do |ss| 32 | ss.dependency 'SUIUtils/Foundation' 33 | ss.source_files = 'SUIUtils/UIKit/*.{h,m}' 34 | end 35 | 36 | s.subspec 'Helper' do |ss| 37 | ss.dependency 'SUIUtils/UIKit' 38 | ss.dependency 'UITableView+FDTemplateLayoutCell', '~> 1.4' 39 | ss.source_files = 'SUIUtils/Helper/*.{h,m}' 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSArray+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSArray (SUIAdditions) 14 | 15 | 16 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 17 | * Prehash 18 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 19 | 20 | #pragma mark - Prehash 21 | 22 | @property (nullable,readonly,copy) NSString *sui_toString; 23 | 24 | 25 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 26 | * Operate 27 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 28 | 29 | #pragma mark - Operate 30 | 31 | - (void)sui_each:(void (^)(ObjectType obj, NSUInteger index))cb; 32 | 33 | - (void)sui_eachReverse:(void (^)(ObjectType obj, NSUInteger index))cb; 34 | 35 | - (void)sui_eachWithStop:(BOOL (^)(ObjectType obj, NSUInteger index))cb; 36 | 37 | - (void)sui_eachReverseWithStop:(BOOL (^)(ObjectType obj, NSUInteger index))cb; 38 | 39 | - (instancetype)sui_map:(id (^)(ObjectType obj, NSUInteger index))cb; 40 | 41 | - (instancetype)sui_filter:(BOOL (^)(ObjectType obj))cb; 42 | 43 | - (instancetype)sui_filterInArray:(NSArray *)array; 44 | 45 | - (instancetype)sui_filterNotInArray:(NSArray *)array; 46 | 47 | - (instancetype)sui_merge:(NSArray *)array; 48 | 49 | 50 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 51 | * Sequence 52 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 53 | 54 | #pragma mark - Sequence 55 | 56 | - (nullable id)sui_randomObject; 57 | 58 | - (instancetype)sui_shuffledArray; 59 | 60 | - (instancetype)sui_reverseObject; 61 | 62 | 63 | @end 64 | 65 | 66 | 67 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 68 | * NSMutableArray 69 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 70 | 71 | @interface NSMutableArray (SUIAdditions) 72 | 73 | 74 | - (void)sui_moveObjectFromIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex; 75 | 76 | 77 | @end 78 | 79 | NS_ASSUME_NONNULL_END 80 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSArray+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSArray+SUIAdditions.h" 10 | #import "SUIMacro.h" 11 | 12 | @implementation NSArray (SUIAdditions) 13 | 14 | 15 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 16 | * Prehash 17 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 18 | 19 | #pragma mark - Prehash 20 | 21 | - (NSString *)sui_toString 22 | { 23 | if ([NSJSONSerialization isValidJSONObject:self]) { 24 | NSError *anyError = nil; 25 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&anyError]; 26 | if (anyError) { 27 | SUILogError(@"array to string Error ⤭ %@ ⤪", anyError); 28 | return nil; 29 | } 30 | NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 31 | return json; 32 | } else { 33 | SUILogError(@"array to string invalid Array ⤭ %@ ⤪", self); 34 | } 35 | return nil; 36 | } 37 | 38 | 39 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 40 | * Operate 41 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 42 | 43 | #pragma mark - Operate 44 | 45 | - (void)sui_each:(void (^)(id _Nonnull, NSUInteger))cb 46 | { 47 | if (self.count == 0) return; 48 | [self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 49 | cb(obj, idx); 50 | }]; 51 | } 52 | 53 | - (void)sui_eachReverse:(void (^)(id _Nonnull, NSUInteger))cb 54 | { 55 | if (self.count == 0) return; 56 | [self enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 57 | cb(obj, idx); 58 | }]; 59 | } 60 | 61 | - (void)sui_eachWithStop:(BOOL (^)(id _Nonnull, NSUInteger))cb 62 | { 63 | if (self.count == 0) return; 64 | [self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 65 | BOOL toStop = cb(obj, idx); 66 | if (toStop) { 67 | *stop = YES; 68 | } 69 | }]; 70 | } 71 | 72 | - (void)sui_eachReverseWithStop:(BOOL (^)(id _Nonnull, NSUInteger))cb 73 | { 74 | if (self.count == 0) return; 75 | [self enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 76 | BOOL toStop = cb(obj, idx); 77 | if (toStop) { 78 | *stop = YES; 79 | } 80 | }]; 81 | } 82 | 83 | - (instancetype)sui_map:(id _Nonnull (^)(id _Nonnull, NSUInteger))cb 84 | { 85 | if (self.count == 0) return @[]; 86 | NSMutableArray *curAry = [NSMutableArray arrayWithCapacity:self.count]; 87 | [self sui_each:^(id _Nonnull obj, NSUInteger index) { 88 | id returnValue = cb(obj, index); 89 | if (!kNilOrNull(returnValue)) { 90 | [curAry addObject:returnValue]; 91 | } 92 | }]; 93 | return curAry; 94 | } 95 | 96 | - (instancetype)sui_filter:(BOOL (^)(id _Nonnull))cb 97 | { 98 | if (self.count == 0) return @[]; 99 | NSPredicate *curPredicate = [NSPredicate predicateWithBlock:^BOOL(id _Nonnull evaluatedObject, NSDictionary * _Nullable bindings) { 100 | return cb(evaluatedObject); 101 | }]; 102 | NSArray *curAry = [self filteredArrayUsingPredicate:curPredicate]; 103 | return curAry; 104 | } 105 | 106 | - (instancetype)sui_filterInArray:(NSArray *)array 107 | { 108 | if (self.count == 0) return @[]; 109 | NSPredicate *curPredicate = gPredicate(@"SELF in %@", array); 110 | NSArray *curAry = [self filteredArrayUsingPredicate:curPredicate]; 111 | return curAry; 112 | } 113 | 114 | // @[@(1), @(2), @(4)] -- @[@(1), @(3)] --> @[@(2), @(4)] 115 | - (instancetype)sui_filterNotInArray:(NSArray *)array 116 | { 117 | if (self.count == 0) return @[]; 118 | NSPredicate *curPredicate = gPredicate(@"NOT (SELF in %@)", array); 119 | NSArray *curAry = [self filteredArrayUsingPredicate:curPredicate]; 120 | return curAry; 121 | } 122 | 123 | - (instancetype)sui_merge:(NSArray *)array 124 | { 125 | NSMutableArray *curAry = [NSMutableArray arrayWithArray:self]; 126 | [curAry addObjectsFromArray:array]; 127 | return curAry; 128 | } 129 | 130 | 131 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 132 | * Sequence 133 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 134 | 135 | #pragma mark - Sequence 136 | 137 | 138 | - (id)sui_randomObject 139 | { 140 | if (self.count == 0) return nil; 141 | NSInteger curIdx = gRandomInRange(0, self.count-1); 142 | id curObj = self[curIdx]; 143 | return curObj; 144 | } 145 | 146 | - (instancetype)sui_shuffledArray 147 | { 148 | if (self.count == 0) return @[]; 149 | NSMutableArray *curAry = [self mutableCopy]; 150 | for (NSInteger idx = self.count - 1; idx > 0; idx--) { 151 | [curAry exchangeObjectAtIndex:arc4random_uniform((u_int32_t)idx + 1) withObjectAtIndex:idx]; 152 | } 153 | return curAry; 154 | } 155 | 156 | - (instancetype)sui_reverseObject 157 | { 158 | if (self.count == 0) return @[]; 159 | NSEnumerator *curEnumer = [self reverseObjectEnumerator]; 160 | NSArray *curAry = [[NSMutableArray alloc] initWithArray:[curEnumer allObjects]]; 161 | return curAry; 162 | } 163 | 164 | 165 | @end 166 | 167 | 168 | 169 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 170 | * NSMutableArray 171 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 172 | 173 | @implementation NSMutableArray (SUIAdditions) 174 | 175 | 176 | - (void)sui_moveObjectFromIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex 177 | { 178 | if (toIndex != fromIndex && fromIndex < [self count] && toIndex< [self count]) { 179 | id obj = [self objectAtIndex:fromIndex]; 180 | [self removeObjectAtIndex:fromIndex]; 181 | if (toIndex >= [self count]) { 182 | [self addObject:obj]; 183 | } else { 184 | [self insertObject:obj atIndex:toIndex]; 185 | } 186 | } 187 | } 188 | 189 | 190 | @end 191 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSArray+SUISafeAccess.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+SUISafeAccess.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSArray (SUISafeAccess) 15 | 16 | 17 | - (nullable id)sui_objectWithIndex:(NSUInteger)cIndex; 18 | 19 | - (nullable NSString *)sui_stringWithIndex:(NSUInteger)cIndex; 20 | 21 | - (nullable NSNumber *)sui_numberWithIndex:(NSUInteger)cIndex; 22 | 23 | - (nullable NSArray *)sui_arrayWithIndex:(NSUInteger)cIndex; 24 | 25 | - (nullable NSMutableArray *)sui_mutableArrayWithIndex:(NSUInteger)cIndex; 26 | 27 | - (nullable NSDictionary *)sui_dictionaryWithIndex:(NSUInteger)cIndex; 28 | 29 | - (nullable NSMutableDictionary *)sui_mutableDictionaryWithIndex:(NSUInteger)cIndex; 30 | 31 | - (NSInteger)sui_integerWithIndex:(NSUInteger)cIndex; 32 | 33 | - (NSUInteger)sui_unsignedIntegerWithIndex:(NSUInteger)cIndex; 34 | 35 | - (BOOL)sui_boolWithIndex:(NSUInteger)cIndex; 36 | 37 | - (float)sui_floatWithIndex:(NSUInteger)cIndex; 38 | 39 | - (double)sui_doubleWithIndex:(NSUInteger)cIndex; 40 | 41 | - (CGFloat)sui_CGFloatWithIndex:(NSUInteger)cIndex; 42 | 43 | - (CGPoint)sui_pointWithIndex:(NSUInteger)cIndex; 44 | 45 | - (CGSize)sui_sizeWithIndex:(NSUInteger)cIndex; 46 | 47 | - (CGRect)sui_rectWithIndex:(NSUInteger)cIndex; 48 | 49 | 50 | @end 51 | 52 | 53 | 54 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 55 | * NSMutableArray 56 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 57 | 58 | @interface NSMutableArray (SUISafeAccess) 59 | 60 | 61 | - (void)sui_addObj:(id)a; 62 | 63 | - (void)sui_addInteger:(NSInteger)a; 64 | 65 | - (void)sui_addUnsignedInteger:(NSUInteger)a; 66 | 67 | - (void)sui_addBool:(BOOL)a; 68 | 69 | - (void)sui_addFloat:(float)a; 70 | 71 | - (void)sui_addDouble:(double)a; 72 | 73 | - (void)sui_addCGFloat:(CGFloat)a; 74 | 75 | - (void)sui_addPoint:(CGPoint)a; 76 | 77 | - (void)sui_addSize:(CGSize)a; 78 | 79 | - (void)sui_addRect:(CGRect)a; 80 | 81 | 82 | @end 83 | 84 | NS_ASSUME_NONNULL_END 85 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSArray+SUISafeAccess.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+SUISafeAccess.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSArray+SUISafeAccess.h" 10 | #import "SUIMacro.h" 11 | #import "NSString+SUIAdditions.h" 12 | #import "NSNumber+SUIAdditions.h" 13 | 14 | @implementation NSArray (SUISafeAccess) 15 | 16 | 17 | - (id)sui_objectWithIndex:(NSUInteger)cIndex 18 | { 19 | if (cIndex < self.count) { 20 | return self[cIndex]; 21 | } 22 | return nil; 23 | } 24 | 25 | - (NSString *)sui_stringWithIndex:(NSUInteger)cIndex 26 | { 27 | id curValue = [self sui_objectWithIndex:cIndex]; 28 | if (!kNilOrNull(curValue)) { 29 | NSString *curString = [NSString sui_stringFromObject:curValue]; 30 | return curString; 31 | } 32 | return nil; 33 | } 34 | 35 | - (NSNumber *)sui_numberWithIndex:(NSUInteger)cIndex 36 | { 37 | id curValue = [self sui_objectWithIndex:cIndex]; 38 | if (!kNilOrNull(curValue)) { 39 | if ([curValue isKindOfClass:[NSNumber class]]) { 40 | return curValue; 41 | } 42 | if ([curValue isKindOfClass:[NSString class]]) { 43 | NSNumber *curNumber = ((NSString *)curValue).sui_toNumber; 44 | return curNumber; 45 | } 46 | } 47 | return nil; 48 | } 49 | 50 | - (NSArray *)sui_arrayWithIndex:(NSUInteger)cIndex 51 | { 52 | id curValue = [self sui_objectWithIndex:cIndex]; 53 | if (!kNilOrNull(curValue)) { 54 | if ([curValue isKindOfClass:[NSArray class]]) { 55 | return curValue; 56 | } 57 | } 58 | return nil; 59 | } 60 | 61 | - (NSMutableArray *)sui_mutableArrayWithIndex:(NSUInteger)cIndex 62 | { 63 | id curValue = [self sui_objectWithIndex:cIndex]; 64 | if (!kNilOrNull(curValue)) { 65 | if ([curValue isKindOfClass:[NSMutableArray class]]) { 66 | return curValue; 67 | } 68 | } 69 | return nil; 70 | } 71 | 72 | - (NSDictionary *)sui_dictionaryWithIndex:(NSUInteger)cIndex 73 | { 74 | id curValue = [self sui_objectWithIndex:cIndex]; 75 | if (!kNilOrNull(curValue)) { 76 | if ([curValue isKindOfClass:[NSDictionary class]]) { 77 | return curValue; 78 | } 79 | } 80 | return nil; 81 | } 82 | 83 | - (NSMutableDictionary *)sui_mutableDictionaryWithIndex:(NSUInteger)cIndex 84 | { 85 | id curValue = [self sui_objectWithIndex:cIndex]; 86 | if (!kNilOrNull(curValue)) { 87 | if ([curValue isKindOfClass:[NSMutableDictionary class]]) { 88 | return curValue; 89 | } 90 | } 91 | return nil; 92 | } 93 | 94 | - (NSInteger)sui_integerWithIndex:(NSUInteger)cIndex 95 | { 96 | id curValue = [self sui_objectWithIndex:cIndex]; 97 | if (!kNilOrNull(curValue)) { 98 | if ([curValue isKindOfClass:[NSString class]] || 99 | [curValue isKindOfClass:[NSNumber class]]) { 100 | NSInteger curInteger = [curValue integerValue]; 101 | return curInteger; 102 | } 103 | } 104 | return 0; 105 | } 106 | 107 | - (NSUInteger)sui_unsignedIntegerWithIndex:(NSUInteger)cIndex 108 | { 109 | id curValue = [self sui_objectWithIndex:cIndex]; 110 | if (!kNilOrNull(curValue)) { 111 | if ([curValue isKindOfClass:[NSString class]] || 112 | [curValue isKindOfClass:[NSNumber class]]) { 113 | NSUInteger curUInteger = [curValue unsignedIntegerValue]; 114 | return curUInteger; 115 | } 116 | } 117 | return 0; 118 | } 119 | 120 | - (BOOL)sui_boolWithIndex:(NSUInteger)cIndex 121 | { 122 | id curValue = [self sui_objectWithIndex:cIndex]; 123 | if (!kNilOrNull(curValue)) { 124 | if ([curValue isKindOfClass:[NSString class]] || 125 | [curValue isKindOfClass:[NSNumber class]]) { 126 | BOOL curRet = [curValue boolValue]; 127 | return curRet; 128 | } 129 | } 130 | return NO; 131 | } 132 | 133 | - (float)sui_floatWithIndex:(NSUInteger)cIndex 134 | { 135 | id curValue = [self sui_objectWithIndex:cIndex]; 136 | if (!kNilOrNull(curValue)) { 137 | if ([curValue isKindOfClass:[NSString class]] || 138 | [curValue isKindOfClass:[NSNumber class]]) { 139 | float curFloat = [curValue floatValue]; 140 | return curFloat; 141 | } 142 | } 143 | return 0; 144 | } 145 | 146 | - (double)sui_doubleWithIndex:(NSUInteger)cIndex 147 | { 148 | id curValue = [self sui_objectWithIndex:cIndex]; 149 | if (!kNilOrNull(curValue)) { 150 | if ([curValue isKindOfClass:[NSString class]] || 151 | [curValue isKindOfClass:[NSNumber class]]) { 152 | double curDouble = [curValue doubleValue]; 153 | return curDouble; 154 | } 155 | } 156 | return 0; 157 | } 158 | 159 | - (CGFloat)sui_CGFloatWithIndex:(NSUInteger)cIndex 160 | { 161 | id curValue = [self sui_objectWithIndex:cIndex]; 162 | if (!kNilOrNull(curValue)) { 163 | if ([curValue isKindOfClass:[NSString class]]) { 164 | NSNumber *curNumber = ((NSString *)curValue).sui_toNumber; 165 | CGFloat curFloat = curNumber.sui_CGFloatValue; 166 | return curFloat; 167 | } else if ([curValue isKindOfClass:[NSNumber class]]) { 168 | CGFloat curFloat = ((NSNumber *)curValue).sui_CGFloatValue; 169 | return curFloat; 170 | } 171 | } 172 | return 0; 173 | } 174 | 175 | - (CGPoint)sui_pointWithIndex:(NSUInteger)cIndex 176 | { 177 | id curValue = [self sui_objectWithIndex:cIndex]; 178 | if (!kNilOrNull(curValue)) { 179 | if ([curValue isKindOfClass:[NSString class]]) { 180 | CGPoint curPoint = CGPointFromString(curValue); 181 | return curPoint; 182 | } 183 | } 184 | return CGPointZero; 185 | } 186 | 187 | - (CGSize)sui_sizeWithIndex:(NSUInteger)cIndex 188 | { 189 | id curValue = [self sui_objectWithIndex:cIndex]; 190 | if (!kNilOrNull(curValue)) { 191 | if ([curValue isKindOfClass:[NSString class]]) { 192 | CGSize curSize = CGSizeFromString(curValue); 193 | return curSize; 194 | } 195 | } 196 | return CGSizeZero; 197 | } 198 | 199 | - (CGRect)sui_rectWithIndex:(NSUInteger)cIndex 200 | { 201 | id curValue = [self sui_objectWithIndex:cIndex]; 202 | if (!kNilOrNull(curValue)) { 203 | if ([curValue isKindOfClass:[NSString class]]) { 204 | CGRect curRect = CGRectFromString(curValue); 205 | return curRect; 206 | } 207 | } 208 | return CGRectZero; 209 | } 210 | 211 | 212 | @end 213 | 214 | 215 | 216 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 217 | * NSMutableArray 218 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 219 | 220 | @implementation NSMutableArray (SUISafeAccess) 221 | 222 | 223 | - (void)sui_addObj:(id)a 224 | { 225 | if (a != nil) { 226 | [self addObject:a]; 227 | } 228 | } 229 | 230 | - (void)sui_addInteger:(NSInteger)a 231 | { 232 | [self addObject:@(a)]; 233 | } 234 | 235 | - (void)sui_addUnsignedInteger:(NSUInteger)a 236 | { 237 | [self addObject:@(a)]; 238 | } 239 | 240 | - (void)sui_addBool:(BOOL)a 241 | { 242 | [self addObject:@(a)]; 243 | } 244 | 245 | - (void)sui_addFloat:(float)a 246 | { 247 | [self addObject:@(a)]; 248 | } 249 | 250 | - (void)sui_addDouble:(double)a 251 | { 252 | [self addObject:@(a)]; 253 | } 254 | 255 | - (void)sui_addCGFloat:(CGFloat)a 256 | { 257 | [self addObject:@(a)]; 258 | } 259 | 260 | - (void)sui_addPoint:(CGPoint)a 261 | { 262 | [self addObject:NSStringFromCGPoint(a)]; 263 | } 264 | 265 | - (void)sui_addSize:(CGSize)a 266 | { 267 | [self addObject:NSStringFromCGSize(a)]; 268 | } 269 | 270 | - (void)sui_addRect:(CGRect)a 271 | { 272 | [self addObject:NSStringFromCGRect(a)]; 273 | } 274 | 275 | 276 | @end 277 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSData+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSData (SUIAdditions) 14 | 15 | 16 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 17 | * Prehash 18 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 19 | 20 | #pragma mark - Prehash 21 | 22 | @property (readonly,copy) NSString *sui_toUTF8String; 23 | 24 | 25 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 26 | * Base64 27 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 28 | 29 | #pragma mark - Base64 30 | 31 | @property (nullable,readonly,copy) NSString *sui_base64Encode; 32 | 33 | 34 | @end 35 | 36 | NS_ASSUME_NONNULL_END 37 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSData+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSData+SUIAdditions.h" 10 | 11 | @implementation NSData (SUIAdditions) 12 | 13 | 14 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 15 | * Prehash 16 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 17 | 18 | #pragma mark - Prehash 19 | 20 | - (NSString *)sui_toUTF8String 21 | { 22 | NSString *curString = [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding]; 23 | return curString; 24 | } 25 | 26 | 27 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 28 | * Base64 29 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 30 | 31 | #pragma mark - Base64 32 | 33 | - (NSString *)sui_base64Encode 34 | { 35 | if (self.length == 0) return nil; 36 | NSString *curStr = [self base64EncodedStringWithOptions:0]; 37 | return curStr; 38 | } 39 | 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSDate+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSDate (SUIAdditions) 14 | 15 | 16 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 17 | * Prehash 18 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 19 | 20 | #pragma mark - Prehash 21 | 22 | @property (readonly) NSTimeInterval sui_toTime; 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | @end 39 | 40 | NS_ASSUME_NONNULL_END 41 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSDate+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSDate+SUIAdditions.h" 10 | 11 | @implementation NSDate (SUIAdditions) 12 | 13 | 14 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 15 | * Prehash 16 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 17 | 18 | #pragma mark - Prehash 19 | 20 | - (NSTimeInterval)sui_toTime 21 | { 22 | return [self timeIntervalSince1970]; 23 | } 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSDictionary+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSDictionary<__covariant KeyType, __covariant ObjectType> (SUIAdditions) 14 | 15 | 16 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 17 | * Prehash 18 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 19 | 20 | #pragma mark - Prehash 21 | 22 | @property (nullable,readonly,copy) NSString *sui_toString; 23 | 24 | 25 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 26 | * Operate 27 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 28 | 29 | #pragma mark - Operate 30 | 31 | - (BOOL)sui_hasKey:(id)cKey; 32 | 33 | - (void)sui_each:(void (^)(KeyType key, ObjectType obj))cb; 34 | 35 | - (void)sui_eachWithStop:(BOOL (^)(KeyType key, ObjectType obj))cb; 36 | 37 | - (instancetype)sui_map:(id (^)(KeyType key, ObjectType obj))cb; 38 | 39 | - (instancetype)sui_pick:(NSArray *)cKeys; 40 | 41 | - (instancetype)sui_Omit:(NSArray *)cKeys; 42 | 43 | - (instancetype)sui_merge:(NSDictionary *)dictionary; 44 | 45 | 46 | @end 47 | 48 | NS_ASSUME_NONNULL_END 49 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSDictionary+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+SUIAdditions.h" 10 | #import "SUIMacro.h" 11 | 12 | @implementation NSDictionary (SUIAdditions) 13 | 14 | 15 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 16 | * Prehash 17 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 18 | 19 | #pragma mark - Prehash 20 | 21 | - (NSString *)sui_toString 22 | { 23 | if ([NSJSONSerialization isValidJSONObject:self]) { 24 | NSError *anyError = nil; 25 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&anyError]; 26 | if (anyError) { 27 | SUILogError(@"dict to string Error ⤭ %@ ⤪", anyError); 28 | return nil; 29 | } 30 | NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 31 | return json; 32 | } else { 33 | SUILogError(@"dict to string invalid Array ⤭ %@ ⤪", self); 34 | } 35 | return nil; 36 | } 37 | 38 | 39 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 40 | * Operate 41 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 42 | 43 | #pragma mark - Operate 44 | 45 | - (BOOL)sui_hasKey:(id)cKey 46 | { 47 | id curValue = [self objectForKey:cKey]; 48 | if (!kNilOrNull(curValue)) { 49 | return YES; 50 | } 51 | return NO; 52 | } 53 | 54 | - (void)sui_each:(void (^)(id _Nonnull, id _Nonnull))cb 55 | { 56 | if (self.count == 0) return; 57 | [self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { 58 | cb(key, obj); 59 | }]; 60 | } 61 | 62 | - (void)sui_eachWithStop:(BOOL (^)(id _Nonnull, id _Nonnull))cb 63 | { 64 | if (self.count == 0) return; 65 | [self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { 66 | BOOL toStop = cb(key, obj); 67 | if (toStop) { 68 | *stop = YES; 69 | } 70 | }]; 71 | } 72 | 73 | - (instancetype)sui_map:(id _Nonnull (^)(id _Nonnull, id _Nonnull))cb 74 | { 75 | if (self.count == 0) return @{}; 76 | NSMutableDictionary *curDict = [NSMutableDictionary dictionaryWithCapacity:self.count]; 77 | [self sui_each:^(id _Nonnull key, id _Nonnull obj) { 78 | id returnValue = cb(key, obj); 79 | if (!kNilOrNull(returnValue)) { 80 | [curDict setObject:returnValue forKey:key]; 81 | } 82 | }]; 83 | return curDict; 84 | } 85 | 86 | - (instancetype)sui_pick:(NSArray *)cKeys 87 | { 88 | if (cKeys.count == 0) return @{}; 89 | NSMutableDictionary *curDict = [NSMutableDictionary dictionary]; 90 | [self sui_each:^(id _Nonnull key, id _Nonnull obj) { 91 | if ([cKeys containsObject:key]) { 92 | curDict[key] = obj; 93 | } 94 | }]; 95 | return curDict; 96 | } 97 | 98 | - (instancetype)sui_Omit:(NSArray *)cKeys 99 | { 100 | if (self.count == 0) return @{}; 101 | NSMutableDictionary *curDict = [NSMutableDictionary dictionary]; 102 | [self sui_each:^(id _Nonnull key, id _Nonnull obj) { 103 | if (![cKeys containsObject:key]) { 104 | curDict[key] = obj; 105 | } 106 | }]; 107 | return curDict; 108 | } 109 | 110 | - (instancetype)sui_merge:(NSDictionary *)dictionary 111 | { 112 | NSMutableDictionary *curDict = [self mutableCopy]; 113 | [curDict addEntriesFromDictionary:dictionary]; 114 | return curDict; 115 | } 116 | 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSDictionary+SUISafeAccess.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+SUISafeAccess.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSDictionary (SUISafeAccess) 15 | 16 | 17 | - (nullable id)sui_objectForKey:(id)cKey; 18 | 19 | - (nullable NSString *)sui_stringForKey:(id)cKey; 20 | 21 | - (nullable NSNumber *)sui_numberForKey:(id)cKey; 22 | 23 | - (nullable NSArray *)sui_arrayForKey:(id)cKey; 24 | 25 | - (nullable NSMutableArray *)sui_mutableArrayForKey:(id)cKey; 26 | 27 | - (nullable NSDictionary *)sui_dictionaryForKey:(id)cKey; 28 | 29 | - (nullable NSMutableDictionary *)sui_mutableDictionaryForKey:(id)cKey; 30 | 31 | - (NSInteger)sui_integerForKey:(id)cKey; 32 | 33 | - (NSUInteger)sui_unsignedIntegerForKey:(id)cKey; 34 | 35 | - (BOOL)sui_boolForKey:(id)cKey; 36 | 37 | - (float)sui_floatForKey:(id)cKey; 38 | 39 | - (double)sui_doubleForKey:(id)cKey; 40 | 41 | - (CGFloat)sui_CGFloatForKey:(id)cKey; 42 | 43 | - (CGPoint)sui_pointForKey:(id)cKey; 44 | 45 | - (CGSize)sui_sizeForKey:(id)cKey; 46 | 47 | - (CGRect)sui_rectForKey:(id)cKey; 48 | 49 | 50 | @end 51 | 52 | 53 | 54 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 55 | * NSMutableDictionary 56 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 57 | 58 | @interface NSMutableDictionary(SUISafeAccess) 59 | 60 | 61 | - (void)sui_setObj:(id)a forKey:(NSString *)cKey; 62 | 63 | - (void)sui_setInteger:(NSInteger)a forKey:(NSString *)cKey; 64 | 65 | - (void)sui_setUnsignedInteger:(NSUInteger)a forKey:(NSString *)cKey; 66 | 67 | - (void)sui_setBool:(BOOL)a forKey:(NSString *)cKey; 68 | 69 | - (void)sui_setFloat:(float)a forKey:(NSString *)cKey; 70 | 71 | - (void)sui_setDouble:(double)a forKey:(NSString *)cKey; 72 | 73 | - (void)sui_setCGFloat:(CGFloat)a forKey:(NSString *)cKey; 74 | 75 | - (void)sui_setPoint:(CGPoint)a forKey:(NSString *)cKey; 76 | 77 | - (void)sui_setSize:(CGSize)a forKey:(NSString *)cKey; 78 | 79 | - (void)sui_setRect:(CGRect)a forKey:(NSString *)cKey; 80 | 81 | 82 | @end 83 | 84 | NS_ASSUME_NONNULL_END 85 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSDictionary+SUISafeAccess.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+SUISafeAccess.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+SUISafeAccess.h" 10 | #import "SUIMacro.h" 11 | #import "NSString+SUIAdditions.h" 12 | #import "NSNumber+SUIAdditions.h" 13 | 14 | @implementation NSDictionary (SUISafeAccess) 15 | 16 | 17 | - (id)sui_objectForKey:(id)cKey 18 | { 19 | id curValue = [self objectForKey:cKey]; 20 | if (!kNilOrNull(curValue)) { 21 | return curValue; 22 | } 23 | return nil; 24 | } 25 | 26 | - (NSString *)sui_stringForKey:(id)cKey 27 | { 28 | id curValue = [self sui_objectForKey:cKey]; 29 | if (curValue) { 30 | NSString *curString = [NSString sui_stringFromObject:curValue]; 31 | return curString; 32 | } 33 | return nil; 34 | } 35 | 36 | - (NSNumber *)sui_numberForKey:(id)cKey 37 | { 38 | id curValue = [self sui_objectForKey:cKey]; 39 | if (curValue) { 40 | if ([curValue isKindOfClass:[NSNumber class]]) { 41 | return curValue; 42 | } 43 | if ([curValue isKindOfClass:[NSString class]]) { 44 | NSNumber *curNumber = ((NSString *)curValue).sui_toNumber; 45 | return curNumber; 46 | } 47 | } 48 | return nil; 49 | } 50 | 51 | - (NSArray *)sui_arrayForKey:(id)cKey 52 | { 53 | id curValue = [self sui_objectForKey:cKey]; 54 | if (curValue) { 55 | if ([curValue isKindOfClass:[NSArray class]]) { 56 | return curValue; 57 | } 58 | } 59 | return nil; 60 | } 61 | 62 | - (NSMutableArray *)sui_mutableArrayForKey:(id)cKey 63 | { 64 | id curValue = [self sui_objectForKey:cKey]; 65 | if (curValue) { 66 | if ([curValue isKindOfClass:[NSMutableArray class]]) { 67 | return curValue; 68 | } 69 | } 70 | return nil; 71 | } 72 | 73 | - (NSDictionary *)sui_dictionaryForKey:(id)cKey 74 | { 75 | id curValue = [self sui_objectForKey:cKey]; 76 | if (curValue) { 77 | if ([curValue isKindOfClass:[NSDictionary class]]) { 78 | return curValue; 79 | } 80 | } 81 | return nil; 82 | } 83 | 84 | - (NSMutableDictionary *)sui_mutableDictionaryForKey:(id)cKey 85 | { 86 | id curValue = [self sui_objectForKey:cKey]; 87 | if (curValue) { 88 | if ([curValue isKindOfClass:[NSMutableDictionary class]]) { 89 | return curValue; 90 | } 91 | } 92 | return nil; 93 | } 94 | 95 | - (NSInteger)sui_integerForKey:(id)cKey 96 | { 97 | id curValue = [self sui_objectForKey:cKey]; 98 | if (curValue) { 99 | if ([curValue isKindOfClass:[NSString class]] || 100 | [curValue isKindOfClass:[NSNumber class]]) { 101 | NSInteger curInteger = [curValue integerValue]; 102 | return curInteger; 103 | } 104 | } 105 | return 0; 106 | } 107 | 108 | - (NSUInteger)sui_unsignedIntegerForKey:(id)cKey 109 | { 110 | id curValue = [self sui_objectForKey:cKey]; 111 | if (curValue) { 112 | if ([curValue isKindOfClass:[NSString class]] || 113 | [curValue isKindOfClass:[NSNumber class]]) { 114 | NSUInteger curUInteger = [curValue unsignedIntegerValue]; 115 | return curUInteger; 116 | } 117 | } 118 | return 0; 119 | } 120 | 121 | - (BOOL)sui_boolForKey:(id)cKey 122 | { 123 | id curValue = [self sui_objectForKey:cKey]; 124 | if (!kNilOrNull(curValue)) { 125 | if ([curValue isKindOfClass:[NSString class]] || 126 | [curValue isKindOfClass:[NSNumber class]]) { 127 | BOOL curRet = [curValue boolValue]; 128 | return curRet; 129 | } 130 | } 131 | return NO; 132 | } 133 | 134 | - (float)sui_floatForKey:(id)cKey 135 | { 136 | id curValue = [self sui_objectForKey:cKey]; 137 | if (curValue) { 138 | if ([curValue isKindOfClass:[NSString class]] || 139 | [curValue isKindOfClass:[NSNumber class]]) { 140 | float curFloat = [curValue floatValue]; 141 | return curFloat; 142 | } 143 | } 144 | return 0; 145 | } 146 | 147 | - (double)sui_doubleForKey:(id)cKey 148 | { 149 | id curValue = [self sui_objectForKey:cKey]; 150 | if (curValue) { 151 | if ([curValue isKindOfClass:[NSString class]] || 152 | [curValue isKindOfClass:[NSNumber class]]) { 153 | double curDouble = [curValue doubleValue]; 154 | return curDouble; 155 | } 156 | } 157 | return 0; 158 | } 159 | 160 | - (CGFloat)sui_CGFloatForKey:(id)cKey 161 | { 162 | id curValue = [self sui_objectForKey:cKey]; 163 | if (curValue) { 164 | if ([curValue isKindOfClass:[NSString class]]) { 165 | NSNumber *curNumber = ((NSString *)curValue).sui_toNumber; 166 | CGFloat curFloat = curNumber.sui_CGFloatValue; 167 | return curFloat; 168 | } else if ([curValue isKindOfClass:[NSNumber class]]) { 169 | CGFloat curFloat = ((NSNumber *)curValue).sui_CGFloatValue; 170 | return curFloat; 171 | } 172 | } 173 | return 0; 174 | } 175 | 176 | - (CGPoint)sui_pointForKey:(id)cKey 177 | { 178 | id curValue = [self sui_objectForKey:cKey]; 179 | if (curValue) { 180 | if ([curValue isKindOfClass:[NSString class]]) { 181 | CGPoint curPoint = CGPointFromString(curValue); 182 | return curPoint; 183 | } 184 | } 185 | return CGPointZero; 186 | } 187 | 188 | - (CGSize)sui_sizeForKey:(id)cKey 189 | { 190 | id curValue = [self sui_objectForKey:cKey]; 191 | if (curValue) { 192 | if ([curValue isKindOfClass:[NSString class]]) { 193 | CGSize curSize = CGSizeFromString(curValue); 194 | return curSize; 195 | } 196 | } 197 | return CGSizeZero; 198 | } 199 | 200 | - (CGRect)sui_rectForKey:(id)cKey 201 | { 202 | id curValue = [self sui_objectForKey:cKey]; 203 | if (curValue) { 204 | if ([curValue isKindOfClass:[NSString class]]) { 205 | CGRect curRect = CGRectFromString(curValue); 206 | return curRect; 207 | } 208 | } 209 | return CGRectZero; 210 | } 211 | 212 | 213 | @end 214 | 215 | 216 | 217 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 218 | * NSMutableDictionary 219 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 220 | 221 | @implementation NSMutableDictionary (SUISafeAccess) 222 | 223 | 224 | - (void)sui_setObj:(id)a forKey:(NSString *)cKey 225 | { 226 | if (a != nil) { 227 | [self setObject:a forKey:cKey]; 228 | } 229 | } 230 | 231 | - (void)sui_setInteger:(NSInteger)a forKey:(NSString *)cKey 232 | { 233 | [self setObject:@(a) forKey:cKey]; 234 | } 235 | 236 | - (void)sui_setUnsignedInteger:(NSUInteger)a forKey:(NSString *)cKey 237 | { 238 | [self setObject:@(a) forKey:cKey]; 239 | } 240 | 241 | - (void)sui_setBool:(BOOL)a forKey:(NSString *)cKey 242 | { 243 | [self setObject:@(a) forKey:cKey]; 244 | } 245 | 246 | - (void)sui_setFloat:(float)a forKey:(NSString *)cKey 247 | { 248 | [self setObject:@(a) forKey:cKey]; 249 | } 250 | 251 | - (void)sui_setDouble:(double)a forKey:(NSString *)cKey 252 | { 253 | [self setObject:@(a) forKey:cKey]; 254 | } 255 | 256 | - (void)sui_setCGFloat:(CGFloat)a forKey:(NSString *)cKey 257 | { 258 | [self setObject:@(a) forKey:cKey]; 259 | } 260 | 261 | - (void)sui_setPoint:(CGPoint)a forKey:(NSString *)cKey 262 | { 263 | [self setObject:NSStringFromCGPoint(a) forKey:cKey]; 264 | } 265 | 266 | - (void)sui_setSize:(CGSize)a forKey:(NSString *)cKey 267 | { 268 | [self setObject:NSStringFromCGSize(a) forKey:cKey]; 269 | } 270 | 271 | - (void)sui_setRect:(CGRect)a forKey:(NSString *)cKey 272 | { 273 | [self setObject:NSStringFromCGRect(a) forKey:cKey]; 274 | } 275 | 276 | 277 | @end 278 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSIndexPath+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSIndexPath+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSIndexPath (SUIAdditions) 14 | 15 | 16 | - (instancetype)sui_previousRow; 17 | 18 | - (instancetype)sui_nextRow; 19 | 20 | - (instancetype)sui_previousSection; 21 | 22 | - (instancetype)sui_nextSection; 23 | 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSIndexPath+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSIndexPath+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSIndexPath+SUIAdditions.h" 10 | #import 11 | 12 | @implementation NSIndexPath (SUIAdditions) 13 | 14 | 15 | - (instancetype)sui_previousRow 16 | { 17 | NSIndexPath *curIndexPath = [NSIndexPath indexPathForRow:self.row-1 18 | inSection:self.section]; 19 | return curIndexPath; 20 | } 21 | 22 | - (instancetype)sui_nextRow 23 | { 24 | NSIndexPath *curIndexPath = [NSIndexPath indexPathForRow:self.row+1 25 | inSection:self.section]; 26 | return curIndexPath; 27 | } 28 | 29 | - (instancetype)sui_previousSection 30 | { 31 | NSIndexPath *curIndexPath = [NSIndexPath indexPathForRow:self.row 32 | inSection:self.section-1]; 33 | return curIndexPath; 34 | } 35 | 36 | - (instancetype)sui_nextSection 37 | { 38 | NSIndexPath *curIndexPath = [NSIndexPath indexPathForRow:self.row 39 | inSection:self.section+1]; 40 | return curIndexPath; 41 | } 42 | 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSNumber+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSNumber (SUIAdditions) 15 | 16 | 17 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 18 | * Prehash 19 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 20 | 21 | #pragma mark - Prehash 22 | 23 | @property (readonly,copy) NSDate *sui_toDate; 24 | 25 | @property (readonly,copy) NSString *sui_toString; 26 | 27 | 28 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 29 | * FloatValue 30 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 31 | 32 | #pragma mark - CGFloatValue 33 | 34 | @property (readonly) CGFloat sui_CGFloatValue; 35 | 36 | + (instancetype)sui_numberWithCGFloat:(CGFloat)cValue; 37 | 38 | 39 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 40 | * Round 41 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 42 | 43 | #pragma mark - Round 44 | 45 | - (instancetype)sui_roundWithDigit:(NSUInteger)digit; 46 | 47 | - (instancetype)sui_ceilWithDigit:(NSUInteger)digit; 48 | 49 | - (instancetype)sui_floorWithDigit:(NSUInteger)digit; 50 | 51 | 52 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 53 | * RomanNumeral 54 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 55 | 56 | #pragma mark - romanNumeral 57 | 58 | @property (readonly,copy) NSString *sui_toRomanNumeral; 59 | 60 | 61 | @end 62 | 63 | NS_ASSUME_NONNULL_END 64 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSNumber+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSNumber+SUIAdditions.h" 10 | 11 | @implementation NSNumber (SUIAdditions) 12 | 13 | 14 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 15 | * Prehash 16 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 17 | 18 | #pragma mark - Prehash 19 | 20 | - (NSDate *)sui_toDate 21 | { 22 | double curTime = [self doubleValue]; 23 | NSDate *curDate = [NSDate dateWithTimeIntervalSince1970:curTime]; 24 | return curDate; 25 | } 26 | 27 | - (NSString *)sui_toString 28 | { 29 | NSString *curString = [NSString stringWithFormat:@"%@", self]; 30 | return curString; 31 | } 32 | 33 | 34 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 35 | * FloatValue 36 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 37 | 38 | #pragma mark - CGFloatValue 39 | 40 | - (CGFloat)sui_CGFloatValue 41 | { 42 | #if (CGFLOAT_IS_DOUBLE == 1) 43 | CGFloat curValue = [self doubleValue]; 44 | #else 45 | CGFloat curValue = [self floatValue]; 46 | #endif 47 | return curValue; 48 | } 49 | 50 | + (instancetype)sui_numberWithCGFloat:(CGFloat)cValue 51 | { 52 | #if (CGFLOAT_IS_DOUBLE == 1) 53 | NSNumber *curNumber = [[self alloc] initWithDouble:cValue]; 54 | #else 55 | NSNumber *curNumber = [[self alloc] initWithFloat:cValue]; 56 | #endif 57 | return curNumber; 58 | } 59 | 60 | 61 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 62 | * Round 63 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 64 | 65 | #pragma mark - Round 66 | 67 | - (instancetype)sui_roundWithDigit:(NSUInteger)digit 68 | { 69 | NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 70 | [formatter setRoundingMode:NSNumberFormatterRoundHalfUp]; 71 | [formatter setMaximumFractionDigits:digit]; 72 | [formatter setMinimumFractionDigits:digit]; 73 | NSString *curString = [formatter stringFromNumber:self]; 74 | NSNumber *curNumber = [NSNumber numberWithDouble:[curString doubleValue]]; 75 | return curNumber; 76 | } 77 | 78 | - (instancetype)sui_ceilWithDigit:(NSUInteger)digit 79 | { 80 | NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 81 | [formatter setRoundingMode:NSNumberFormatterRoundCeiling]; 82 | [formatter setMaximumFractionDigits:digit]; 83 | NSString *curString = [formatter stringFromNumber:self]; 84 | NSNumber *curNumber = [NSNumber numberWithDouble:[curString doubleValue]]; 85 | return curNumber; 86 | } 87 | 88 | - (instancetype)sui_floorWithDigit:(NSUInteger)digit 89 | { 90 | NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 91 | [formatter setRoundingMode:NSNumberFormatterRoundFloor]; 92 | [formatter setMaximumFractionDigits:digit]; 93 | NSString *curString = [formatter stringFromNumber:self]; 94 | NSNumber *curNumber = [NSNumber numberWithDouble:[curString doubleValue]]; 95 | return curNumber; 96 | } 97 | 98 | 99 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 100 | * RomanNumeral 101 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 102 | 103 | #pragma mark - romanNumeral 104 | 105 | - (NSString *)sui_toRomanNumeral 106 | { 107 | NSArray *numerals = [NSArray arrayWithObjects:@"M", @"CM", @"D", @"CD", @"C", @"XC", @"L", @"XL", @"X", @"IX", @"V", @"IV", @"I", nil]; 108 | NSUInteger valueCount = 13; 109 | NSUInteger values[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; 110 | 111 | NSMutableString *numeralString = [NSMutableString string]; 112 | NSInteger curInteger = [self integerValue]; 113 | for (NSUInteger i = 0; i < valueCount; i++) 114 | { 115 | while (curInteger >= values[i]) 116 | { 117 | curInteger -= values[i]; 118 | [numeralString appendString:[numerals objectAtIndex:i]]; 119 | } 120 | } 121 | return numeralString; 122 | } 123 | 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSObject+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSObject (SUIAdditions) 15 | 16 | 17 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 18 | * AssociatedObject 19 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 20 | 21 | #pragma mark - AssociatedObject 22 | 23 | - (nullable id)sui_getAssociatedObjectWithKey:(const void *)cKey; 24 | 25 | - (void)sui_setAssociatedAssignObject:(nullable id)cValue key:(const void *)cKey; 26 | 27 | - (void)sui_setAssociatedRetainObject:(nullable id)cValue key:(const void *)cKey; 28 | 29 | - (void)sui_setAssociatedCopyObject:(nullable id)cValue key:(const void *)cKey; 30 | 31 | - (void)sui_setAssociatedObject:(nullable id)cValue key:(const void *)cKey policy:(objc_AssociationPolicy)cPolicy; 32 | 33 | 34 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 35 | * PerformedOnce 36 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 37 | 38 | #pragma mark - PerformedOnce 39 | 40 | - (void)sui_performOnce:(void (^)(void))cb key:(NSString *)cKey; 41 | 42 | 43 | @end 44 | 45 | NS_ASSUME_NONNULL_END 46 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSObject+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSObject+SUIAdditions.h" 10 | 11 | @implementation NSObject (SUIAdditions) 12 | 13 | 14 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 15 | * AssociatedObject 16 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 17 | 18 | #pragma mark - AssociatedObject 19 | 20 | - (id)sui_getAssociatedObjectWithKey:(const void *)cKey 21 | { 22 | return objc_getAssociatedObject(self, cKey); 23 | } 24 | 25 | - (void)sui_setAssociatedAssignObject:(id)cValue key:(const void *)cKey 26 | { 27 | objc_setAssociatedObject(self, cKey, cValue, OBJC_ASSOCIATION_ASSIGN); 28 | } 29 | 30 | - (void)sui_setAssociatedRetainObject:(id)cValue key:(const void *)cKey 31 | { 32 | objc_setAssociatedObject(self, cKey, cValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 33 | } 34 | 35 | - (void)sui_setAssociatedCopyObject:(id)cValue key:(const void *)cKey 36 | { 37 | objc_setAssociatedObject(self, cKey, cValue, OBJC_ASSOCIATION_COPY); 38 | } 39 | 40 | - (void)sui_setAssociatedObject:(id)cValue key:(const void *)cKey policy:(objc_AssociationPolicy)cPolicy 41 | { 42 | objc_setAssociatedObject(self, cKey, cValue, cPolicy); 43 | } 44 | 45 | 46 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 47 | * PerformedOnce 48 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 49 | 50 | #pragma mark - PerformedOnce 51 | 52 | - (void)sui_performOnce:(void (^)(void))cb key:(NSString *)cKey 53 | { 54 | NSMutableArray *performedArray = [self sui_performedArray]; 55 | if (![performedArray containsObject:cKey]) 56 | { 57 | [performedArray addObject:cKey]; 58 | cb(); 59 | } 60 | } 61 | 62 | - (NSMutableArray *)sui_performedArray 63 | { 64 | NSMutableArray *curArray = [self sui_getAssociatedObjectWithKey:_cmd]; 65 | if (curArray) return curArray; 66 | 67 | curArray = [NSMutableArray array]; 68 | [self sui_setAssociatedObject:curArray key:_cmd policy:OBJC_ASSOCIATION_RETAIN_NONATOMIC]; 69 | return curArray; 70 | } 71 | 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSString+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSString (SUIAdditions) 15 | 16 | 17 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 18 | * Prehash 19 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 20 | 21 | #pragma mark - Prehash 22 | 23 | @property (nullable,readonly,copy) NSData *sui_toData; 24 | @property (nullable,readonly,copy) NSURL *sui_toURL; 25 | @property (nullable,readonly,copy) NSURLRequest *sui_toURLRequest; 26 | @property (readonly,copy) NSNumber *sui_toNumber; 27 | 28 | 29 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 30 | * Formatter 31 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 32 | 33 | #pragma mark - Formatter 34 | 35 | + (nullable NSString *)sui_stringFromObject:(id)cObject; 36 | 37 | 38 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 39 | * Appending 40 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 41 | 42 | #pragma mark - Appending 43 | 44 | - (NSString *)sui_appendingObject:(id)cObject; 45 | - (NSString *)sui_appendingString:(NSString *)cString; 46 | - (NSString *)sui_appendingFormat:(NSString *)cFormat, ... NS_FORMAT_FUNCTION(1,2); 47 | 48 | 49 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 50 | * Contains 51 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 52 | 53 | #pragma mark - Contains 54 | 55 | - (BOOL)sui_containsObject:(id)cObject; 56 | - (BOOL)sui_containsString:(NSString *)cString; 57 | - (BOOL)sui_isNotEmpty; 58 | - (BOOL)sui_containsChinese; 59 | 60 | 61 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 62 | * Delstr 63 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 64 | 65 | #pragma mark - Delstr 66 | 67 | - (NSString *)sui_delstrBlankInHeadTail; 68 | - (NSString *)sui_delstrBlankAndWrapInHeadTail; 69 | - (NSString *)sui_delstrStringInHeadTail:(NSString *)cString; 70 | - (NSString *)sui_delstrWrapInHeadTail; 71 | 72 | 73 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 74 | * Substr 75 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 76 | 77 | #pragma mark - Substr 78 | 79 | - (NSString *)sui_substrToIndex:(NSUInteger)cIndex; 80 | - (nullable NSString *)sui_substrFromIndex:(NSUInteger)cIndex; 81 | - (nullable NSString *)sui_substrWithRange:(NSRange)cRange; 82 | 83 | 84 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 85 | * Replace 86 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 87 | 88 | #pragma mark - Replace 89 | 90 | - (NSString *)sui_replaceString:(NSString *)cString withString:(NSString *)cReplacement; 91 | - (NSString *)sui_replaceString:(NSString *)cString withString:(NSString *)cReplacement options:(NSStringCompareOptions)cOptions; 92 | - (NSString *)sui_replaceRegex:(NSString *)cRegex withString:(NSString *)cReplacement; 93 | 94 | 95 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 96 | * Resource 97 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 98 | 99 | #pragma mark - Resource 100 | 101 | - (nullable NSString *)sui_resourceNameCompleteOfType:(nullable NSString *)ext; 102 | - (nullable NSString *)sui_resourcePathForMainBundleOfType:(nullable NSString *)ext; 103 | 104 | 105 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 106 | * Size 107 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 108 | 109 | #pragma mark - Size 110 | 111 | - (CGFloat)sui_heightWithFont:(nullable UIFont *)font constrainedToWidth:(CGFloat)width; 112 | - (CGSize)sui_sizeWithFont:(nullable UIFont *)font constrainedToWidth:(CGFloat)width; 113 | 114 | 115 | @end 116 | 117 | NS_ASSUME_NONNULL_END 118 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSString+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSString+SUIAdditions.h" 10 | #import "SUIMacro.h" 11 | #import "NSArray+SUIAdditions.h" 12 | #import "NSDictionary+SUIAdditions.h" 13 | #import "NSString+SUICrypto.h" 14 | #import "NSString+SUIRegex.h" 15 | 16 | @implementation NSString (SUIAdditions) 17 | 18 | 19 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 20 | * Prehash 21 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 22 | 23 | #pragma mark - Prehash 24 | 25 | - (NSData *)sui_toData 26 | { 27 | if (self.length == 0) return nil; 28 | NSData *curData = [self dataUsingEncoding:NSUTF8StringEncoding]; 29 | return curData; 30 | } 31 | 32 | - (NSURL *)sui_toURL 33 | { 34 | if (self.length == 0) return nil; 35 | NSURL *curURL = [NSURL URLWithString:self]; 36 | if (curURL == nil) { 37 | curURL = [NSURL URLWithString:[self sui_URLEncode]]; 38 | } 39 | return curURL; 40 | } 41 | 42 | - (NSURLRequest *)sui_toURLRequest 43 | { 44 | if (self.length == 0) return nil; 45 | NSURLRequest *curURLRequest = [NSURLRequest requestWithURL:self.sui_toURL]; 46 | return curURLRequest; 47 | } 48 | 49 | - (NSNumber *)sui_toNumber 50 | { 51 | NSNumberFormatter *curFormatter = [[NSNumberFormatter alloc] init]; 52 | [curFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; 53 | NSNumber *curNumber = [curFormatter numberFromString:self]; 54 | return curNumber; 55 | } 56 | 57 | 58 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 59 | * Formatter 60 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 61 | 62 | #pragma mark - Formatter 63 | 64 | + (NSString *)sui_stringFromObject:(id)cObject 65 | { 66 | NSString *curStr = nil; 67 | if (kNilOrNull(cObject)) { 68 | SUILogError(@"Object is nil. This may not be what you want."); 69 | } else if ([cObject isKindOfClass:[NSString class]]) { 70 | curStr = cObject; 71 | } else if ([cObject isKindOfClass:[NSNumber class]]) { 72 | NSNumber *curNumber = cObject; 73 | curStr = [curNumber description]; 74 | } else if ([cObject isKindOfClass:[NSURL class]]) { 75 | NSURL *curURL = cObject; 76 | curStr = [curURL absoluteString]; 77 | } else if ([cObject isKindOfClass:[NSArray class]]) { 78 | NSArray *curAry = cObject; 79 | curStr = [curAry sui_toString]; 80 | } else if ([cObject isKindOfClass:[NSDictionary class]]) { 81 | NSDictionary *curDict = cObject; 82 | curStr = [curDict sui_toString]; 83 | } else { 84 | curStr = [cObject description]; 85 | } 86 | return curStr; 87 | } 88 | 89 | 90 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 91 | * Appending 92 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 93 | 94 | #pragma mark - Appending 95 | 96 | - (NSString *)sui_appendingObject:(id)cObject 97 | { 98 | NSString *curStr = [NSString sui_stringFromObject:cObject]; 99 | curStr = [self sui_appendingString:curStr]; 100 | return curStr; 101 | } 102 | - (NSString *)sui_appendingString:(NSString *)cString 103 | { 104 | if (cString.length == 0) return self; 105 | NSString *curStr = [self stringByAppendingString:cString]; 106 | return curStr; 107 | } 108 | - (NSString *)sui_appendingFormat:(NSString *)cFormat, ... 109 | { 110 | va_list args; 111 | va_start(args, cFormat); 112 | NSString *curStr = [[NSString alloc] initWithFormat:cFormat arguments:args]; 113 | va_end(args); 114 | curStr = [self sui_appendingString:curStr]; 115 | return curStr; 116 | } 117 | 118 | 119 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 120 | * Contains 121 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 122 | 123 | #pragma mark - Contains 124 | 125 | - (BOOL)sui_containsObject:(id)cObject 126 | { 127 | NSString *curStr = [NSString sui_stringFromObject:cObject]; 128 | return [self sui_containsString:curStr]; 129 | } 130 | - (BOOL)sui_containsString:(NSString *)cString 131 | { 132 | if (cString.length == 0) return NO; 133 | 134 | BOOL isContains = ([self rangeOfString:cString].location != NSNotFound); 135 | return isContains; 136 | } 137 | - (BOOL)sui_isNotEmpty 138 | { 139 | if (kNilOrNull(self)) return NO; 140 | NSString *curStr = [self sui_regex:@"\\S"]; 141 | if (curStr.length == 0) return NO; 142 | return YES; 143 | } 144 | - (BOOL)sui_containsChinese 145 | { 146 | NSUInteger length = [self length]; 147 | for (NSUInteger i = 0; i < length; i++) { 148 | NSRange range = NSMakeRange(i, 1); 149 | NSString *subString = [self substringWithRange:range]; 150 | const char *cString = [subString UTF8String]; 151 | if (strlen(cString) == 3) { 152 | return YES; 153 | } 154 | } 155 | return NO; 156 | } 157 | 158 | 159 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 160 | * Delstr 161 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 162 | 163 | #pragma mark - Delstr 164 | 165 | - (NSString *)sui_delstrBlankInHeadTail 166 | { 167 | NSString *curStr = [self sui_delstrStringInHeadTail:@" "]; 168 | return curStr; 169 | } 170 | - (NSString *)sui_delstrBlankAndWrapInHeadTail 171 | { 172 | NSString *curBlank = @" "; 173 | NSString *curWrap = @"\n"; 174 | 175 | NSString *curStr = self; 176 | while (1) { 177 | if ([curStr hasPrefix:curBlank]) { 178 | curStr = [curStr substringFromIndex:curBlank.length]; 179 | continue; 180 | } else if ([curStr hasSuffix:curBlank]) { 181 | curStr = [curStr substringToIndex:curStr.length-curBlank.length]; 182 | continue; 183 | } else if ([curStr hasPrefix:curWrap]) { 184 | curStr = [curStr substringFromIndex:curWrap.length]; 185 | continue; 186 | } else if ([curStr hasSuffix:curWrap]) { 187 | curStr = [curStr substringToIndex:curStr.length-curWrap.length]; 188 | continue; 189 | } else { 190 | break; 191 | } 192 | } 193 | return curStr; 194 | } 195 | - (NSString *)sui_delstrStringInHeadTail:(NSString *)cString 196 | { 197 | if (cString.length == 0) return self; 198 | NSString *curStr = self; 199 | while (1) { 200 | if ([curStr hasPrefix:cString]) { 201 | curStr = [curStr substringFromIndex:cString.length]; 202 | continue; 203 | } else if ([curStr hasSuffix:cString]) { 204 | curStr = [curStr substringToIndex:curStr.length-cString.length]; 205 | continue; 206 | } else { 207 | break; 208 | } 209 | } 210 | return curStr; 211 | } 212 | - (NSString *)sui_delstrWrapInHeadTail 213 | { 214 | NSString *curStr = [self sui_delstrStringInHeadTail:@"\n"]; 215 | return curStr; 216 | } 217 | 218 | 219 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 220 | * Substr 221 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 222 | 223 | #pragma mark - Substr 224 | 225 | - (NSString *)sui_substrToIndex:(NSUInteger)cIndex 226 | { 227 | if (self.length <= cIndex) { 228 | SUILogError(@"substrTo Str ⤭ %@ ⤪ length <= Index ⤭ %zd ⤪", self, cIndex); 229 | return self; 230 | } 231 | return [self substringToIndex:cIndex]; 232 | } 233 | - (NSString *)sui_substrFromIndex:(NSUInteger)cIndex 234 | { 235 | if (self.length < cIndex) { 236 | SUILogError(@"substrFrom Str ⤭ %@ ⤪ length < Index ⤭ %zd ⤪", self, cIndex); 237 | return nil; 238 | } 239 | return [self substringFromIndex:cIndex]; 240 | } 241 | - (NSString *)sui_substrWithRange:(NSRange)cRange 242 | { 243 | if (self.length < cRange.location) { 244 | SUILogError(@"substrWithRange Str ⤭ %@ ⤪ length < Range ⤭ %@ ⤪", self, NSStringFromRange(cRange)); 245 | return nil; 246 | } else if (self.length < cRange.location + cRange.length) { 247 | SUILogError(@"substrWithRange Str ⤭ %@ ⤪ length < Range.location+length ⤭ %@ ⤪", self, NSStringFromRange(cRange)); 248 | return [self sui_substrFromIndex:cRange.location]; 249 | } 250 | return [self substringWithRange:cRange]; 251 | } 252 | 253 | 254 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 255 | * Replace 256 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 257 | 258 | #pragma mark - Replace 259 | 260 | - (NSString *)sui_replaceString:(NSString *)cString withString:(NSString *)cReplacement 261 | { 262 | if (cString.length == 0) return self; 263 | NSString *curStr = [self stringByReplacingOccurrencesOfString:cString withString:cReplacement]; 264 | return curStr; 265 | } 266 | - (NSString *)sui_replaceString:(NSString *)cString withString:(NSString *)cReplacement options:(NSStringCompareOptions)cOptions 267 | { 268 | if (cString.length == 0) return self; 269 | NSString *curStr = [self stringByReplacingOccurrencesOfString:cString withString:cReplacement options:cOptions range:NSMakeRange(0, self.length)]; 270 | return curStr; 271 | } 272 | - (NSString *)sui_replaceRegex:(NSString *)cRegex withString:(NSString *)cReplacement 273 | { 274 | if (cRegex.length == 0) return self; 275 | NSString *curStr = [self stringByReplacingOccurrencesOfString:cRegex withString:cReplacement options:NSRegularExpressionSearch range:NSMakeRange(0, self.length)]; 276 | return curStr; 277 | } 278 | 279 | 280 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 281 | * Resource 282 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 283 | 284 | #pragma mark - Resource 285 | 286 | - (NSString *)sui_resourceNameCompleteOfType:(nullable NSString *)ext 287 | { 288 | NSString *curName = nil; 289 | if (ext.length == 0 || [self hasSuffix:ext]) { 290 | curName = self; 291 | } else { 292 | curName = [self stringByAppendingPathExtension:ext]; 293 | } 294 | return curName; 295 | } 296 | - (NSString *)sui_resourcePathForMainBundleOfType:(nullable NSString *)ext 297 | { 298 | NSString *curName = [self sui_resourceNameCompleteOfType:ext]; 299 | NSString *curPath = [[NSBundle mainBundle] pathForResource:curName ofType:nil];; 300 | return curPath; 301 | } 302 | 303 | 304 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 305 | * Size 306 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 307 | 308 | #pragma mark - Size 309 | 310 | - (CGFloat)sui_heightWithFont:(UIFont *)font constrainedToWidth:(CGFloat)width 311 | { 312 | CGFloat curHeight = [self sui_sizeWithFont:font constrainedToWidth:width].height; 313 | return curHeight; 314 | } 315 | - (CGSize)sui_sizeWithFont:(UIFont *)font constrainedToWidth:(CGFloat)width 316 | { 317 | UIFont *textFont = font ? font : [UIFont systemFontOfSize:[UIFont systemFontSize]]; 318 | NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init]; 319 | paragraph.lineBreakMode = NSLineBreakByWordWrapping; 320 | NSDictionary *attributes = @{NSFontAttributeName: textFont, 321 | NSParagraphStyleAttributeName: paragraph}; 322 | CGSize textSize = [self boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) 323 | options:(NSStringDrawingUsesLineFragmentOrigin) 324 | attributes:attributes 325 | context:nil].size; 326 | CGSize curSize = CGSizeMake(width, ceil(textSize.height)); 327 | return curSize; 328 | } 329 | 330 | 331 | @end 332 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSString+SUICrypto.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SUICrypto.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSString (SUICrypto) 15 | 16 | 17 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 18 | * URL 19 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 20 | 21 | #pragma mark - URL 22 | 23 | @property (nullable,readonly,copy) NSString *sui_URLEncode; 24 | 25 | @property (nullable,readonly,copy) NSString *sui_URLDecode; 26 | 27 | - (nullable NSString *)sui_URLEncodeUsingEncoding:(NSStringEncoding)encoding; 28 | 29 | - (nullable NSString *)sui_URLDecodeUsingEncoding:(NSStringEncoding)encoding; 30 | 31 | 32 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 33 | * Base64 34 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 35 | 36 | #pragma mark - Base64 37 | 38 | @property (nullable,readonly,copy) NSString *sui_base64Encode; 39 | 40 | @property (nullable,readonly,copy) NSString *sui_base64Decode; 41 | 42 | @property (nullable,readonly,copy) NSData *sui_base64EncodeData; 43 | 44 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 45 | * SHA1 MD5 SHA224 SHA256 SHA384 SHA512 46 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 47 | 48 | #pragma mark - SHA1 MD5 SHA224 SHA256 SHA384 SHA512 49 | 50 | - (nullable NSString *)sui_md5Digest; 51 | 52 | - (nullable NSString *)sui_HMACDigestWithKey:(NSString *)cKey algorithm:(CCHmacAlgorithm)cAlgorithm; 53 | 54 | 55 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 56 | * Rc4 57 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 58 | 59 | #pragma mark - Rc4 60 | 61 | - (nullable NSData *)sui_rc4WithKey:(NSString *)cKey; 62 | 63 | 64 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 65 | * AES 66 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 67 | 68 | #pragma mark - AES 69 | 70 | - (nullable NSString *)sui_AESEncryptWithKey:(NSString *)cKey andIV:(NSData *)iv; 71 | - (nullable NSString *)sui_AESDecryptWithKey:(NSString *)cKey andIV:(NSData *)iv; 72 | 73 | 74 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 75 | * 3DES 76 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 77 | 78 | #pragma mark - 3DES 79 | 80 | - (nullable NSString *)sui_3DESEncryptWithKey:(NSString *)cKey andIV:(NSData *)iv; 81 | - (nullable NSString *)sui_3DESDecryptWithKey:(NSString *)cKey andIV:(NSData *)iv; 82 | 83 | 84 | @end 85 | 86 | 87 | 88 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 89 | * NSData+SUICrypto 90 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 91 | 92 | @interface NSData (SUICrypto) 93 | 94 | 95 | - (nullable NSData *)sui_AESEncryptWithKey:(NSString *)cKey andIV:(NSData *)iv; 96 | 97 | - (nullable NSData *)sui_AESDecryptWithKey:(NSString *)cKey andIV:(NSData *)iv; 98 | 99 | - (nullable NSData *)sui_3DESEncryptWithKey:(NSString *)cKey andIV:(NSData *)iv; 100 | 101 | - (nullable NSData *)sui_3DESDecryptWithKey:(NSString *)cKey andIV:(NSData *)iv; 102 | 103 | 104 | @end 105 | 106 | NS_ASSUME_NONNULL_END 107 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSString+SUIRegex.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SUIRegex.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | /** 10 | * 正则表达式简单说明 11 | * 语法: 12 | . 匹配除换行符以外的任意字符 13 | \w 匹配字母或数字或下划线或汉字 14 | \s 匹配任意的空白符 15 | \d 匹配数字 16 | \b 匹配单词的开始或结束 17 | ^ 匹配字符串的开始 18 | $ 匹配字符串的结束 19 | * 重复零次或更多次 20 | + 重复一次或更多次 21 | ? 重复零次或一次 22 | {n} 重复n次 23 | {n,} 重复n次或更多次 24 | {n,m} 重复n到m次 25 | \W 匹配任意不是字母,数字,下划线,汉字的字符 26 | \S 匹配任意不是空白符的字符 27 | \D 匹配任意非数字的字符 28 | \B 匹配不是单词开头或结束的位置 29 | [^x] 匹配除了x以外的任意字符 30 | [^aeiou]匹配除了aeiou这几个字母以外的任意字符 31 | *? 重复任意次,但尽可能少重复 32 | +? 重复1次或更多次,但尽可能少重复 33 | ?? 重复0次或1次,但尽可能少重复 34 | {n,m}? 重复n到m次,但尽可能少重复 35 | {n,}? 重复n次以上,但尽可能少重复 36 | \a 报警字符(打印它的效果是电脑嘀一声) 37 | \b 通常是单词分界位置,但如果在字符类里使用代表退格 38 | \t 制表符,Tab 39 | \r 回车 40 | \v 竖向制表符 41 | \f 换页符 42 | \n 换行符 43 | \e Escape 44 | \0nn ASCII代码中八进制代码为nn的字符 45 | \xnn ASCII代码中十六进制代码为nn的字符 46 | \unnnn Unicode代码中十六进制代码为nnnn的字符 47 | \cN ASCII控制字符。比如\cC代表Ctrl+C 48 | \A 字符串开头(类似^,但不受处理多行选项的影响) 49 | \Z 字符串结尾或行尾(不受处理多行选项的影响) 50 | \z 字符串结尾(类似$,但不受处理多行选项的影响) 51 | \G 当前搜索的开头 52 | \p{name} Unicode中命名为name的字符类,例如\p{IsGreek} 53 | (?>exp) 贪婪子表达式 54 | (?-exp) 平衡组 55 | (?im-nsx:exp) 在子表达式exp中改变处理选项 56 | (?im-nsx) 为表达式后面的部分改变处理选项 57 | (?(exp)yes|no) 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no 58 | (?(exp)yes) 同上,只是使用空表达式作为no 59 | (?(name)yes|no) 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no 60 | (?(name)yes) 同上,只是使用空表达式作为no 61 | 62 | 捕获 63 | (exp) 匹配exp,并捕获文本到自动命名的组里 64 | (?exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp) 65 | (?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号 66 | 零宽断言 67 | (?=exp) 匹配exp前面的位置 68 | (?<=exp) 匹配exp后面的位置 69 | (?!exp) 匹配后面跟的不是exp的位置 70 | (? 82 | 83 | NS_ASSUME_NONNULL_BEGIN 84 | 85 | @interface NSString (SUIRegex) 86 | 87 | 88 | - (nullable NSString *)sui_regex:(NSString *)cRegex; 89 | 90 | 91 | - (BOOL)sui_validateByRegex:(NSString *)cRegex; 92 | 93 | - (BOOL)sui_validateNickname; 94 | 95 | - (BOOL)sui_validateMobile; 96 | 97 | - (BOOL)sui_validateIPAddress; 98 | 99 | - (BOOL)sui_validateURL; 100 | 101 | - (BOOL)sui_validateEmail; 102 | 103 | - (BOOL)sui_validateIDCardNumber; 104 | 105 | - (BOOL)sui_validateCarNumber; 106 | 107 | 108 | @end 109 | 110 | NS_ASSUME_NONNULL_END 111 | -------------------------------------------------------------------------------- /SUIUtils/Foundation/NSString+SUIRegex.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SUIRegex.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "NSString+SUIRegex.h" 10 | 11 | @implementation NSString (SUIRegex) 12 | 13 | 14 | - (NSString *)sui_regex:(NSString *)cRegex 15 | { 16 | if (cRegex.length == 0) return nil; 17 | NSRange curRange = [self rangeOfString:cRegex options:NSRegularExpressionSearch]; 18 | if (curRange.location == NSNotFound) return nil; 19 | NSString *curStr = [self substringWithRange:curRange]; 20 | return curStr; 21 | } 22 | 23 | 24 | - (BOOL)sui_validateByRegex:(NSString *)cRegex 25 | { 26 | NSPredicate *curPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",cRegex]; 27 | BOOL ret = [curPredicate evaluateWithObject:self]; 28 | return ret; 29 | } 30 | 31 | - (BOOL)sui_validateNickname 32 | { 33 | NSString *curRegex = @"^[0-9a-zA-Z\u4e00-\u9fa5]+$"; 34 | BOOL ret = [self sui_validateByRegex:curRegex]; 35 | return ret; 36 | } 37 | 38 | - (BOOL)sui_validateMobile 39 | { 40 | /** 41 | * 手机号以13、15、18、170开头,8个 \d 数字字符 42 | * 小灵通 区号:010,020,021,022,023,024,025,027,028,029 还有未设置的新区号xxx 43 | */ 44 | NSString *mobileNoRegex = @"^1((3\\d|5[0-35-9]|8[025-9])\\d|70[059])\\d{7}$";//除4以外的所有个位整数,不能使用[^4,\\d]匹配,这里是否iOS Bug? 45 | NSString *phsRegex = @"^0(10|2[0-57-9]|\\d{3})\\d{7,8}$"; 46 | BOOL ret = [self sui_validateByRegex:mobileNoRegex]; 47 | BOOL ret1 = [self sui_validateByRegex:phsRegex]; 48 | return (ret || ret1); 49 | } 50 | 51 | - (BOOL)sui_validateIPAddress 52 | { 53 | NSString *curRegex = @"^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"; 54 | BOOL ret = [self sui_validateByRegex:curRegex]; 55 | if (ret) { 56 | NSArray *componds = [self componentsSeparatedByString:@","]; 57 | BOOL v = YES; 58 | for (NSString *s in componds) { 59 | if (s.integerValue > 255) { 60 | v = NO; 61 | break; 62 | } 63 | } 64 | return v; 65 | } 66 | return NO; 67 | } 68 | 69 | - (BOOL)sui_validateURL 70 | { 71 | NSString *curRegex = @"^((http)|(https))+:[^\\s]+\\.[^\\s]*$"; 72 | BOOL ret = [self sui_validateByRegex:curRegex]; 73 | return ret; 74 | } 75 | 76 | - (BOOL)sui_validateEmail 77 | { 78 | NSString *curRegex = @"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"; 79 | BOOL ret = [self sui_validateByRegex:curRegex]; 80 | return ret; 81 | } 82 | 83 | - (BOOL)sui_validateIDCardNumber 84 | { 85 | NSString *curRegex = @"^(\\d{14}|\\d{17})(\\d|[xX])$"; 86 | BOOL ret = [self sui_validateByRegex:curRegex]; 87 | return ret; 88 | } 89 | 90 | - (BOOL)sui_validateCarNumber 91 | { 92 | //车牌号:湘K-DE829 香港车牌号码:粤Z-J499港 93 | NSString *curRegex = @"^[\u4e00-\u9fff]{1}[a-zA-Z]{1}[-][a-zA-Z_0-9]{4}[a-zA-Z_0-9_\u4e00-\u9fff]$";//其中\u4e00-\u9fa5表示unicode编码中汉字已编码部分,\u9fa5-\u9fff是保留部分,将来可能会添加 94 | BOOL ret = [self sui_validateByRegex:curRegex]; 95 | return ret; 96 | } 97 | 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /SUIUtils/Helper/SUITableHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUITableHelper.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/20. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | typedef NSString * __nonnull (^SUITableHelperCellIdentifierBlock)(NSIndexPath *cIndexPath, id cModel); 15 | typedef void (^SUITableHelperDidSelectBlock)(NSIndexPath *cIndexPath, id cModel); 16 | 17 | @interface SUITableHelper : NSObject 18 | 19 | 20 | /** 21 | * When using the storyboard and a single cell, set the property inspector same identifier 22 | */ 23 | @property (nullable,nonatomic,copy) NSString *cellIdentifier; 24 | 25 | /** 26 | * When using xib, all incoming nib names 27 | */ 28 | - (void)registerNibs:(NSArray *)cellNibNames; 29 | 30 | /** 31 | * When there are multiple cell, returned identifier in block 32 | */ 33 | - (void)cellMultipleIdentifier:(SUITableHelperCellIdentifierBlock)cb; 34 | 35 | /** 36 | * If you override tableView:didSelectRowAtIndexPath: method, it will be invalid 37 | */ 38 | - (void)didSelect:(SUITableHelperDidSelectBlock)cb; 39 | 40 | @property (nonatomic,weak) UITableView *sui_tableView; 41 | @property (nonatomic,strong) NSIndexPath *sui_indexPath; 42 | 43 | - (void)resetDataAry:(NSArray *)newDataAry forSection:(NSUInteger)cSection; 44 | - (void)reloadDataAry:(NSArray *)newDataAry forSection:(NSUInteger)cSection; 45 | - (void)addDataAry:(NSArray *)newDataAry forSection:(NSUInteger)cSection; 46 | - (void)insertData:(id)cModel AtIndex:(NSIndexPath *)cIndexPath; 47 | - (void)deleteDataAtIndex:(NSIndexPath *)cIndexPath; 48 | 49 | - (id)currentModel; 50 | - (id)currentModelAtIndexPath:(NSIndexPath *)cIndexPath; 51 | 52 | 53 | @end 54 | 55 | NS_ASSUME_NONNULL_END 56 | -------------------------------------------------------------------------------- /SUIUtils/Helper/SUITableHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // SUITableHelper.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/20. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUITableHelper.h" 10 | #import "SUIMacro.h" 11 | #import "UIViewController+SUIAdditions.h" 12 | #import "UITableView+FDTemplateLayoutCell.h" 13 | #import "UITableViewCell+SUIHelper.h" 14 | #import "UITableView+SUIHelper.h" 15 | 16 | @interface SUITableHelper () 17 | 18 | @property (nonatomic,strong) NSMutableArray *dataArray; 19 | @property (nonatomic,copy) SUITableHelperCellIdentifierBlock cellIdentifierBlock; 20 | @property (nonatomic,copy) SUITableHelperDidSelectBlock didSelectBlock; 21 | 22 | @end 23 | 24 | @implementation SUITableHelper 25 | 26 | 27 | - (NSString *)cellIdentifier 28 | { 29 | if (_cellIdentifier == nil) { 30 | NSString *curVCIdentifier = self.sui_tableView.sui_vc.sui_identifier; 31 | if (curVCIdentifier) { 32 | NSString *curCellIdentifier = gFormat(@"SUI%@Cell", curVCIdentifier); 33 | _cellIdentifier = curCellIdentifier; 34 | } 35 | } 36 | return _cellIdentifier; 37 | } 38 | 39 | - (void)registerNibs:(NSArray *)cellNibNames 40 | { 41 | if (cellNibNames.count > 0) { 42 | [cellNibNames enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 43 | [self.sui_tableView registerNib:[UINib nibWithNibName:obj bundle:nil] forCellReuseIdentifier:obj]; 44 | }]; 45 | if (cellNibNames.count == 1) { 46 | self.cellIdentifier = cellNibNames[0]; 47 | } 48 | } 49 | } 50 | 51 | - (void)cellMultipleIdentifier:(SUITableHelperCellIdentifierBlock)cb 52 | { 53 | self.cellIdentifierBlock = cb; 54 | } 55 | 56 | - (void)didSelect:(SUITableHelperDidSelectBlock)cb 57 | { 58 | self.didSelectBlock = cb; 59 | } 60 | 61 | 62 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 63 | * TableView DataSource Delegate 64 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 65 | 66 | #pragma mark - TableView DataSource Delegate 67 | 68 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 69 | { 70 | NSInteger curNumOfSections = self.dataArray.count; 71 | return curNumOfSections; 72 | } 73 | 74 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 75 | { 76 | NSInteger curNumOfRows = 0; 77 | if (self.dataArray.count > section) { 78 | NSMutableArray *subDataAry = self.dataArray[section]; 79 | curNumOfRows = subDataAry.count; 80 | } 81 | return curNumOfRows; 82 | } 83 | 84 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 85 | { 86 | UITableViewCell *curCell = nil; 87 | id curModel = [self currentModelAtIndexPath:indexPath]; 88 | NSString *curCellIdentifier = [self cellIdentifierForRowAtIndexPath:indexPath model:curModel]; 89 | curCell = [tableView dequeueReusableCellWithIdentifier:curCellIdentifier forIndexPath:indexPath]; 90 | SUIAssert(curCell, @"cell is nil Identifier ⤭ %@ ⤪", curCellIdentifier); 91 | [self sui_configureCell:curCell atIndexPath:indexPath model:curModel]; 92 | return curCell; 93 | } 94 | 95 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 96 | { 97 | CGFloat curHeight = 0; 98 | if (tableView.sui_autoSizingCell) { 99 | id curModel = [self currentModelAtIndexPath:indexPath]; 100 | NSString *curCellIdentifier = [self cellIdentifierForRowAtIndexPath:indexPath model:curModel]; 101 | uWeakSelf 102 | curHeight = [tableView fd_heightForCellWithIdentifier:curCellIdentifier cacheByIndexPath:indexPath configuration:^(UITableViewCell *curCell) { 103 | [weakSelf sui_configureCell:curCell atIndexPath:indexPath model:curModel]; 104 | }]; 105 | } else { 106 | curHeight = tableView.rowHeight; 107 | } 108 | return curHeight; 109 | } 110 | 111 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 112 | { 113 | self.sui_indexPath = indexPath; 114 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 115 | if (self.didSelectBlock) { 116 | id curModel = [self currentModelAtIndexPath:indexPath]; 117 | self.didSelectBlock(indexPath, curModel); 118 | } 119 | } 120 | 121 | 122 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 123 | * Handler 124 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 125 | 126 | #pragma mark - Handler 127 | 128 | - (NSString *)cellIdentifierForRowAtIndexPath:(NSIndexPath *)cIndexPath model:(id)cModel 129 | { 130 | NSString *curCellIdentifier = nil; 131 | if (self.cellIdentifierBlock) { 132 | curCellIdentifier = self.cellIdentifierBlock(cIndexPath, cModel); 133 | } else { 134 | curCellIdentifier = self.cellIdentifier; 135 | } 136 | return curCellIdentifier; 137 | } 138 | 139 | - (id)currentModel 140 | { 141 | return [self currentModelAtIndexPath:self.sui_indexPath]; 142 | } 143 | 144 | - (id)currentModelAtIndexPath:(NSIndexPath *)cIndexPath 145 | { 146 | if (self.dataArray.count > cIndexPath.section) { 147 | NSMutableArray *subDataAry = self.dataArray[cIndexPath.section]; 148 | if (subDataAry.count > cIndexPath.row) { 149 | id curModel = subDataAry[cIndexPath.row]; 150 | return curModel; 151 | } 152 | } 153 | return nil; 154 | } 155 | 156 | - (void)sui_configureCell:(UITableViewCell *)cCell atIndexPath:(NSIndexPath *)cIndexPath model:(id)cModel 157 | { 158 | if ([cCell respondsToSelector:@selector(sui_cellWillDisplayWithModel:)]) { 159 | cCell.sui_indexPath = cIndexPath; 160 | [cCell sui_cellWillDisplayWithModel:cModel]; 161 | } 162 | } 163 | 164 | 165 | - (void)resetDataAry:(NSArray *)newDataAry forSection:(NSUInteger)cSection 166 | { 167 | uMainQueue 168 | ( 169 | [self sui_makeUpDataAryForSection:cSection]; 170 | NSMutableArray *subAry = self.dataArray[cSection]; 171 | if (subAry.count) [subAry removeAllObjects]; 172 | if (newDataAry.count) { 173 | [subAry addObjectsFromArray:newDataAry]; 174 | } 175 | [self.sui_tableView reloadData]; 176 | ) 177 | } 178 | - (void)reloadDataAry:(NSArray *)newDataAry forSection:(NSUInteger)cSection 179 | { 180 | if (newDataAry.count == 0) return; 181 | uMainQueue 182 | ( 183 | NSIndexSet *curIndexSet = [self sui_makeUpDataAryForSection:cSection]; 184 | NSMutableArray *subAry = self.dataArray[cSection]; 185 | if (subAry.count) [subAry removeAllObjects]; 186 | [subAry addObjectsFromArray:newDataAry]; 187 | 188 | [self.sui_tableView beginUpdates]; 189 | if (curIndexSet) { 190 | [self.sui_tableView insertSections:curIndexSet withRowAnimation:UITableViewRowAnimationAutomatic]; 191 | } else { 192 | [self.sui_tableView reloadSections:[NSIndexSet indexSetWithIndex:cSection] withRowAnimation:UITableViewRowAnimationNone]; 193 | } 194 | [self.sui_tableView endUpdates]; 195 | ) 196 | } 197 | - (void)addDataAry:(NSArray *)newDataAry forSection:(NSUInteger)cSection 198 | { 199 | if (newDataAry.count == 0) return; 200 | uMainQueue 201 | ( 202 | NSIndexSet *curIndexSet = [self sui_makeUpDataAryForSection:cSection]; 203 | NSMutableArray *subAry = self.dataArray[cSection]; 204 | if (curIndexSet) { 205 | [subAry addObjectsFromArray:newDataAry]; 206 | [self.sui_tableView beginUpdates]; 207 | [self.sui_tableView insertSections:curIndexSet withRowAnimation:UITableViewRowAnimationAutomatic]; 208 | [self.sui_tableView endUpdates]; 209 | } else { 210 | __block NSMutableArray *curIndexPaths = [NSMutableArray arrayWithCapacity:newDataAry.count]; 211 | [newDataAry enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 212 | [curIndexPaths addObject:[NSIndexPath indexPathForRow:subAry.count+idx inSection:cSection]]; 213 | }]; 214 | [subAry addObjectsFromArray:newDataAry]; 215 | [self.sui_tableView beginUpdates]; 216 | [self.sui_tableView insertRowsAtIndexPaths:curIndexPaths withRowAnimation:UITableViewRowAnimationAutomatic]; 217 | [self.sui_tableView endUpdates]; 218 | } 219 | ) 220 | } 221 | - (void)insertData:(id)cModel AtIndex:(NSIndexPath *)cIndexPath; 222 | { 223 | uMainQueue 224 | ( 225 | NSIndexSet *curIndexSet = [self sui_makeUpDataAryForSection:cIndexPath.section]; 226 | NSMutableArray *subAry = self.dataArray[cIndexPath.section]; 227 | if (subAry.count < cIndexPath.row) return; 228 | [subAry insertObject:cModel atIndex:cIndexPath.row]; 229 | if (curIndexSet) { 230 | [self.sui_tableView beginUpdates]; 231 | [self.sui_tableView insertSections:curIndexSet withRowAnimation:UITableViewRowAnimationAutomatic]; 232 | [self.sui_tableView endUpdates]; 233 | } else { 234 | [subAry insertObject:cModel atIndex:cIndexPath.row]; 235 | [self.sui_tableView beginUpdates]; 236 | [self.sui_tableView insertRowsAtIndexPaths:@[cIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 237 | [self.sui_tableView endUpdates]; 238 | } 239 | ) 240 | } 241 | - (void)deleteDataAtIndex:(NSIndexPath *)cIndexPath 242 | { 243 | uMainQueue 244 | ( 245 | if (self.dataArray.count <= cIndexPath.section) return; 246 | NSMutableArray *subAry = self.dataArray[cIndexPath.section]; 247 | if (subAry.count <= cIndexPath.row) return; 248 | 249 | [subAry removeObjectAtIndex:cIndexPath.row]; 250 | [self.sui_tableView beginUpdates]; 251 | [self.sui_tableView deleteRowsAtIndexPaths:@[cIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 252 | [self.sui_tableView endUpdates]; 253 | ) 254 | } 255 | 256 | - (NSIndexSet *)sui_makeUpDataAryForSection:(NSInteger)cSection 257 | { 258 | NSMutableIndexSet *curIndexSet = nil; 259 | if (self.dataArray.count <= cSection) { 260 | curIndexSet = [NSMutableIndexSet indexSet]; 261 | for (NSInteger idx=0; idx<(cSection-self.dataArray.count+1); idx++) { 262 | NSMutableArray *subAry = [NSMutableArray array]; 263 | [self.dataArray addObject:subAry]; 264 | [curIndexSet addIndex:cSection-idx]; 265 | } 266 | } 267 | return curIndexSet; 268 | } 269 | 270 | 271 | - (NSMutableArray *)dataArray 272 | { 273 | if (!_dataArray) { 274 | _dataArray = [NSMutableArray new]; 275 | } 276 | return _dataArray; 277 | } 278 | 279 | 280 | @end 281 | -------------------------------------------------------------------------------- /SUIUtils/Helper/UITableView+SUIHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+SUIHelper.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/20. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class SUITableHelper; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface UITableView (SUIHelper) 16 | 17 | 18 | @property (null_resettable,strong) SUITableHelper *sui_tableHelper; 19 | 20 | @property (nonatomic) IBInspectable BOOL sui_autoSizingCell; 21 | 22 | - (void)sui_resetDataAry:(NSArray *)newDataAry; 23 | - (void)sui_resetDataAry:(NSArray *)newDataAry forSection:(NSInteger)cSection; 24 | - (void)sui_reloadDataAry:(NSArray *)newDataAry; 25 | - (void)sui_reloadDataAry:(NSArray *)newDataAry forSection:(NSInteger)cSection; 26 | - (void)sui_addDataAry:(NSArray *)newDataAry; 27 | - (void)sui_addDataAry:(NSArray *)newDataAry forSection:(NSInteger)cSection; 28 | - (void)sui_insertData:(id)cModel AtIndex:(NSIndexPath *)cIndexPath; 29 | - (void)sui_deleteDataAtIndex:(NSIndexPath *)cIndexPath; 30 | 31 | 32 | @end 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /SUIUtils/Helper/UITableView+SUIHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+SUIHelper.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/20. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UITableView+SUIHelper.h" 10 | #import "NSObject+SUIAdditions.h" 11 | #import "SUITableHelper.h" 12 | 13 | @implementation UITableView (SUIHelper) 14 | 15 | 16 | - (SUITableHelper *)sui_tableHelper 17 | { 18 | SUITableHelper *curTableHelper = [self sui_getAssociatedObjectWithKey:@selector(sui_tableHelper)]; 19 | if (curTableHelper) return curTableHelper; 20 | 21 | curTableHelper = [SUITableHelper new]; 22 | self.sui_tableHelper = curTableHelper; 23 | return curTableHelper; 24 | } 25 | - (void)setSui_tableHelper:(SUITableHelper *)sui_tableHelper 26 | { 27 | [self sui_setAssociatedRetainObject:sui_tableHelper key:@selector(sui_tableHelper)]; 28 | self.delegate = sui_tableHelper; 29 | self.dataSource = sui_tableHelper; 30 | sui_tableHelper.sui_tableView = self; 31 | } 32 | 33 | 34 | - (BOOL)sui_autoSizingCell 35 | { 36 | return [[self sui_getAssociatedObjectWithKey:@selector(sui_autoSizingCell)] boolValue]; 37 | } 38 | - (void)setSui_autoSizingCell:(BOOL)sui_autoSizingCell 39 | { 40 | [self sui_setAssociatedRetainObject:@(sui_autoSizingCell) key:@selector(sui_autoSizingCell)]; 41 | } 42 | 43 | 44 | - (void)sui_resetDataAry:(NSArray *)newDataAry 45 | { 46 | [self sui_resetDataAry:newDataAry forSection:0]; 47 | } 48 | - (void)sui_resetDataAry:(NSArray *)newDataAry forSection:(NSInteger)cSection 49 | { 50 | [self.sui_tableHelper resetDataAry:newDataAry forSection:cSection]; 51 | } 52 | - (void)sui_reloadDataAry:(NSArray *)newDataAry 53 | { 54 | [self sui_reloadDataAry:newDataAry forSection:0]; 55 | } 56 | - (void)sui_reloadDataAry:(NSArray *)newDataAry forSection:(NSInteger)cSection 57 | { 58 | [self.sui_tableHelper reloadDataAry:newDataAry forSection:cSection]; 59 | } 60 | - (void)sui_addDataAry:(NSArray *)newDataAry 61 | { 62 | [self sui_addDataAry:newDataAry forSection:0]; 63 | } 64 | - (void)sui_addDataAry:(NSArray *)newDataAry forSection:(NSInteger)cSection 65 | { 66 | [self.sui_tableHelper addDataAry:newDataAry forSection:cSection]; 67 | } 68 | - (void)sui_insertData:(id)cModel AtIndex:(NSIndexPath *)cIndexPath; 69 | { 70 | [self.sui_tableHelper insertData:cModel AtIndex:cIndexPath]; 71 | } 72 | - (void)sui_deleteDataAtIndex:(NSIndexPath *)cIndexPath 73 | { 74 | [self.sui_tableHelper deleteDataAtIndex:cIndexPath]; 75 | } 76 | 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /SUIUtils/Helper/UITableViewCell+SUIHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell+SUIHelper.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/20. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol SUITableHelperProtocol 14 | @optional 15 | 16 | - (void)sui_cellWillDisplayWithModel:(id)cModel; 17 | 18 | @end 19 | 20 | 21 | @interface UITableViewCell (SUIHelper) 22 | 23 | 24 | @property (nullable,nonatomic,strong) NSIndexPath *sui_indexPath; 25 | 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /SUIUtils/Helper/UITableViewCell+SUIHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell+SUIHelper.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/20. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UITableViewCell+SUIHelper.h" 10 | #import "NSObject+SUIAdditions.h" 11 | 12 | @implementation UITableViewCell (SUIHelper) 13 | 14 | 15 | - (NSIndexPath *)sui_indexPath 16 | { 17 | NSIndexPath *curIndexPath = [self sui_getAssociatedObjectWithKey:@selector(sui_indexPath)]; 18 | return curIndexPath; 19 | } 20 | - (void)setSui_indexPath:(NSIndexPath *)sui_indexPath 21 | { 22 | [self sui_setAssociatedRetainObject:sui_indexPath key:@selector(sui_indexPath)]; 23 | } 24 | 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /SUIUtils/SUIUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUIUtils.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #ifndef SUIUtils_h 10 | #define SUIUtils_h 11 | 12 | 13 | #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0 14 | #error SUIKitTool doesn't support Deployement Target version < 7.0 15 | #endif 16 | 17 | 18 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 19 | * Tool 20 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 21 | 22 | #import "SUIMacro.h" 23 | #import "SUITool.h" 24 | #import "SUITool+Delay.h" 25 | #import "SUITool+Camera.h" 26 | #import "SUITool+OpenURL.h" 27 | #import "SUITool+FileManager.h" 28 | 29 | 30 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 31 | * Categorie 32 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 33 | 34 | // Foundation 35 | 36 | #import "NSObject+SUIAdditions.h" 37 | #import "NSString+SUIAdditions.h" 38 | #import "NSString+SUICrypto.h" 39 | #import "NSString+SUIRegex.h" 40 | #import "NSData+SUIAdditions.h" 41 | #import "NSDate+SUIAdditions.h" 42 | #import "NSNumber+SUIAdditions.h" 43 | #import "NSArray+SUIAdditions.h" 44 | #import "NSArray+SUISafeAccess.h" 45 | #import "NSDictionary+SUIAdditions.h" 46 | #import "NSDictionary+SUISafeAccess.h" 47 | #import "NSIndexPath+SUIAdditions.h" 48 | 49 | 50 | // UIKit 51 | 52 | #import "UIView+SUIAdditions.h" 53 | #import "UIViewController+SUIAdditions.h" 54 | #import "UINavigationController+SUIAdditions.h" 55 | #import "UITableViewCell+SUIAdditions.h" 56 | #import "UIButton+SUIAdditions.h" 57 | #import "UIControl+SUIAdditions.h" 58 | #import "UILabel+SUIAdditions.h" 59 | #import "UITextView+SUIAdditions.h" 60 | #import "UITextField+SUIAdditions.h" 61 | #import "UIImage+SUIAdditions.h" 62 | #import "UIScrollView+SUIAdditions.h" 63 | 64 | 65 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 66 | * Helper 67 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 68 | 69 | #import "SUITableHelper.h" 70 | #import "UITableViewCell+SUIHelper.h" 71 | #import "UITableView+SUIHelper.h" 72 | 73 | 74 | #endif /* SUIUtils_h */ 75 | -------------------------------------------------------------------------------- /SUIUtils/Tool/SUITool+Camera.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUITool+Camera.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUITool.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SUITool (Camera) 14 | 15 | 16 | + (BOOL)cameraAvailable; 17 | 18 | + (BOOL)cameraRearAvailable; 19 | 20 | + (BOOL)cameraFrontAvailable; 21 | 22 | + (BOOL)photoLibraryAvailable; 23 | 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /SUIUtils/Tool/SUITool+Camera.m: -------------------------------------------------------------------------------- 1 | // 2 | // SUITool+Camera.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUITool+Camera.h" 10 | #import 11 | #import "SUIMacro.h" 12 | 13 | @implementation SUITool (Camera) 14 | 15 | 16 | + (BOOL)cameraAvailable 17 | { 18 | BOOL ret = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]; 19 | SUIAssert(ret, @"Camera unavailable."); 20 | return ret; 21 | } 22 | 23 | + (BOOL)cameraRearAvailable 24 | { 25 | BOOL ret = [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]; 26 | SUIAssert(ret, @"Camera rear unavailable."); 27 | return ret; 28 | } 29 | 30 | + (BOOL)cameraFrontAvailable 31 | { 32 | BOOL ret = [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]; 33 | SUIAssert(ret, @"Camera front unavailable."); 34 | return ret; 35 | } 36 | 37 | + (BOOL)photoLibraryAvailable 38 | { 39 | BOOL ret = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]; 40 | SUIAssert(ret, @"Photo library unavailable."); 41 | return ret; 42 | } 43 | 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /SUIUtils/Tool/SUITool+Delay.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUITool+Delay.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUITool.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | typedef void (^SUIToolDelayTask)(BOOL cancel); 14 | 15 | @interface SUITool (Delay) 16 | 17 | 18 | + (SUIToolDelayTask)delay:(NSTimeInterval)delay cb:(void (^)(void))completion; 19 | 20 | + (void)cancelDelayTask:(SUIToolDelayTask)cTask; 21 | 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /SUIUtils/Tool/SUITool+Delay.m: -------------------------------------------------------------------------------- 1 | // 2 | // SUITool+Delay.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUITool+Delay.h" 10 | 11 | @implementation SUITool (Delay) 12 | 13 | 14 | + (SUIToolDelayTask)delay:(NSTimeInterval)delay cb:(void (^)(void))completion; 15 | { 16 | __block dispatch_block_t closure = completion; 17 | __block SUIToolDelayTask currTask = nil; 18 | 19 | SUIToolDelayTask delayedBlock = ^(BOOL cancel) { 20 | if (cancel == NO) { 21 | dispatch_async(dispatch_get_main_queue(), closure); 22 | } 23 | closure = nil; 24 | currTask = nil; 25 | }; 26 | 27 | currTask = delayedBlock; 28 | 29 | [self sui_delayExecutive:delay cb:^{ 30 | if (currTask) currTask(NO); 31 | }]; 32 | return currTask; 33 | } 34 | 35 | + (void)cancelDelayTask:(SUIToolDelayTask)cTask 36 | { 37 | if (cTask) cTask(YES); 38 | } 39 | 40 | + (void)sui_delayExecutive:(NSTimeInterval)delayInSeconds cb:(void (^)(void))completionBlock 41 | { 42 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 43 | dispatch_after(popTime, dispatch_get_main_queue(), completionBlock); 44 | } 45 | 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /SUIUtils/Tool/SUITool+FileManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUITool+FileManager.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUITool.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SUITool (FileManager) 14 | 15 | 16 | + (BOOL)fileCreateDirectory:(NSString *)filePath; 17 | 18 | + (BOOL)fileExist:(NSString *)filePath; 19 | 20 | + (BOOL)fileWrite:(NSData *)data toPath:(NSString *)filePath; 21 | 22 | + (BOOL)fileMove:(NSString *)sourcePath toPath:(NSString *)filePath; 23 | 24 | + (BOOL)fileCopy:(NSString *)sourcePath toPath:(NSString *)filePath; 25 | 26 | + (nullable NSData *)fileRead:(NSString *)filePath; 27 | 28 | + (NSUInteger)fileSize:(NSString *)filePath; 29 | 30 | + (BOOL)fileDelete:(NSString *)filePath; 31 | 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /SUIUtils/Tool/SUITool+FileManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // SUITool+FileManager.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUITool+FileManager.h" 10 | #import "SUIMacro.h" 11 | 12 | @implementation SUITool (FileManager) 13 | 14 | 15 | + (BOOL)fileCreateDirectory:(NSString *)filePath 16 | { 17 | if (![self fileExist:filePath]) { 18 | NSError *anyError = nil; 19 | BOOL ret = [[NSFileManager defaultManager] 20 | createDirectoryAtPath:filePath 21 | withIntermediateDirectories:YES 22 | attributes:nil 23 | error:&anyError]; 24 | SUIAssert(ret, @"file create director Error ⤭ %@ ⤪ At ⤭ %@ ⤪", anyError, filePath); 25 | return ret; 26 | } 27 | return YES; 28 | } 29 | 30 | + (BOOL)fileExist:(NSString *)filePath 31 | { 32 | BOOL ret = [[NSFileManager defaultManager] fileExistsAtPath:filePath]; 33 | return ret; 34 | } 35 | 36 | + (BOOL)fileWrite:(NSData *)data toPath:(NSString *)filePath 37 | { 38 | NSError *anyError = nil; 39 | BOOL ret = [data writeToFile:filePath 40 | options:NSDataWritingAtomic 41 | error:&anyError]; 42 | SUIAssert(ret, @"file write Error ⤭ %@ ⤪ To ⤭ %@ ⤪", anyError, filePath); 43 | return ret; 44 | } 45 | 46 | + (BOOL)fileMove:(NSString *)sourcePath toPath:(NSString *)filePath 47 | { 48 | NSError *anyError = nil; 49 | BOOL ret = [[NSFileManager defaultManager] 50 | moveItemAtPath:sourcePath 51 | toPath:filePath 52 | error:&anyError]; 53 | SUIAssert(ret, @"file move Error ⤭ %@ ⤪ Source ⤭ %@ ⤪ To ⤭ %@ ⤪", anyError, sourcePath, filePath); 54 | return ret; 55 | } 56 | 57 | + (BOOL)fileCopy:(NSString *)sourcePath toPath:(NSString *)filePath 58 | { 59 | NSError *anyError = nil; 60 | BOOL ret = [[NSFileManager defaultManager] 61 | copyItemAtPath:sourcePath 62 | toPath:filePath 63 | error:&anyError]; 64 | SUIAssert(ret, @"file copy Error ⤭ %@ ⤪ Source ⤭ %@ ⤪ To ⤭ %@ ⤪", anyError, sourcePath, filePath); 65 | return ret; 66 | } 67 | 68 | + (NSData *)fileRead:(NSString *)filePath 69 | { 70 | NSError *anyError = nil; 71 | NSData *readData = [NSData dataWithContentsOfFile:filePath 72 | options:NSDataReadingMappedIfSafe 73 | error:&anyError]; 74 | SUIAssert(!anyError, @"file read Error ⤭ %@ ⤪ At ⤭ %@ ⤪", anyError, filePath); 75 | return readData; 76 | } 77 | 78 | + (NSUInteger)fileSize:(NSString *)filePath 79 | { 80 | if ([self fileExist:filePath]) 81 | { 82 | NSError *anyError = nil; 83 | NSDictionary *attributes = [[NSFileManager defaultManager] 84 | attributesOfItemAtPath:filePath 85 | error:&anyError]; 86 | SUIAssert(!anyError, @"file size Error ⤭ %@ ⤪ At ⤭ %@ ⤪", anyError, filePath); 87 | if (!anyError) { 88 | NSInteger fSize = [[attributes objectForKey:NSFileSize] integerValue]; 89 | return fSize; 90 | } 91 | } 92 | return 0; 93 | } 94 | 95 | + (BOOL)fileDelete:(NSString *)filePath 96 | { 97 | if ([self fileExist:filePath]) 98 | { 99 | NSError *anyError = nil; 100 | BOOL ret = [[NSFileManager defaultManager] 101 | removeItemAtPath:filePath 102 | error:&anyError]; 103 | SUIAssert(ret, @"file delete Error ⤭ %@ ⤪ At ⤭ %@ ⤪", anyError, filePath); 104 | return ret; 105 | } 106 | return YES; 107 | } 108 | 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /SUIUtils/Tool/SUITool+OpenURL.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUITool+OpenURL.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUITool.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SUITool (OpenURL) 14 | 15 | 16 | + (BOOL)openMail:(NSString *)mail; 17 | 18 | + (BOOL)openPhone:(NSString *)phone; 19 | 20 | + (BOOL)openAppStore:(NSString *)appId; 21 | 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /SUIUtils/Tool/SUITool+OpenURL.m: -------------------------------------------------------------------------------- 1 | // 2 | // SUITool+OpenURL.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUITool+OpenURL.h" 10 | #import 11 | #import "SUIMacro.h" 12 | 13 | @implementation SUITool (OpenURL) 14 | 15 | 16 | + (BOOL)openMail:(NSString *)mail 17 | { 18 | NSString *curURL = gFormat(@"mailto://%@", mail); 19 | BOOL ret = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:curURL]]; 20 | SUIAssert(ret, @"open mail failed Mail ⤭ %@ ⤪", mail); 21 | return ret; 22 | } 23 | 24 | + (BOOL)openPhone:(NSString *)phone 25 | { 26 | NSString *curURL = gFormat(@"telprompt://%@", phone); 27 | BOOL ret = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:curURL]]; 28 | SUIAssert(ret, @"open phone failed Phone ⤭ %@ ⤪", phone); 29 | return ret; 30 | } 31 | 32 | + (BOOL)openAppStore:(NSString *)appId 33 | { 34 | NSString *curURL = gFormat(@"itms-apps://itunes.apple.com/app/id%@", appId); 35 | BOOL ret = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:curURL]]; 36 | SUIAssert(ret, @"open app store failed AppId ⤭ %@ ⤪", appId); 37 | return ret; 38 | } 39 | 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /SUIUtils/Tool/SUITool.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUITool.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | typedef NS_ENUM(NSInteger, SUILaunchedType) { 14 | SUILaunchedLatestVersion = 0, 15 | SUILaunchedFirstLaunched = 1, 16 | SUILaunchedUpdateVersion = 2 17 | }; 18 | 19 | @interface SUITool : NSObject 20 | 21 | 22 | + (SUILaunchedType)launchedType; 23 | 24 | + (nullable NSString *)previousVersion; 25 | 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /SUIUtils/Tool/SUITool.m: -------------------------------------------------------------------------------- 1 | // 2 | // SUITool.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUITool.h" 10 | #import "SUIMacro.h" 11 | 12 | NSString *const sui_everLaunched = @"sui_everLaunched"; 13 | NSString *const sui_everVersion = @"sui_everVersion"; 14 | 15 | @interface SUITool () 16 | 17 | @property (nonatomic) SUILaunchedType launchedType; 18 | 19 | @end 20 | 21 | @implementation SUITool 22 | 23 | 24 | uSharedInstanceWithCommonInit 25 | 26 | - (void)commonInit 27 | { 28 | [self updateVersion]; 29 | } 30 | 31 | - (void)updateVersion 32 | { 33 | if (gUserDefaultsBoolForKey(sui_everLaunched)) 34 | { 35 | NSString *cVersion = kVersion; 36 | NSString *eVersion = [SUITool previousVersion]; 37 | 38 | if ([eVersion isEqualToString:cVersion]) { 39 | self.launchedType = SUILaunchedLatestVersion; 40 | SUILogInfo(@"ever launched latest-version CurrVersion ⤭ %@ ⤪", cVersion); 41 | } 42 | else 43 | { 44 | [gUserDefaults setObject:cVersion forKey:sui_everVersion]; 45 | [gUserDefaults synchronize]; 46 | 47 | self.launchedType = SUILaunchedUpdateVersion; 48 | SUILogInfo(@"ever launched update-version EverVersion ⤭ %@ ⤪ CurrVersion ⤭ %@ ⤪", eVersion, cVersion); 49 | } 50 | } 51 | else 52 | { 53 | [gUserDefaults setBool:YES forKey:sui_everLaunched]; 54 | [gUserDefaults setObject:kVersion forKey:sui_everVersion]; 55 | [gUserDefaults synchronize]; 56 | 57 | self.launchedType = SUILaunchedFirstLaunched; 58 | SUILogInfo(@"first launched CurrVersion ⤭ %@ ⤪", kVersion); 59 | } 60 | } 61 | 62 | + (SUILaunchedType)launchedType 63 | { 64 | return [[self sharedInstance] launchedType]; 65 | } 66 | 67 | + (NSString *)previousVersion 68 | { 69 | return gUserDefaultsObjForKey(sui_everVersion); 70 | } 71 | 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIButton+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIButton (SUIAdditions) 12 | 13 | 14 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 15 | * Normal 16 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 17 | 18 | #pragma mark - Normal 19 | 20 | @property (nullable,nonatomic,copy) NSString *sui_normalTitle; 21 | @property (nullable,nonatomic,copy) UIColor *sui_normalTitleColo; 22 | @property (nullable,nonatomic,copy) UIImage *sui_normalImage; 23 | @property (nullable,nonatomic,copy) UIImage *sui_normalBackgroundImage; 24 | 25 | 26 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 27 | * Highlighted 28 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 29 | 30 | #pragma mark - Highlighted 31 | 32 | @property (nullable,nonatomic,copy) NSString *sui_highlightedTitle; 33 | @property (nullable,nonatomic,copy) UIColor *sui_highlightedTitleColo; 34 | @property (nullable,nonatomic,copy) UIImage *sui_highlightedImage; 35 | @property (nullable,nonatomic,copy) UIImage *sui_highlightedBackgroundImage; 36 | 37 | 38 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 39 | * Selected 40 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 41 | 42 | #pragma mark - Selected 43 | 44 | @property (nullable,nonatomic,copy) NSString *sui_selectedTitle; 45 | @property (nullable,nonatomic,copy) UIColor *sui_selectedTitleColo; 46 | @property (nullable,nonatomic,copy) UIImage *sui_selectedImage; 47 | @property (nullable,nonatomic,copy) UIImage *sui_selectedBackgroundImage; 48 | 49 | 50 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 51 | * Disabled 52 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 53 | 54 | #pragma mark - Disabled 55 | 56 | @property (nullable,nonatomic,copy) NSString *sui_disabledTitle; 57 | @property (nullable,nonatomic,copy) UIColor *sui_disabledTitleColo; 58 | @property (nullable,nonatomic,copy) UIImage *sui_disabledImage; 59 | @property (nullable,nonatomic,copy) UIImage *sui_disabledBackgroundImage; 60 | 61 | 62 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 63 | * Padding & Insets 64 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 65 | 66 | #pragma mark - Padding & Insets 67 | 68 | @property (nonatomic) CGFloat sui_padding; // left & right 69 | @property (nonatomic) UIEdgeInsets sui_insets; 70 | 71 | 72 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 73 | * TintColor 74 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 75 | 76 | #pragma mark - TintColor 77 | 78 | @property (nullable,nonatomic,copy) IBInspectable UIColor *sui_imageTintColor; 79 | 80 | /** 81 | * set text hex color 82 | */ 83 | @property (nullable,assign,nonatomic) IBInspectable NSString *sui_titleHexColor; 84 | 85 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 86 | * Resizable 87 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 88 | 89 | #pragma mark - Resizable 90 | 91 | @property (nonatomic) IBInspectable BOOL sui_resizableImage; 92 | @property (nonatomic) IBInspectable BOOL sui_resizableBackground; 93 | 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIButton+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UIButton+SUIAdditions.h" 10 | #import "UIImage+SUIAdditions.h" 11 | 12 | @implementation UIButton (SUIAdditions) 13 | 14 | 15 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 16 | * Normal 17 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 18 | 19 | #pragma mark - Normal 20 | 21 | - (NSString *)sui_normalTitle 22 | { 23 | return [self titleForState:UIControlStateNormal]; 24 | } 25 | - (void)setSui_normalTitle:(NSString *)sui_normalTitle 26 | { 27 | [self setTitle:sui_normalTitle forState:UIControlStateNormal]; 28 | } 29 | 30 | - (UIColor *)sui_normalTitleColo 31 | { 32 | return [self titleColorForState:UIControlStateNormal]; 33 | } 34 | - (void)setSui_normalTitleColo:(UIColor *)sui_normalTitleColo 35 | { 36 | [self setTitleColor:sui_normalTitleColo forState:UIControlStateNormal]; 37 | } 38 | 39 | - (UIImage *)sui_normalImage 40 | { 41 | return [self imageForState:UIControlStateNormal]; 42 | } 43 | - (void)setSui_normalImage:(UIImage *)sui_normalImage 44 | { 45 | [self setImage:sui_normalImage forState:UIControlStateNormal]; 46 | } 47 | 48 | - (UIImage *)sui_normalBackgroundImage 49 | { 50 | return [self backgroundImageForState:UIControlStateNormal]; 51 | } 52 | - (void)setSui_normalBackgroundImage:(UIImage *)sui_normalBackgroundImage 53 | { 54 | [self setBackgroundImage:sui_normalBackgroundImage forState:UIControlStateNormal]; 55 | } 56 | 57 | 58 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 59 | * Highlighted 60 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 61 | 62 | #pragma mark - Highlighted 63 | 64 | - (NSString *)sui_highlightedTitle 65 | { 66 | return [self titleForState:UIControlStateHighlighted]; 67 | } 68 | - (void)setSui_highlightedTitle:(NSString *)sui_highlightedTitle 69 | { 70 | [self setTitle:sui_highlightedTitle forState:UIControlStateHighlighted]; 71 | } 72 | 73 | - (UIColor *)sui_highlightedTitleColo 74 | { 75 | return [self titleColorForState:UIControlStateHighlighted]; 76 | } 77 | - (void)setSui_highlightedTitleColo:(UIColor *)sui_highlightedTitleColo 78 | { 79 | [self setTitleColor:sui_highlightedTitleColo forState:UIControlStateHighlighted]; 80 | } 81 | 82 | - (UIImage *)sui_highlightedImage 83 | { 84 | return [self imageForState:UIControlStateHighlighted]; 85 | } 86 | - (void)setSui_highlightedImage:(UIImage *)sui_highlightedImage 87 | { 88 | [self setImage:sui_highlightedImage forState:UIControlStateHighlighted]; 89 | } 90 | 91 | - (UIImage *)sui_highlightedBackgroundImage 92 | { 93 | return [self backgroundImageForState:UIControlStateHighlighted]; 94 | } 95 | - (void)setSui_highlightedBackgroundImage:(UIImage *)sui_highlightedBackgroundImage 96 | { 97 | [self setBackgroundImage:sui_highlightedBackgroundImage forState:UIControlStateHighlighted]; 98 | 99 | } 100 | 101 | 102 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 103 | * Selected 104 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 105 | 106 | #pragma mark - Selected 107 | 108 | - (NSString *)sui_selectedTitle 109 | { 110 | return [self titleForState:UIControlStateSelected]; 111 | } 112 | - (void)setSui_selectedTitle:(NSString *)sui_selectedTitle 113 | { 114 | [self setTitle:sui_selectedTitle forState:UIControlStateSelected]; 115 | } 116 | 117 | - (UIColor *)sui_selectedTitleColo 118 | { 119 | return [self titleColorForState:UIControlStateSelected]; 120 | } 121 | - (void)setSui_selectedTitleColo:(UIColor *)sui_selectedTitleColo 122 | { 123 | [self setTitleColor:sui_selectedTitleColo forState:UIControlStateSelected]; 124 | } 125 | 126 | - (UIImage *)sui_selectedImage 127 | { 128 | return [self imageForState:UIControlStateSelected]; 129 | } 130 | - (void)setSui_selectedImage:(UIImage *)sui_selectedImage 131 | { 132 | [self setImage:sui_selectedImage forState:UIControlStateSelected]; 133 | } 134 | 135 | - (UIImage *)sui_selectedBackgroundImage 136 | { 137 | return [self backgroundImageForState:UIControlStateSelected]; 138 | } 139 | - (void)setSui_selectedBackgroundImage:(UIImage *)sui_selectedBackgroundImage 140 | { 141 | [self setBackgroundImage:sui_selectedBackgroundImage forState:UIControlStateSelected]; 142 | } 143 | 144 | 145 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 146 | * Disabled 147 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 148 | 149 | #pragma mark - Disabled 150 | 151 | - (NSString *)sui_disabledTitle 152 | { 153 | return [self titleForState:UIControlStateDisabled]; 154 | } 155 | - (void)setSui_disabledTitle:(NSString *)sui_disabledTitle 156 | { 157 | [self setTitle:sui_disabledTitle forState:UIControlStateDisabled]; 158 | } 159 | 160 | - (UIColor *)sui_disabledTitleColo 161 | { 162 | return [self titleColorForState:UIControlStateDisabled]; 163 | } 164 | - (void)setSui_disabledTitleColo:(UIColor *)sui_disabledTitleColo 165 | { 166 | [self setTitleColor:sui_disabledTitleColo forState:UIControlStateDisabled]; 167 | } 168 | 169 | - (UIImage *)sui_disabledImage 170 | { 171 | return [self imageForState:UIControlStateDisabled]; 172 | } 173 | - (void)setSui_disabledImage:(UIImage *)sui_disabledImage 174 | { 175 | [self setImage:sui_disabledImage forState:UIControlStateDisabled]; 176 | } 177 | 178 | - (UIImage *)sui_disabledBackgroundImage 179 | { 180 | return [self backgroundImageForState:UIControlStateDisabled]; 181 | } 182 | - (void)setSui_disabledBackgroundImage:(UIImage *)sui_disabledBackgroundImage 183 | { 184 | [self setBackgroundImage:sui_disabledBackgroundImage forState:UIControlStateDisabled]; 185 | } 186 | 187 | 188 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 189 | * Padding & Insets 190 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 191 | 192 | #pragma mark - Padding & Insets 193 | 194 | - (CGFloat)sui_padding 195 | { 196 | UIEdgeInsets curInsets = self.contentEdgeInsets; 197 | if (curInsets.left == curInsets.right) { 198 | return curInsets.left; 199 | } 200 | return 0; 201 | } 202 | - (void)setSui_padding:(CGFloat)sui_padding 203 | { 204 | self.contentEdgeInsets = UIEdgeInsetsMake(0, sui_padding, 0, sui_padding); 205 | [self sizeToFit]; 206 | } 207 | 208 | - (UIEdgeInsets)sui_insets 209 | { 210 | return self.contentEdgeInsets; 211 | } 212 | - (void)setSui_insets:(UIEdgeInsets)sui_insets 213 | { 214 | self.contentEdgeInsets = sui_insets; 215 | [self sizeToFit]; 216 | } 217 | 218 | 219 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 220 | * TintColor 221 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 222 | 223 | #pragma mark - TintColor 224 | 225 | - (UIColor *)sui_imageTintColor 226 | { 227 | return nil; 228 | } 229 | - (void)setSui_imageTintColor:(UIColor *)sui_imageTintColor 230 | { 231 | if (sui_imageTintColor) { 232 | UIImage *curImage = [[self currentImage] sui_imageWithGradientTintColor:sui_imageTintColor]; 233 | self.sui_normalImage = curImage; 234 | } 235 | } 236 | 237 | 238 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 239 | * Resizable 240 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 241 | 242 | #pragma mark - Resizable 243 | 244 | - (BOOL)sui_resizableImage 245 | { 246 | self.sui_resizableImage = YES; 247 | return YES; 248 | } 249 | - (void)setSui_resizableImage:(BOOL)sui_resizableImage 250 | { 251 | if (sui_resizableImage) { 252 | UIEdgeInsets curInsets = UIEdgeInsetsMake(self.currentImage.size.height/2-1, 253 | self.currentImage.size.width/2-1, 254 | self.currentImage.size.height/2, 255 | self.currentImage.size.width/2); 256 | self.sui_normalImage = [self.currentImage resizableImageWithCapInsets:curInsets resizingMode:UIImageResizingModeStretch]; 257 | } 258 | } 259 | 260 | - (BOOL)sui_resizableBackground 261 | { 262 | self.sui_resizableBackground = YES; 263 | return YES; 264 | } 265 | - (void)setSui_resizableBackground:(BOOL)sui_resizableBackground 266 | { 267 | if (sui_resizableBackground) { 268 | UIEdgeInsets curInsets = UIEdgeInsetsMake(self.currentBackgroundImage.size.height/2-1, 269 | self.currentBackgroundImage.size.width/2-1, 270 | self.currentBackgroundImage.size.height/2, 271 | self.currentBackgroundImage.size.width/2); 272 | self.sui_normalBackgroundImage = [self.currentBackgroundImage resizableImageWithCapInsets:curInsets resizingMode:UIImageResizingModeStretch]; 273 | } 274 | } 275 | 276 | #pragma mark - hexRgbColor 277 | - (NSString *)sui_titleHexColor 278 | { 279 | return @"0xffffff"; 280 | } 281 | 282 | - (void)setSui_titleHexColor:(NSString *)sui_titleHexColor { 283 | NSScanner *scanner = [NSScanner scannerWithString:sui_titleHexColor]; 284 | unsigned hexNum; 285 | if (![scanner scanHexInt:&hexNum]) return; 286 | [self setTitleColor:[self sui_colorWithRGBHex:hexNum] forState:UIControlStateNormal]; 287 | } 288 | 289 | - (UIColor *)sui_colorWithRGBHex:(UInt32)hex 290 | { 291 | int r = (hex >> 16) & 0xFF; 292 | int g = (hex >> 8) & 0xFF; 293 | int b = (hex) & 0xFF; 294 | 295 | return [UIColor colorWithRed:r / 255.0f 296 | green:g / 255.0f 297 | blue:b / 255.0f 298 | alpha:1.0f]; 299 | } 300 | 301 | @end 302 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIControl+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UIControl (SUIAdditions) 14 | 15 | 16 | @property (nonatomic) BOOL sui_enabled; 17 | @property (nonatomic) BOOL sui_selected; 18 | @property (nonatomic) BOOL sui_highlighted; 19 | 20 | - (void)sui_click:(void (^)(void))cb; 21 | - (void)sui_controlEvents:(UIControlEvents)controlEvents cb:(void (^)(void))cb; 22 | 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIControl+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UIControl+SUIAdditions.h" 10 | #import "NSObject+SUIAdditions.h" 11 | 12 | @implementation UIControl (SUIAdditions) 13 | 14 | 15 | - (BOOL)sui_enabled 16 | { 17 | return self.enabled; 18 | } 19 | - (void)setSui_enabled:(BOOL)sui_enabled 20 | { 21 | if (self.enabled != sui_enabled) { 22 | self.enabled = sui_enabled; 23 | } 24 | } 25 | 26 | - (BOOL)sui_selected 27 | { 28 | return self.selected; 29 | } 30 | - (void)setSui_selected:(BOOL)sui_selected 31 | { 32 | if (self.selected != sui_selected) { 33 | self.selected = sui_selected; 34 | } 35 | } 36 | 37 | - (BOOL)sui_highlighted 38 | { 39 | return self.highlighted; 40 | } 41 | - (void)setSui_highlighted:(BOOL)sui_highlighted 42 | { 43 | if (self.highlighted != sui_highlighted) { 44 | self.highlighted = sui_highlighted; 45 | } 46 | } 47 | 48 | 49 | - (void)sui_click:(void (^)(void))cb 50 | { 51 | [self sui_setAssociatedCopyObject:cb key:@selector(sui_handleClick)]; 52 | [self addTarget:self action:@selector(sui_handleClick) forControlEvents:UIControlEventTouchUpInside]; 53 | } 54 | - (void)sui_handleClick 55 | { 56 | void (^clickBlock)(void) = [self sui_getAssociatedObjectWithKey:_cmd]; 57 | if (clickBlock) clickBlock(); 58 | } 59 | 60 | - (void)sui_controlEvents:(UIControlEvents)controlEvents cb:(void (^)(void))cb 61 | { 62 | [self sui_setAssociatedCopyObject:cb key:@selector(sui_handleControlEvents)]; 63 | [self addTarget:self action:@selector(sui_handleClick) forControlEvents:controlEvents]; 64 | } 65 | - (void)sui_handleControlEvents 66 | { 67 | void (^controlEventsBlock)(void) = [self sui_getAssociatedObjectWithKey:_cmd]; 68 | if (controlEventsBlock) controlEventsBlock(); 69 | } 70 | 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIImage+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UIImage (SUIAdditions) 14 | 15 | 16 | - (UIImage * __null_unspecified)sui_imageWithTintColor:(UIColor *)tintColo; // kCGBlendModeDestinationIn 17 | - (UIImage * __null_unspecified)sui_imageWithGradientTintColor:(UIColor *)tintColo; // kCGBlendModeOverlay 18 | - (UIImage * __null_unspecified)sui_imageWithTintColor:(UIColor *)tintColo blendMode:(CGBlendMode)blendMode; 19 | 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIImage+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UIImage+SUIAdditions.h" 10 | 11 | @implementation UIImage (SUIAdditions) 12 | 13 | 14 | - (UIImage *)sui_imageWithTintColor:(UIColor *)tintColo 15 | { 16 | return [self sui_imageWithTintColor:tintColo blendMode:kCGBlendModeDestinationIn]; 17 | } 18 | 19 | - (UIImage *)sui_imageWithGradientTintColor:(UIColor *)tintColo 20 | { 21 | return [self sui_imageWithTintColor:tintColo blendMode:kCGBlendModeOverlay]; 22 | } 23 | 24 | - (UIImage *)sui_imageWithTintColor:(UIColor *)tintColo blendMode:(CGBlendMode)blendMode 25 | { 26 | //We want to keep alpha, set opaque to NO; Use 0.0f for scale to use the scale factor of the device’s main screen. 27 | UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); 28 | [tintColo setFill]; 29 | CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height); 30 | UIRectFill(bounds); 31 | 32 | //Draw the tinted image in context 33 | [self drawInRect:bounds blendMode:blendMode alpha:1.0f]; 34 | 35 | if (blendMode != kCGBlendModeDestinationIn) { 36 | [self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f]; 37 | } 38 | 39 | UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext(); 40 | UIGraphicsEndImageContext(); 41 | 42 | return tintedImage; 43 | } 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UILabel+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UILabel (SUIAdditions) 14 | 15 | /** 16 | * set text hex color 17 | */ 18 | @property (assign,nonatomic) IBInspectable NSString *sui_textHexColor; 19 | 20 | /** 21 | * 为UILabel添加中划线 22 | * 23 | * @param lineColor 划线颜色 24 | * @param lineTextColor 划线文本颜色 25 | * @param range 划线范围 26 | */ 27 | - (void)sui_addHorizontalLineWithColor:(UIColor *)lineColor lineTextColor:(UIColor *)lineTextColor range:(NSRange)range; 28 | 29 | - (CGFloat)sui_calculateHeight; 30 | 31 | - (CGSize)sui_calculateSize; 32 | 33 | 34 | @end 35 | 36 | NS_ASSUME_NONNULL_END 37 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UILabel+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UILabel+SUIAdditions.h" 10 | #import "NSString+SUIAdditions.h" 11 | #import "UIView+SUIAdditions.h" 12 | 13 | @implementation UILabel (SUIAdditions) 14 | 15 | 16 | - (CGFloat)sui_calculateHeight 17 | { 18 | return [self.text sui_heightWithFont:self.font constrainedToWidth:self.sui_width]; 19 | } 20 | 21 | - (CGSize)sui_calculateSize 22 | { 23 | return [self.text sui_sizeWithFont:self.font constrainedToWidth:self.sui_width]; 24 | } 25 | 26 | #pragma mark - hexRgbColor 27 | 28 | - (NSString *)sui_textHexColor 29 | { 30 | return @"0xffffff"; 31 | } 32 | 33 | - (void)setSui_textHexColor:(NSString *)sui_textHexColor { 34 | NSScanner *scanner = [NSScanner scannerWithString:sui_textHexColor]; 35 | unsigned hexNum; 36 | if (![scanner scanHexInt:&hexNum]) return; 37 | self.textColor = [self sui_colorWithRGBHex:hexNum]; 38 | } 39 | 40 | - (UIColor *)sui_colorWithRGBHex:(UInt32)hex 41 | { 42 | int r = (hex >> 16) & 0xFF; 43 | int g = (hex >> 8) & 0xFF; 44 | int b = (hex) & 0xFF; 45 | 46 | return [UIColor colorWithRed:r / 255.0f 47 | green:g / 255.0f 48 | blue:b / 255.0f 49 | alpha:1.0f]; 50 | } 51 | 52 | - (void)sui_addHorizontalLineWithColor:(UIColor *)lineColor lineTextColor:(UIColor *)lineTextColor range:(NSRange)range 53 | { 54 | 55 | NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]initWithString:self.text]; 56 | [attributedString addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range]; 57 | UIColor *textColor = nil; 58 | lineTextColor == nil ? (textColor = self.textColor) : (textColor = lineTextColor); 59 | [attributedString addAttribute:NSForegroundColorAttributeName value:textColor range:range]; 60 | [attributedString addAttribute:NSStrikethroughColorAttributeName value:lineColor range:range]; 61 | self.attributedText = attributedString; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UINavigationController+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationController+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UINavigationController (SUIAdditions) 14 | 15 | 16 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 17 | * Relationship 18 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 19 | 20 | #pragma mark - Relationship 21 | 22 | @property (nullable,readonly,copy) __kindof UIViewController *sui_rootVC; 23 | 24 | @property (readonly) BOOL sui_onlyHasRootVC; 25 | 26 | - (nullable __kindof UIViewController *)sui_findVC:(NSString *)className; 27 | 28 | - (nullable __kindof UIViewController *)sui_findReverseVC:(NSString *)className; 29 | 30 | 31 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 32 | * StoryboardLink 33 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 34 | 35 | #pragma mark - StoryboardLink 36 | 37 | @property (nullable,nonatomic,copy) IBInspectable NSString *sui_storyboardNameAndID; 38 | 39 | 40 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 41 | * Pop 42 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 43 | 44 | #pragma mark - Pop 45 | 46 | - (nullable NSArray<__kindof UIViewController *> *)sui_popToVC:(NSString *)className animated:(BOOL)animated; 47 | - (nullable NSArray<__kindof UIViewController *> *)sui_popToIndex:(NSUInteger)cIndex animated:(BOOL)animated; 48 | 49 | 50 | @end 51 | 52 | NS_ASSUME_NONNULL_END 53 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UINavigationController+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationController+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UINavigationController+SUIAdditions.h" 10 | #import "SUIMacro.h" 11 | #import "NSArray+SUIAdditions.h" 12 | #import "NSObject+SUIAdditions.h" 13 | 14 | @implementation UINavigationController (SUIAdditions) 15 | 16 | 17 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 18 | * Relationship 19 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 20 | 21 | #pragma mark - Relationship 22 | 23 | - (UIViewController *)sui_rootVC 24 | { 25 | if (self.viewControllers.count > 0) { 26 | UIViewController *curVC = [self.viewControllers firstObject]; 27 | return curVC; 28 | } 29 | return nil; 30 | } 31 | 32 | - (BOOL)sui_onlyHasRootVC 33 | { 34 | if (self.viewControllers.count == 1) { 35 | return YES; 36 | } 37 | return NO; 38 | } 39 | 40 | - (UIViewController *)sui_findVC:(NSString *)className 41 | { 42 | __block UIViewController *curVC = nil; 43 | [self.viewControllers sui_eachWithStop:^BOOL(__kindof UIViewController * _Nonnull obj, NSUInteger index) { 44 | if ([obj isKindOfClass:NSClassFromString(className)]) { 45 | curVC = obj; 46 | return YES; 47 | } 48 | return NO; 49 | }]; 50 | return curVC; 51 | } 52 | 53 | - (UIViewController *)sui_findReverseVC:(NSString *)className 54 | { 55 | __block UIViewController *curVC = nil; 56 | [self.viewControllers sui_eachReverseWithStop:^BOOL(__kindof UIViewController * _Nonnull obj, NSUInteger index) { 57 | if ([obj isKindOfClass:NSClassFromString(className)]) { 58 | curVC = obj; 59 | return YES; 60 | } 61 | return NO; 62 | }]; 63 | return curVC; 64 | } 65 | 66 | 67 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 68 | * StoryboardLink 69 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 70 | 71 | #pragma mark - StoryboardLink 72 | 73 | - (NSString *)sui_storyboardNameAndID 74 | { 75 | return [self sui_getAssociatedObjectWithKey:@selector(sui_storyboardNameAndID)]; 76 | } 77 | - (void)setSui_storyboardNameAndID:(NSString *)sui_storyboardNameAndID 78 | { 79 | [self sui_setAssociatedCopyObject:sui_storyboardNameAndID key:@selector(sui_storyboardNameAndID)]; 80 | [self sui_setRootViewController]; 81 | } 82 | 83 | - (void)sui_setRootViewController 84 | { 85 | NSArray *components = [self.sui_storyboardNameAndID componentsSeparatedByString:@"."]; 86 | NSString *curStoryboardName = nil; 87 | NSString *curStoryboardID = nil; 88 | if (components.count > 0) { 89 | curStoryboardName = components[0]; 90 | if (components.count > 1) { 91 | curStoryboardID = components[1]; 92 | } 93 | } 94 | 95 | if (curStoryboardName) { 96 | UIViewController *curRootVC = nil; 97 | if (curStoryboardID) { 98 | curRootVC = gStoryboardInstantiate(curStoryboardName, curStoryboardID); 99 | } else { 100 | curRootVC = gStoryboardInitialViewController(curStoryboardName); 101 | } 102 | SUIAssert(curRootVC, @"check storyboardNameAndID"); 103 | [self setViewControllers:@[curRootVC] animated:NO]; 104 | } 105 | } 106 | 107 | 108 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 109 | * Pop 110 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 111 | 112 | #pragma mark - Pop 113 | 114 | - (NSArray *)sui_popToVC:(NSString *)className animated:(BOOL)animated 115 | { 116 | UIViewController *curVC = [self sui_findReverseVC:className]; 117 | NSArray *curAry = [self popToViewController:curVC animated:animated]; 118 | return curAry; 119 | } 120 | 121 | - (NSArray *)sui_popToIndex:(NSUInteger)cIndex animated:(BOOL)animated 122 | { 123 | NSUInteger curCount = self.viewControllers.count; 124 | if (cIndex < curCount-1) { 125 | UIViewController *curVC = self.viewControllers[cIndex]; 126 | NSArray *curAry = [self popToViewController:curVC animated:animated]; 127 | return curAry; 128 | } 129 | return self.viewControllers; 130 | } 131 | 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIScrollView+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UIScrollView (SUIAdditions) 14 | 15 | 16 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 17 | * Content 18 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 19 | 20 | #pragma mark - Content 21 | 22 | @property (nonatomic) CGFloat sui_contentWidth; 23 | @property (nonatomic) CGFloat sui_contentHeight; 24 | @property (nonatomic) CGFloat sui_contentOffsetX; 25 | @property (nonatomic) CGFloat sui_contentOffsetY; 26 | 27 | @property (readonly) CGFloat sui_contentRealTop; 28 | @property (readonly) CGFloat sui_contentRealBottom; 29 | @property (readonly) CGFloat sui_contentRealLeft; 30 | @property (readonly) CGFloat sui_contentRealRight; 31 | 32 | 33 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 34 | * Scroll 35 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 36 | 37 | #pragma mark - Scroll 38 | 39 | @property (readonly) BOOL sui_isScrollToTop; 40 | @property (readonly) BOOL sui_isScrollToBottom; 41 | @property (readonly) BOOL sui_isScrollToLeft; 42 | @property (readonly) BOOL sui_isScrollToRight; 43 | 44 | - (void)sui_scrollToTopAnimated:(BOOL)animated; 45 | - (void)sui_scrollToBottomAnimated:(BOOL)animated; 46 | - (void)sui_scrollToLeftAnimated:(BOOL)animated; 47 | - (void)sui_scrollToRightAnimated:(BOOL)animated; 48 | 49 | 50 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 51 | * PageIndex 52 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 53 | 54 | #pragma mark - PageIndex 55 | 56 | @property (readonly) NSUInteger sui_pageIndexVartical; 57 | @property (readonly) NSUInteger sui_pageIndexHorizontal; 58 | @property (readonly) NSUInteger sui_pageTotalVartical; 59 | @property (readonly) NSUInteger sui_pageTotalHorizontal; 60 | 61 | - (void)sui_scrollToPageIndexVartical:(NSUInteger)pageIndex animated:(BOOL)animated; 62 | - (void)sui_scrollToPageIndexHorizontal:(NSUInteger)pageIndex animated:(BOOL)animated; 63 | 64 | 65 | @end 66 | 67 | NS_ASSUME_NONNULL_END 68 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIScrollView+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UIScrollView+SUIAdditions.h" 10 | #import "UIView+SUIAdditions.h" 11 | 12 | @implementation UIScrollView (SUIAdditions) 13 | 14 | 15 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 16 | * Content 17 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 18 | 19 | #pragma mark - Content 20 | 21 | - (CGFloat)sui_contentWidth 22 | { 23 | return self.contentSize.width; 24 | } 25 | - (void)setSui_contentWidth:(CGFloat)sui_contentWidth 26 | { 27 | CGSize curSize = self.contentSize; 28 | if (curSize.width != sui_contentWidth) { 29 | curSize.width = sui_contentWidth; 30 | self.contentSize = curSize; 31 | } 32 | } 33 | 34 | - (CGFloat)sui_contentHeight 35 | { 36 | return self.contentSize.height; 37 | } 38 | - (void)setSui_contentHeight:(CGFloat)sui_contentHeight 39 | { 40 | CGSize curSize = self.contentSize; 41 | if (curSize.height != sui_contentHeight) { 42 | curSize.height = sui_contentHeight; 43 | self.contentSize = curSize; 44 | } 45 | } 46 | 47 | - (CGFloat)sui_contentOffsetX 48 | { 49 | return self.contentOffset.x; 50 | } 51 | - (void)setSui_contentOffsetX:(CGFloat)sui_contentOffsetX 52 | { 53 | CGPoint curPoint = self.contentOffset; 54 | if (curPoint.x != sui_contentOffsetX) { 55 | curPoint.x = sui_contentOffsetX; 56 | self.contentOffset = curPoint; 57 | } 58 | } 59 | 60 | - (CGFloat)sui_contentOffsetY 61 | { 62 | return self.contentOffset.y; 63 | } 64 | - (void)setSui_contentOffsetY:(CGFloat)sui_contentOffsetY 65 | { 66 | CGPoint curPoint = self.contentOffset; 67 | if (curPoint.y != sui_contentOffsetY) { 68 | curPoint.y = sui_contentOffsetY; 69 | self.contentOffset = curPoint; 70 | } 71 | } 72 | 73 | - (CGFloat)sui_contentRealTop 74 | { 75 | return -self.contentInset.top; 76 | } 77 | - (CGFloat)sui_contentRealBottom 78 | { 79 | return self.contentSize.height + self.contentInset.bottom - self.bounds.size.height; 80 | } 81 | - (CGFloat)sui_contentRealLeft 82 | { 83 | return -self.contentInset.left; 84 | } 85 | - (CGFloat)sui_contentRealRight 86 | { 87 | return self.contentSize.width + self.contentInset.right - self.bounds.size.width; 88 | } 89 | 90 | 91 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 92 | * Scroll 93 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 94 | 95 | #pragma mark - Scroll 96 | 97 | - (BOOL)sui_isScrollToTop 98 | { 99 | return self.sui_contentOffsetY <= self.sui_contentRealTop; 100 | } 101 | - (BOOL)sui_isScrollToBottom 102 | { 103 | return self.sui_contentOffsetY >= self.sui_contentRealBottom; 104 | } 105 | - (BOOL)sui_isScrollToLeft 106 | { 107 | return self.sui_contentOffsetX <= self.sui_contentRealLeft; 108 | } 109 | - (BOOL)sui_isScrollToRight 110 | { 111 | return self.sui_contentOffsetX >= self.sui_contentRealRight; 112 | } 113 | 114 | - (void)sui_scrollToTopAnimated:(BOOL)animated 115 | { 116 | [self setContentOffset:CGPointMake(0, self.sui_contentRealTop) animated:animated]; 117 | } 118 | - (void)sui_scrollToBottomAnimated:(BOOL)animated 119 | { 120 | [self setContentOffset:CGPointMake(0, self.sui_contentRealBottom) animated:animated]; 121 | } 122 | - (void)sui_scrollToLeftAnimated:(BOOL)animated 123 | { 124 | [self setContentOffset:CGPointMake(self.sui_contentRealLeft, 0) animated:animated]; 125 | } 126 | - (void)sui_scrollToRightAnimated:(BOOL)animated 127 | { 128 | [self setContentOffset:CGPointMake(self.sui_contentRealRight, 0) animated:animated]; 129 | } 130 | 131 | 132 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 133 | * PageIndex 134 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 135 | 136 | #pragma mark - PageIndex 137 | 138 | - (NSUInteger)sui_pageIndexVartical 139 | { 140 | NSUInteger curPageIndex = self.sui_contentOffsetY / self.sui_height; 141 | return curPageIndex; 142 | } 143 | - (NSUInteger)sui_pageIndexHorizontal 144 | { 145 | NSUInteger curPageIndex = self.sui_contentOffsetX / self.sui_width; 146 | return curPageIndex; 147 | } 148 | - (NSUInteger)sui_pageTotalVartical 149 | { 150 | NSUInteger curPageTotal = self.sui_contentHeight / self.sui_height; 151 | return curPageTotal; 152 | } 153 | - (NSUInteger)sui_pageTotalHorizontal 154 | { 155 | NSUInteger curPageTotal = self.sui_contentWidth / self.sui_width; 156 | return curPageTotal; 157 | } 158 | 159 | - (void)sui_scrollToPageIndexVartical:(NSUInteger)pageIndex animated:(BOOL)animated 160 | { 161 | [self setContentOffset:CGPointMake(0, self.sui_height * pageIndex) animated:animated]; 162 | } 163 | - (void)sui_scrollToPageIndexHorizontal:(NSUInteger)pageIndex animated:(BOOL)animated 164 | { 165 | [self setContentOffset:CGPointMake(self.sui_width * pageIndex, 0) animated:animated]; 166 | } 167 | 168 | 169 | @end 170 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIStoryboardSegue+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIStoryboardSegue+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/19. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIStoryboardSegue (SUIAdditions) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIStoryboardSegue+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIStoryboardSegue+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/19. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UIStoryboardSegue+SUIAdditions.h" 10 | #import "SUIMacro.h" 11 | #import 12 | 13 | @implementation UIStoryboardSegue (SUIAdditions) 14 | 15 | // 16 | //+ (void)load 17 | //{ 18 | // static dispatch_once_t sui_onceToken; 19 | // dispatch_once(&sui_onceToken, ^{ 20 | // 21 | // Method originMethod = class_getInstanceMethod([self class], @selector(perform)); 22 | // Method swizzMthod = class_getInstanceMethod([self class], @selector(sui_perform)); 23 | // 24 | // BOOL didAdd = class_addMethod([self class], @selector(perform), 25 | // method_getImplementation(swizzMthod), method_getTypeEncoding(swizzMthod)); 26 | // 27 | // if (didAdd) { 28 | // class_replaceMethod([self class], @selector(sui_perform), 29 | // method_getImplementation(originMethod),method_getTypeEncoding(originMethod)); 30 | // } else { 31 | // method_exchangeImplementations(originMethod, swizzMthod); 32 | // } 33 | // }); 34 | //} 35 | // 36 | //- (void)sui_perform 37 | //{ 38 | // [self sui_perform]; 39 | // 40 | // SUILogObj(self.sourceViewController); 41 | // SUILogObj(self.destinationViewController); 42 | // SUILogLine 43 | //} 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UITableViewCell+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/20. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UITableViewCell (SUIAdditions) 14 | 15 | 16 | @property (nonatomic,weak) UITableView *sui_tableView; 17 | 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UITableViewCell+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewCell+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/20. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UITableViewCell+SUIAdditions.h" 10 | #import "NSObject+SUIAdditions.h" 11 | #import "UIView+SUIAdditions.h" 12 | 13 | @implementation UITableViewCell (SUIAdditions) 14 | 15 | 16 | - (UITableView *)sui_tableView 17 | { 18 | UITableView *curTableView = [self sui_getAssociatedObjectWithKey:@selector(sui_tableView)]; 19 | if (curTableView) return curTableView; 20 | 21 | curTableView = [self sui_findSupview:@"UITableView"]; 22 | if (curTableView) { 23 | self.sui_tableView = curTableView; 24 | } 25 | return curTableView; 26 | } 27 | - (void)setSui_tableView:(UITableView *)sui_tableView 28 | { 29 | [self sui_setAssociatedAssignObject:sui_tableView key:@selector(sui_tableView)]; 30 | } 31 | 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UITextField+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UITextField (SUIAdditions) 14 | 15 | 16 | @property (nonatomic) IBInspectable BOOL sui_showKeyboard; 17 | 18 | - (void)sui_dismissKeyboard; 19 | 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UITextField+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UITextField+SUIAdditions.h" 10 | 11 | @implementation UITextField (SUIAdditions) 12 | 13 | 14 | - (BOOL)sui_showKeyboard 15 | { 16 | if (![self isFirstResponder]) 17 | { 18 | return [self becomeFirstResponder]; 19 | } 20 | return YES; 21 | } 22 | - (void)setSui_showKeyboard:(BOOL)sui_showKeyboard 23 | { 24 | if (sui_showKeyboard && ![self isFirstResponder]) 25 | { 26 | [self becomeFirstResponder]; 27 | } 28 | } 29 | 30 | - (void)sui_dismissKeyboard 31 | { 32 | if (self.isFirstResponder) 33 | { 34 | [self resignFirstResponder]; 35 | } 36 | } 37 | 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UITextView+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITextView+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UITextView (SUIAdditions) 14 | 15 | 16 | @property (nonatomic) IBInspectable BOOL sui_showKeyboard; 17 | 18 | - (void)sui_dismissKeyboard; 19 | 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UITextView+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITextView+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/18. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UITextView+SUIAdditions.h" 10 | 11 | @implementation UITextView (SUIAdditions) 12 | 13 | 14 | - (BOOL)sui_showKeyboard 15 | { 16 | if (![self isFirstResponder]) 17 | { 18 | return [self becomeFirstResponder]; 19 | } 20 | return YES; 21 | } 22 | - (void)setSui_showKeyboard:(BOOL)sui_showKeyboard 23 | { 24 | if (sui_showKeyboard && ![self isFirstResponder]) 25 | { 26 | [self becomeFirstResponder]; 27 | } 28 | } 29 | 30 | - (void)sui_dismissKeyboard 31 | { 32 | if (self.isFirstResponder) 33 | { 34 | [self resignFirstResponder]; 35 | } 36 | } 37 | 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIView+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/16. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UIView (SUIAdditions) 14 | 15 | 16 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 17 | * Frame 18 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 19 | 20 | #pragma mark - Frame 21 | 22 | @property (nonatomic) CGFloat sui_x; 23 | @property (nonatomic) CGFloat sui_y; 24 | @property (nonatomic) CGFloat sui_width; 25 | @property (nonatomic) CGFloat sui_height; 26 | @property (nonatomic) CGPoint sui_origin; 27 | @property (nonatomic) CGSize sui_size; 28 | @property (readonly) CGFloat sui_right; // (x + width). 29 | @property (readonly) CGFloat sui_bottom; // (y + height). 30 | @property (nonatomic) CGFloat sui_centerX; // (x + width/2). 31 | @property (nonatomic) CGFloat sui_centerY; // (y + height/2). 32 | 33 | 34 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 35 | * Constraint 36 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 37 | 38 | #pragma mark - Constraint 39 | 40 | - (nullable NSLayoutConstraint *)sui_constraintTop; 41 | - (nullable NSLayoutConstraint *)sui_constraintBottom; 42 | - (nullable NSLayoutConstraint *)sui_constraintLeading; 43 | - (nullable NSLayoutConstraint *)sui_constraintTrailing; 44 | - (nullable NSLayoutConstraint *)sui_constraintWidth; 45 | - (nullable NSLayoutConstraint *)sui_constraintHeight; 46 | - (nullable NSLayoutConstraint *)sui_constraintCenterX; 47 | - (nullable NSLayoutConstraint *)sui_constraintCenterY; 48 | - (nullable NSLayoutConstraint *)sui_constraintBaseline; 49 | - (void)sui_layoutPinnedToSuperview; 50 | 51 | 52 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 53 | * Nib 54 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 55 | 56 | #pragma mark - Nib 57 | 58 | @property (nonatomic) IBInspectable BOOL sui_loadNib; 59 | 60 | + (instancetype)sui_loadInstanceFromNib; 61 | + (instancetype)sui_loadInstanceFromNibWithName:(NSString *)nibName; 62 | + (instancetype)sui_loadInstanceFromNibWithName:(NSString *)nibName owner:(nullable id)owner; 63 | + (instancetype)sui_loadInstanceFromNibWithName:(NSString *)nibName owner:(nullable id)owner bundle:(NSBundle *)bundle; 64 | 65 | 66 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 67 | * Layer 68 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 69 | 70 | #pragma mark - Layer 71 | 72 | @property (nonatomic) IBInspectable CGFloat sui_cornerRadius; // Defaults to zero. 73 | 74 | @property (nonatomic) CGFloat sui_borderWidth; // Defaults to zero. 75 | @property (nonatomic,copy) UIColor *sui_borderColor; // Defaults to opaque black. 76 | 77 | @property (nonatomic,copy) UIColor *sui_shadowColor; // Defaults to opaque black 78 | @property (nonatomic) CGFloat sui_shadowOpacity; // Defaults to 0. [0,1] 79 | @property (nonatomic) CGSize sui_shadowOffset; // Defaults to (0, -3). 80 | @property (nonatomic) CGFloat sui_shadowRadius; // Defaults to 3. 81 | @property (nonatomic) BOOL sui_shadowPath; // Defaults to NO. When using animation set YES. 82 | 83 | 84 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 85 | * Relationship 86 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 87 | 88 | #pragma mark - Relationship 89 | 90 | @property (nullable,readonly,copy) __kindof UIViewController *sui_currentVC; 91 | 92 | - (nullable __kindof UIView *)sui_findSubview:(NSString *)className; 93 | - (nullable __kindof UIView *)sui_findSupview:(NSString *)className; 94 | 95 | 96 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 97 | * GestureRecognizer 98 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 99 | 100 | #pragma mark - GestureRecognizer 101 | 102 | - (void)sui_addTapGes:(void (^)(UITapGestureRecognizer *cTapGes))cb; 103 | - (void)sui_addLongPressGes:(void (^)(UILongPressGestureRecognizer *cLongPressGes))cb; 104 | 105 | 106 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 107 | * Snapshot 108 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 109 | 110 | #pragma mark - Snapshot 111 | 112 | - (nullable UIView *)sui_snapshotView:(BOOL)arterUpdates; 113 | - (null_unspecified UIImage *)sui_snapshotImage:(BOOL)arterUpdates; 114 | - (null_unspecified UIImage *)sui_snapshotWithRenderInContext; 115 | 116 | 117 | @end 118 | 119 | NS_ASSUME_NONNULL_END 120 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIViewController+SUIAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+SUIAdditions.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/17. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, SUISegueType) { 12 | SUISegueTypePush = 0, 13 | SUISegueTypeModal = 1 14 | }; 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | @interface UIViewController (SUIAdditions) 19 | 20 | 21 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 22 | * Relationship 23 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 24 | 25 | #pragma mark - Relationship 26 | 27 | @property (nullable,readonly,copy) NSString *sui_identifier; 28 | @property (nullable,nonatomic,strong) __kindof UITableView *sui_tableView; 29 | @property (nullable,nonatomic,weak) __kindof UIViewController *sui_sourceVC; 30 | 31 | 32 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 33 | * NavBack 34 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 35 | 36 | #pragma mark - NavBack 37 | 38 | - (IBAction)sui_backToLast; 39 | - (IBAction)sui_backToRoot; 40 | 41 | 42 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 43 | * StoryboardLink 44 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 45 | 46 | #pragma mark - StoryboardLink 47 | 48 | - (void)sui_storyboardSegueWithIdentifier:(NSString *)cIdentifier; 49 | - (void)sui_storyboardInstantiate:(NSString *)sui_storyboardNameAndID; 50 | - (void)sui_storyboardInstantiate:(NSString *)sui_storyboardNameAndID segueType:(SUISegueType)cType; 51 | - (void)sui_storyboardInstantiateWithStoryboard:(UIStoryboard *)cStoryboard storyboardID:(nullable NSString *)cStoryboardID segueType:(SUISegueType)cType; 52 | 53 | - (void)sui_prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender; 54 | 55 | /** 56 | * 返回storybosrd中指定identifer的控制器 57 | * 58 | * @param storyboardName 控制器所在的storyboard 59 | * @param identifier 控制器在storyboard中指定的标识符 60 | * 61 | * @return 指定identifer的控制器 62 | */ 63 | + (__kindof UIViewController *)sui_viewControllerWithStoryboard:(nullable NSString *)storyboard identifier:(NSString *)identifier; 64 | 65 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 66 | * Geometry 67 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 68 | 69 | #pragma mark - Geometry 70 | 71 | @property (readonly) CGFloat sui_opaqueNavBarHeight; 72 | @property (readonly) CGFloat sui_translucentNavBarHeight; 73 | @property (readonly) CGFloat sui_opaqueTabBarHeight; 74 | @property (readonly) CGFloat sui_translucentTabBarHeight; 75 | @property (readonly) CGRect sui_viewFrame; 76 | 77 | 78 | @end 79 | 80 | 81 | 82 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 83 | * UITableView 84 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 85 | 86 | @interface UITableView (SUIViewController) 87 | 88 | 89 | @property (nullable,nonatomic,weak) UIViewController *sui_vc; 90 | 91 | 92 | @end 93 | 94 | NS_ASSUME_NONNULL_END 95 | -------------------------------------------------------------------------------- /SUIUtils/UIKit/UIViewController+SUIAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+SUIAdditions.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/17. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+SUIAdditions.h" 10 | #import "SUIMacro.h" 11 | #import "UIView+SUIAdditions.h" 12 | #import "NSObject+SUIAdditions.h" 13 | #import "NSString+SUIRegex.h" 14 | 15 | @implementation UIViewController (SUIAdditions) 16 | 17 | 18 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 19 | * Relationship 20 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 21 | 22 | #pragma mark - Relationship 23 | 24 | - (NSString *)sui_identifier 25 | { 26 | NSString *curIdentifier = [self sui_getAssociatedObjectWithKey:_cmd]; 27 | if (curIdentifier) return curIdentifier; 28 | 29 | NSString *curClassName = NSStringFromClass([self class]); 30 | curIdentifier = [curClassName sui_regex:@"(?<=^SUI)\\S+(?=VC$)"]; 31 | SUIAssert(curIdentifier, @"className should prefix with 'SUI' and suffix with 'VC'"); 32 | 33 | if (!kNilOrNull(curClassName)) { 34 | [self sui_setAssociatedCopyObject:curClassName key:_cmd]; 35 | } 36 | return curIdentifier; 37 | } 38 | 39 | - (UITableView *)sui_tableView 40 | { 41 | UITableView *curTableView = [self sui_getAssociatedObjectWithKey:@selector(sui_tableView)]; 42 | if (curTableView) return curTableView; 43 | 44 | if ([self isKindOfClass:[UITableViewController class]]) { 45 | curTableView = (UITableView *)self.view; 46 | } else { 47 | curTableView = [self.view sui_findSubview:@"UITableView"]; 48 | } 49 | 50 | if (curTableView) self.sui_tableView = curTableView; 51 | return curTableView; 52 | } 53 | - (void)setSui_tableView:(UITableView *)sui_tableView 54 | { 55 | sui_tableView.sui_vc = self; 56 | [self sui_setAssociatedRetainObject:sui_tableView key:@selector(sui_tableView)]; 57 | } 58 | 59 | - (UIViewController *)sui_sourceVC 60 | { 61 | __block UIViewController *curVC = [self sui_getAssociatedObjectWithKey:@selector(sui_sourceVC)]; 62 | if (curVC) return curVC; 63 | 64 | if (self.navigationController) { 65 | __block BOOL curFlag = NO; 66 | [self.navigationController.viewControllers enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 67 | if (curFlag) { 68 | curVC = obj; 69 | self.sui_sourceVC = curVC; 70 | *stop = YES; 71 | } 72 | if (obj == self) { 73 | curFlag = YES; 74 | } 75 | }]; 76 | } 77 | return curVC; 78 | } 79 | - (void)setSui_sourceVC:(UIViewController *)sui_sourceVC 80 | { 81 | [self sui_setAssociatedAssignObject:sui_sourceVC key:@selector(sui_sourceVC)]; 82 | } 83 | 84 | 85 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 86 | * NavBack 87 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 88 | 89 | #pragma mark - NavBack 90 | 91 | - (IBAction)sui_popToLastVC:(UIStoryboardSegue *)unwindSegue {} 92 | 93 | - (IBAction)sui_backToLast 94 | { 95 | if (self.navigationController) { 96 | if (self.navigationController.viewControllers.count == 1) { 97 | [self.navigationController dismissViewControllerAnimated:YES completion:^{ 98 | }]; 99 | } else { 100 | [self.navigationController popViewControllerAnimated:YES]; 101 | } 102 | } else { 103 | [self dismissViewControllerAnimated:YES completion:^{ 104 | }]; 105 | } 106 | } 107 | 108 | - (IBAction)sui_backToRoot 109 | { 110 | [self.navigationController popToRootViewControllerAnimated:YES]; 111 | } 112 | 113 | 114 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 115 | * StoryboardLink 116 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 117 | 118 | #pragma mark - StoryboardLink 119 | 120 | - (void)sui_storyboardSegueWithIdentifier:(NSString *)cIdentifier 121 | { 122 | [self performSegueWithIdentifier:cIdentifier sender:self]; 123 | } 124 | - (void)sui_storyboardInstantiate:(NSString *)sui_storyboardNameAndID 125 | { 126 | [self sui_storyboardInstantiate:sui_storyboardNameAndID segueType:SUISegueTypePush]; 127 | } 128 | - (void)sui_storyboardInstantiate:(NSString *)sui_storyboardNameAndID segueType:(SUISegueType)cType 129 | { 130 | NSArray *components = [sui_storyboardNameAndID componentsSeparatedByString:@"."]; 131 | NSString *curStoryboardName = nil; 132 | NSString *curStoryboardID = nil; 133 | if (components.count == 1) { 134 | curStoryboardID = components[0]; 135 | } else if (components.count > 1) { 136 | curStoryboardName = components[0]; 137 | curStoryboardID = components[1]; 138 | } 139 | 140 | UIStoryboard *curStoryboard = nil; 141 | if (curStoryboardName) { 142 | curStoryboard = gStoryboardNamed(curStoryboardName); 143 | } else { 144 | curStoryboard = self.storyboard; 145 | } 146 | [self sui_storyboardInstantiateWithStoryboard:curStoryboard storyboardID:curStoryboardID segueType:cType]; 147 | } 148 | - (void)sui_storyboardInstantiateWithStoryboard:(UIStoryboard *)cStoryboard storyboardID:(NSString *)cStoryboardID segueType:(SUISegueType)cType 149 | { 150 | UIViewController *curVC = nil; 151 | if (cStoryboardID) { 152 | curVC = [cStoryboard instantiateViewControllerWithIdentifier:cStoryboardID]; 153 | } else { 154 | curVC = cStoryboard.instantiateInitialViewController; 155 | } 156 | 157 | if ([curVC isKindOfClass:[UINavigationController class]]) { 158 | UINavigationController *curNav = (UINavigationController *)curVC; 159 | curNav.topViewController.sui_sourceVC = self; 160 | [self presentViewController:curVC animated:YES completion:nil]; 161 | } else { 162 | curVC.sui_sourceVC = self; 163 | if (cType == SUISegueTypePush) { 164 | [self.navigationController pushViewController:curVC animated:YES]; 165 | } else { 166 | [self presentViewController:curVC animated:YES completion:nil]; 167 | } 168 | } 169 | } 170 | 171 | __attribute__((constructor)) 172 | void sui_segue(void) { 173 | Class curClass = [UIViewController class]; 174 | SEL originSel = @selector(prepareForSegue:sender:); 175 | SEL swizzSel = @selector(sui_prepareForSegue:sender:); 176 | Method originMethod = class_getInstanceMethod(curClass, originSel); 177 | Method swizzMthod = class_getInstanceMethod(curClass, swizzSel); 178 | 179 | BOOL didAdd = class_addMethod(curClass, 180 | originSel, 181 | method_getImplementation(swizzMthod), 182 | method_getTypeEncoding(swizzMthod)); 183 | if (didAdd) { 184 | class_replaceMethod(curClass, 185 | swizzSel, 186 | method_getImplementation(originMethod), 187 | method_getTypeEncoding(originMethod)); 188 | } else { 189 | method_exchangeImplementations(originMethod, swizzMthod); 190 | } 191 | } 192 | 193 | - (void)sui_prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 194 | { 195 | [self sui_prepareForSegue:segue sender:sender]; 196 | 197 | UIViewController *sourceVC = segue.sourceViewController; 198 | UIViewController *destVC = segue.destinationViewController; 199 | 200 | if ([destVC isKindOfClass:[UINavigationController class]]) { 201 | UINavigationController *curNav = (UINavigationController *)destVC; 202 | curNav.topViewController.sui_sourceVC = sourceVC; 203 | } else { 204 | destVC.sui_sourceVC = sourceVC; 205 | destVC.hidesBottomBarWhenPushed = YES; 206 | } 207 | } 208 | 209 | + (UIViewController *)sui_viewControllerWithStoryboard:(nullable NSString *)storyboard identifier:(NSString *)identifier { 210 | NSString *storyboardName = storyboard ?: @"Main"; 211 | UIStoryboard *sb = [UIStoryboard storyboardWithName:storyboardName bundle:nil]; 212 | return [sb instantiateViewControllerWithIdentifier:identifier]; 213 | } 214 | 215 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 216 | * Geometry 217 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 218 | 219 | #pragma mark - Geometry 220 | 221 | - (CGFloat)sui_opaqueNavBarHeight 222 | { 223 | CGFloat curNavBarHeight = 0; 224 | UINavigationBar *curNavigationBar = self.navigationController.navigationBar; 225 | if (!curNavigationBar.translucent) { 226 | curNavBarHeight += curNavigationBar.bounds.size.height; 227 | if (![self prefersStatusBarHidden]) { 228 | curNavBarHeight += [UIApplication sharedApplication].statusBarFrame.size.height; 229 | } 230 | } 231 | return curNavBarHeight; 232 | } 233 | - (CGFloat)sui_translucentNavBarHeight 234 | { 235 | CGFloat curNavBarHeight = 0; 236 | UINavigationBar *curNavigationBar = self.navigationController.navigationBar; 237 | if (curNavigationBar.translucent) { 238 | curNavBarHeight += curNavigationBar.bounds.size.height; 239 | if (![self prefersStatusBarHidden]) { 240 | curNavBarHeight += [UIApplication sharedApplication].statusBarFrame.size.height; 241 | } 242 | } 243 | return curNavBarHeight; 244 | } 245 | 246 | - (CGFloat)sui_opaqueTabBarHeight 247 | { 248 | UITabBar *curTabBar = self.navigationController.tabBarController.tabBar; 249 | if (!curTabBar.translucent && ![self hidesBottomBarWhenPushed]) { 250 | return curTabBar.bounds.size.height; 251 | } 252 | return 0; 253 | } 254 | - (CGFloat)sui_translucentTabBarHeight 255 | { 256 | UITabBar *curTabBar = self.navigationController.tabBarController.tabBar; 257 | if (curTabBar.translucent && ![self hidesBottomBarWhenPushed]) { 258 | return curTabBar.bounds.size.height; 259 | } 260 | return 0; 261 | } 262 | 263 | - (CGRect)sui_viewFrame 264 | { 265 | return CGRectMake(0, 0, self.view.sui_width, kScreenHeight-self.sui_opaqueNavBarHeight-self.sui_opaqueTabBarHeight); 266 | } 267 | 268 | 269 | @end 270 | 271 | 272 | 273 | /*o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o* 274 | * UITableView 275 | *o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~o~*/ 276 | 277 | @implementation UITableView (SUIViewController) 278 | 279 | 280 | - (UIViewController *)sui_vc 281 | { 282 | UIViewController *curVC = [self sui_getAssociatedObjectWithKey:@selector(sui_vc)]; 283 | if (curVC) return curVC; 284 | 285 | curVC = [self sui_currentVC]; 286 | if (curVC) { 287 | self.sui_vc = curVC; 288 | } 289 | return curVC; 290 | } 291 | 292 | - (void)setSui_vc:(UIViewController *)sui_vc 293 | { 294 | [self sui_setAssociatedAssignObject:sui_vc key:@selector(sui_vc)]; 295 | } 296 | 297 | 298 | @end 299 | -------------------------------------------------------------------------------- /SUIUtils/UITableView+FDTemplateLayoutCell/UITableView+FDIndexPathHeightCache.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 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 | #import 24 | 25 | @interface FDIndexPathHeightCache : NSObject 26 | 27 | // Enable automatically if you're using index path driven height cache 28 | @property (nonatomic, assign) BOOL automaticallyInvalidateEnabled; 29 | 30 | // Height cache 31 | - (BOOL)existsHeightAtIndexPath:(NSIndexPath *)indexPath; 32 | - (void)cacheHeight:(CGFloat)height byIndexPath:(NSIndexPath *)indexPath; 33 | - (CGFloat)heightForIndexPath:(NSIndexPath *)indexPath; 34 | - (void)invalidateHeightAtIndexPath:(NSIndexPath *)indexPath; 35 | - (void)invalidateAllHeightCache; 36 | 37 | @end 38 | 39 | @interface UITableView (FDIndexPathHeightCache) 40 | /// Height cache by index path. Generally, you don't need to use it directly. 41 | @property (nonatomic, strong, readonly) FDIndexPathHeightCache *fd_indexPathHeightCache; 42 | @end 43 | 44 | @interface UITableView (FDIndexPathHeightCacheInvalidation) 45 | /// Call this method when you want to reload data but don't want to invalidate 46 | /// all height cache by index path, for example, load more data at the bottom of 47 | /// table view. 48 | - (void)fd_reloadDataWithoutInvalidateIndexPathHeightCache; 49 | @end 50 | -------------------------------------------------------------------------------- /SUIUtils/UITableView+FDTemplateLayoutCell/UITableView+FDKeyedHeightCache.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 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 | #import 24 | 25 | @interface FDKeyedHeightCache : NSObject 26 | 27 | - (BOOL)existsHeightForKey:(id)key; 28 | - (void)cacheHeight:(CGFloat)height byKey:(id)key; 29 | - (CGFloat)heightForKey:(id)key; 30 | 31 | // Invalidation 32 | - (void)invalidateHeightForKey:(id)key; 33 | - (void)invalidateAllHeightCache; 34 | @end 35 | 36 | @interface UITableView (FDKeyedHeightCache) 37 | 38 | /// Height cache by key. Generally, you don't need to use it directly. 39 | @property (nonatomic, strong, readonly) FDKeyedHeightCache *fd_keyedHeightCache; 40 | @end 41 | -------------------------------------------------------------------------------- /SUIUtils/UITableView+FDTemplateLayoutCell/UITableView+FDKeyedHeightCache.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 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 | #import "UITableView+FDKeyedHeightCache.h" 24 | #import 25 | 26 | @interface FDKeyedHeightCache () 27 | @property (nonatomic, strong) NSMutableDictionary, NSNumber *> *mutableHeightsByKeyForPortrait; 28 | @property (nonatomic, strong) NSMutableDictionary, NSNumber *> *mutableHeightsByKeyForLandscape; 29 | @end 30 | 31 | @implementation FDKeyedHeightCache 32 | 33 | - (instancetype)init { 34 | self = [super init]; 35 | if (self) { 36 | _mutableHeightsByKeyForPortrait = [NSMutableDictionary dictionary]; 37 | _mutableHeightsByKeyForLandscape = [NSMutableDictionary dictionary]; 38 | } 39 | return self; 40 | } 41 | 42 | - (NSMutableDictionary, NSNumber *> *)mutableHeightsByKeyForCurrentOrientation { 43 | return UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation) ? self.mutableHeightsByKeyForPortrait: self.mutableHeightsByKeyForLandscape; 44 | } 45 | 46 | - (BOOL)existsHeightForKey:(id)key { 47 | NSNumber *number = self.mutableHeightsByKeyForCurrentOrientation[key]; 48 | return number && ![number isEqualToNumber:@-1]; 49 | } 50 | 51 | - (void)cacheHeight:(CGFloat)height byKey:(id)key { 52 | self.mutableHeightsByKeyForCurrentOrientation[key] = @(height); 53 | } 54 | 55 | - (CGFloat)heightForKey:(id)key { 56 | #if CGFLOAT_IS_DOUBLE 57 | return [self.mutableHeightsByKeyForCurrentOrientation[key] doubleValue]; 58 | #else 59 | return [self.mutableHeightsByKeyForCurrentOrientation[key] floatValue]; 60 | #endif 61 | } 62 | 63 | - (void)invalidateHeightForKey:(id)key { 64 | [self.mutableHeightsByKeyForPortrait removeObjectForKey:key]; 65 | [self.mutableHeightsByKeyForLandscape removeObjectForKey:key]; 66 | } 67 | 68 | - (void)invalidateAllHeightCache { 69 | [self.mutableHeightsByKeyForPortrait removeAllObjects]; 70 | [self.mutableHeightsByKeyForLandscape removeAllObjects]; 71 | } 72 | 73 | @end 74 | 75 | @implementation UITableView (FDKeyedHeightCache) 76 | 77 | - (FDKeyedHeightCache *)fd_keyedHeightCache { 78 | FDKeyedHeightCache *cache = objc_getAssociatedObject(self, _cmd); 79 | if (!cache) { 80 | cache = [FDKeyedHeightCache new]; 81 | objc_setAssociatedObject(self, _cmd, cache, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 82 | } 83 | return cache; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /SUIUtils/UITableView+FDTemplateLayoutCell/UITableView+FDTemplateLayoutCell.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 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 | #import 24 | #import "UITableView+FDKeyedHeightCache.h" 25 | #import "UITableView+FDIndexPathHeightCache.h" 26 | #import "UITableView+FDTemplateLayoutCellDebug.h" 27 | 28 | @interface UITableView (FDTemplateLayoutCell) 29 | 30 | /// Access to internal template layout cell for given reuse identifier. 31 | /// Generally, you don't need to know these template layout cells. 32 | /// 33 | /// @param identifier Reuse identifier for cell which must be registered. 34 | /// 35 | - (__kindof UITableViewCell *)fd_templateCellForReuseIdentifier:(NSString *)identifier; 36 | 37 | /// Returns height of cell of type specifed by a reuse identifier and configured 38 | /// by the configuration block. 39 | /// 40 | /// The cell would be layed out on a fixed-width, vertically expanding basis with 41 | /// respect to its dynamic content, using auto layout. Thus, it is imperative that 42 | /// the cell was set up to be self-satisfied, i.e. its content always determines 43 | /// its height given the width is equal to the tableview's. 44 | /// 45 | /// @param identifier A string identifier for retrieving and maintaining template 46 | /// cells with system's "-dequeueReusableCellWithIdentifier:" call. 47 | /// @param configuration An optional block for configuring and providing content 48 | /// to the template cell. The configuration should be minimal for scrolling 49 | /// performance yet sufficient for calculating cell's height. 50 | /// 51 | - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id cell))configuration; 52 | 53 | /// This method does what "-fd_heightForCellWithIdentifier:configuration" does, and 54 | /// calculated height will be cached by its index path, returns a cached height 55 | /// when needed. Therefore lots of extra height calculations could be saved. 56 | /// 57 | /// No need to worry about invalidating cached heights when data source changes, it 58 | /// will be done automatically when you call "-reloadData" or any method that triggers 59 | /// UITableView's reloading. 60 | /// 61 | /// @param indexPath where this cell's height cache belongs. 62 | /// 63 | - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier cacheByIndexPath:(NSIndexPath *)indexPath configuration:(void (^)(id cell))configuration; 64 | 65 | /// This method caches height by your model entity's identifier. 66 | /// If your model's changed, call "-invalidateHeightForKey:(id )key" to 67 | /// invalidate cache and re-calculate, it's much cheaper and effective than "cacheByIndexPath". 68 | /// 69 | /// @param key model entity's identifier whose data configures a cell. 70 | /// 71 | - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier cacheByKey:(id)key configuration:(void (^)(id cell))configuration; 72 | 73 | @end 74 | 75 | @interface UITableViewCell (FDTemplateLayoutCell) 76 | 77 | /// Indicate this is a template layout cell for calculation only. 78 | /// You may need this when there are non-UI side effects when configure a cell. 79 | /// Like: 80 | /// - (void)configureCell:(FooCell *)cell atIndexPath:(NSIndexPath *)indexPath { 81 | /// cell.entity = [self entityAtIndexPath:indexPath]; 82 | /// if (!cell.fd_isTemplateLayoutCell) { 83 | /// [self notifySomething]; // non-UI side effects 84 | /// } 85 | /// } 86 | /// 87 | @property (nonatomic, assign) BOOL fd_isTemplateLayoutCell; 88 | 89 | /// Enable to enforce this template layout cell to use "frame layout" rather than "auto layout", 90 | /// and will ask cell's height by calling "-sizeThatFits:", so you must override this method. 91 | /// Use this property only when you want to manually control this template layout cell's height 92 | /// calculation mode, default to NO. 93 | /// 94 | @property (nonatomic, assign) BOOL fd_enforceFrameLayout; 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /SUIUtils/UITableView+FDTemplateLayoutCell/UITableView+FDTemplateLayoutCell.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 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 | #import "UITableView+FDTemplateLayoutCell.h" 24 | #import 25 | 26 | @implementation UITableView (FDTemplateLayoutCell) 27 | 28 | - (__kindof UITableViewCell *)fd_templateCellForReuseIdentifier:(NSString *)identifier { 29 | NSAssert(identifier.length > 0, @"Expect a valid identifier - %@", identifier); 30 | 31 | NSMutableDictionary *templateCellsByIdentifiers = objc_getAssociatedObject(self, _cmd); 32 | if (!templateCellsByIdentifiers) { 33 | templateCellsByIdentifiers = @{}.mutableCopy; 34 | objc_setAssociatedObject(self, _cmd, templateCellsByIdentifiers, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 35 | } 36 | 37 | UITableViewCell *templateCell = templateCellsByIdentifiers[identifier]; 38 | 39 | if (!templateCell) { 40 | templateCell = [self dequeueReusableCellWithIdentifier:identifier]; 41 | NSAssert(templateCell != nil, @"Cell must be registered to table view for identifier - %@", identifier); 42 | templateCell.fd_isTemplateLayoutCell = YES; 43 | templateCell.contentView.translatesAutoresizingMaskIntoConstraints = NO; 44 | templateCellsByIdentifiers[identifier] = templateCell; 45 | [self fd_debugLog:[NSString stringWithFormat:@"layout cell created - %@", identifier]]; 46 | } 47 | 48 | return templateCell; 49 | } 50 | 51 | - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id cell))configuration { 52 | if (!identifier) { 53 | return 0; 54 | } 55 | 56 | UITableViewCell *templateLayoutCell = [self fd_templateCellForReuseIdentifier:identifier]; 57 | 58 | // Manually calls to ensure consistent behavior with actual cells (that are displayed on screen). 59 | [templateLayoutCell prepareForReuse]; 60 | 61 | // Customize and provide content for our template cell. 62 | if (configuration) { 63 | configuration(templateLayoutCell); 64 | } 65 | 66 | CGFloat contentViewWidth = CGRectGetWidth(self.frame); 67 | 68 | // If a cell has accessory view or system accessory type, its content view's width is smaller 69 | // than cell's by some fixed values. 70 | if (templateLayoutCell.accessoryView) { 71 | contentViewWidth -= 16 + CGRectGetWidth(templateLayoutCell.accessoryView.frame); 72 | } else { 73 | static const CGFloat systemAccessoryWidths[] = { 74 | [UITableViewCellAccessoryNone] = 0, 75 | [UITableViewCellAccessoryDisclosureIndicator] = 34, 76 | [UITableViewCellAccessoryDetailDisclosureButton] = 68, 77 | [UITableViewCellAccessoryCheckmark] = 40, 78 | [UITableViewCellAccessoryDetailButton] = 48 79 | }; 80 | contentViewWidth -= systemAccessoryWidths[templateLayoutCell.accessoryType]; 81 | } 82 | 83 | CGSize fittingSize = CGSizeZero; 84 | 85 | if (templateLayoutCell.fd_enforceFrameLayout) { 86 | // If not using auto layout, you have to override "-sizeThatFits:" to provide a fitting size by yourself. 87 | // This is the same method used in iOS8 self-sizing cell's implementation. 88 | // Note: fitting height should not include separator view. 89 | SEL selector = @selector(sizeThatFits:); 90 | BOOL inherited = ![templateLayoutCell isMemberOfClass:UITableViewCell.class]; 91 | BOOL overrided = [templateLayoutCell.class instanceMethodForSelector:selector] != [UITableViewCell instanceMethodForSelector:selector]; 92 | if (inherited && !overrided) { 93 | NSAssert(NO, @"Customized cell must override '-sizeThatFits:' method if not using auto layout."); 94 | } 95 | fittingSize = [templateLayoutCell sizeThatFits:CGSizeMake(contentViewWidth, 0)]; 96 | } else { 97 | // Add a hard width constraint to make dynamic content views (like labels) expand vertically instead 98 | // of growing horizontally, in a flow-layout manner. 99 | if (contentViewWidth > 0) { 100 | NSLayoutConstraint *widthFenceConstraint = [NSLayoutConstraint constraintWithItem:templateLayoutCell.contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:contentViewWidth]; 101 | [templateLayoutCell.contentView addConstraint:widthFenceConstraint]; 102 | // Auto layout engine does its math 103 | fittingSize = [templateLayoutCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 104 | [templateLayoutCell.contentView removeConstraint:widthFenceConstraint]; 105 | } 106 | } 107 | 108 | // Add separator's height, using a private property in UITableViewCell. 109 | CGFloat separatorHeight = [[templateLayoutCell valueForKey:@"_separatorHeight"] doubleValue]; 110 | fittingSize.height += separatorHeight; 111 | 112 | if (templateLayoutCell.fd_enforceFrameLayout) { 113 | [self fd_debugLog:[NSString stringWithFormat:@"calculate using frame layout - %@", @(fittingSize.height)]]; 114 | } else { 115 | [self fd_debugLog:[NSString stringWithFormat:@"calculate using auto layout - %@", @(fittingSize.height)]]; 116 | } 117 | 118 | return fittingSize.height; 119 | } 120 | 121 | - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier cacheByIndexPath:(NSIndexPath *)indexPath configuration:(void (^)(id cell))configuration { 122 | if (!identifier || !indexPath) { 123 | return 0; 124 | } 125 | 126 | // Hit cache 127 | if ([self.fd_indexPathHeightCache existsHeightAtIndexPath:indexPath]) { 128 | [self fd_debugLog:[NSString stringWithFormat:@"hit cache by index path[%@:%@] - %@", @(indexPath.section), @(indexPath.row), @([self.fd_indexPathHeightCache heightForIndexPath:indexPath])]]; 129 | return [self.fd_indexPathHeightCache heightForIndexPath:indexPath]; 130 | } 131 | 132 | CGFloat height = [self fd_heightForCellWithIdentifier:identifier configuration:configuration]; 133 | [self.fd_indexPathHeightCache cacheHeight:height byIndexPath:indexPath]; 134 | [self fd_debugLog:[NSString stringWithFormat: @"cached by index path[%@:%@] - %@", @(indexPath.section), @(indexPath.row), @(height)]]; 135 | 136 | return height; 137 | } 138 | 139 | - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier cacheByKey:(id)key configuration:(void (^)(id cell))configuration { 140 | if (!identifier || !key) { 141 | return 0; 142 | } 143 | 144 | // Hit cache 145 | if ([self.fd_keyedHeightCache existsHeightForKey:key]) { 146 | CGFloat cachedHeight = [self.fd_keyedHeightCache heightForKey:key]; 147 | [self fd_debugLog:[NSString stringWithFormat:@"hit cache by key[%@] - %@", key, @(cachedHeight)]]; 148 | return cachedHeight; 149 | } 150 | 151 | CGFloat height = [self fd_heightForCellWithIdentifier:identifier configuration:configuration]; 152 | [self.fd_keyedHeightCache cacheHeight:height byKey:key]; 153 | [self fd_debugLog:[NSString stringWithFormat:@"cached by key[%@] - %@", key, @(height)]]; 154 | 155 | return height; 156 | } 157 | 158 | @end 159 | 160 | @implementation UITableViewCell (FDTemplateLayoutCell) 161 | 162 | - (BOOL)fd_isTemplateLayoutCell { 163 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 164 | } 165 | 166 | - (void)setFd_isTemplateLayoutCell:(BOOL)isTemplateLayoutCell { 167 | objc_setAssociatedObject(self, @selector(fd_isTemplateLayoutCell), @(isTemplateLayoutCell), OBJC_ASSOCIATION_RETAIN); 168 | } 169 | 170 | - (BOOL)fd_enforceFrameLayout { 171 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 172 | } 173 | 174 | - (void)setFd_enforceFrameLayout:(BOOL)enforceFrameLayout { 175 | objc_setAssociatedObject(self, @selector(fd_enforceFrameLayout), @(enforceFrameLayout), OBJC_ASSOCIATION_RETAIN); 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /SUIUtils/UITableView+FDTemplateLayoutCell/UITableView+FDTemplateLayoutCellDebug.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 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 | #import 24 | 25 | @interface UITableView (FDTemplateLayoutCellDebug) 26 | 27 | /// Helps to debug or inspect what is this "FDTemplateLayoutCell" extention doing, 28 | /// turning on to print logs when "creating", "calculating", "precaching" or "hitting cache". 29 | /// 30 | /// Default to NO, log by NSLog. 31 | /// 32 | @property (nonatomic, assign) BOOL fd_debugLogEnabled; 33 | 34 | /// Debug log controlled by "fd_debugLogEnabled". 35 | - (void)fd_debugLog:(NSString *)message; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /SUIUtils/UITableView+FDTemplateLayoutCell/UITableView+FDTemplateLayoutCellDebug.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 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 | #import "UITableView+FDTemplateLayoutCellDebug.h" 24 | #import 25 | 26 | @implementation UITableView (FDTemplateLayoutCellDebug) 27 | 28 | - (BOOL)fd_debugLogEnabled { 29 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 30 | } 31 | 32 | - (void)setFd_debugLogEnabled:(BOOL)debugLogEnabled { 33 | objc_setAssociatedObject(self, @selector(fd_debugLogEnabled), @(debugLogEnabled), OBJC_ASSOCIATION_RETAIN); 34 | } 35 | 36 | - (void)fd_debugLog:(NSString *)message { 37 | if (self.fd_debugLogEnabled) { 38 | NSLog(@"** FDTemplateLayoutCell ** %@", message); 39 | } 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /SUIUtilsDemo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/randomprocess/SUIUtils/f0685ad7a76db3f5880fc3fbe894b31a2085d308/SUIUtilsDemo/.DS_Store -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/SUICategoryDemo/SUICategoryRootVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUICategoryRootVC.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/19. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SUICategoryRootVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/SUICategoryDemo/SUICategoryRootVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // SUICategoryRootVC.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/19. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUICategoryRootVC.h" 10 | #import "SUIUtils.h" 11 | 12 | @interface SUICategoryRootVC () 13 | 14 | @end 15 | 16 | @implementation SUICategoryRootVC 17 | 18 | 19 | - (void)viewDidLoad 20 | { 21 | [super viewDidLoad]; 22 | 23 | 24 | SUILogObj(self.sui_tableView); 25 | 26 | SUILogObj(self.sui_sourceVC); 27 | 28 | SUILogLine 29 | } 30 | 31 | 32 | @end 33 | 34 | 35 | @interface SUICategoryRootTableVC : UITableViewController 36 | 37 | @end 38 | 39 | @implementation SUICategoryRootTableVC 40 | 41 | 42 | - (void)viewDidLoad 43 | { 44 | [super viewDidLoad]; 45 | 46 | 47 | SUILogObj(self.sui_tableView); 48 | 49 | SUILogObj(self.sui_sourceVC); 50 | 51 | SUILogLine 52 | } 53 | 54 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 55 | { 56 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 57 | 58 | switch (indexPath.row) { 59 | case 0: 60 | [self sui_storyboardInstantiate:@"CategorySecond"]; 61 | break; 62 | case 1: 63 | [self sui_storyboardInstantiate:@"SUICategoryDemo.CategorySecond" segueType:SUISegueTypeModal]; 64 | break; 65 | case 2: 66 | [self sui_storyboardInstantiate:@"CategorySecondNav"]; 67 | break; 68 | default: 69 | break; 70 | } 71 | } 72 | 73 | 74 | @end 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/SUICategoryDemo/SUICategorySecondCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUICategorySecondCell.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/20. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SUICategorySecondCell : UITableViewCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/SUICategoryDemo/SUICategorySecondCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SUICategorySecondCell.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/20. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUICategorySecondCell.h" 10 | #import "SUIUtils.h" 11 | 12 | 13 | @interface SUICategorySecondCell () 14 | 15 | @end 16 | 17 | @implementation SUICategorySecondCell 18 | 19 | 20 | - (void)sui_cellWillDisplayWithModel:(id)cModel 21 | { 22 | SUILogObj(cModel); 23 | SUILogLine 24 | } 25 | 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/SUICategoryDemo/SUICategorySecondCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/SUICategoryDemo/SUICategorySecondVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // SUICategorySecondVC.h 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/19. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SUICategorySecondVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/SUICategoryDemo/SUICategorySecondVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // SUICategorySecondVC.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/19. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import "SUICategorySecondVC.h" 10 | #import "SUIUtils.h" 11 | 12 | @interface SUICategorySecondVC () 13 | 14 | @end 15 | 16 | @implementation SUICategorySecondVC 17 | 18 | 19 | - (void)viewDidLoad 20 | { 21 | [super viewDidLoad]; 22 | 23 | 24 | // SUILogObj(self.sui_tableView); 25 | // 26 | // SUILogObj(self.sui_sourceVC); 27 | // 28 | // SUILogLine 29 | 30 | [self.sui_tableView registerNib:[UINib nibWithNibName:@"SUICategorySecondCell" bundle:nil] forCellReuseIdentifier:@"SUICategorySecondCell"]; 31 | self.sui_tableView.sui_autoSizingCell = YES; 32 | [self.sui_tableView sui_reloadDataAry:@[@"1", @"2"]]; 33 | [self.sui_tableView.sui_tableHelper didSelect:^(NSIndexPath * _Nonnull cIndexPath, id _Nonnull model) { 34 | NSLog(@"dddd"); 35 | }]; 36 | } 37 | 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/SUIToolDemo/SUIToolDemo.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SUIUtilsDemo 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SUIUtilsDemo/SUIUtilsDemoTests/SUIUtilsDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SUIUtilsDemoTests.m 3 | // SUIUtilsDemoTests 4 | // 5 | // Created by RandomSuio on 16/2/15. 6 | // Copyright © 2016年 RandomSuio. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SUIUtilsDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation SUIUtilsDemoTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | --------------------------------------------------------------------------------