Permalink
Browse files

+ `UIColor` `colorWithHex:` method accepts `0x` along with `#` as a

valid prefix
+ `UIControl` `setTarget: action: forControlEvents:` method allows to
set exclusive handler removing all previously registered actions
+ Performance optimization `UIView` `firstSuperviewOfClass:` method
+ `NSNumber` category
	+ `dateValue` method creates an instance of `NSDate` with current
number as a timestamp
	+ `pluralWithForms:` or `pluralWithForms: language:` method returns
correct plural form for the current number, e.g. `1 tweet` vs `2 tweets`
  • Loading branch information...
1 parent c05bfa5 commit 7610eeac303c9fa2ced9a0a8d4bf3b88cd3ff3db Alexander Zats committed May 23, 2012
View
12 SSToolkit.xcodeproj/project.pbxproj
@@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
+ 962F5F5E156CCD1200408648 /* NSNumber+SSToolkitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 962F5F5B156CCD1200408648 /* NSNumber+SSToolkitAdditions.h */; };
+ 962F5F5F156CCD1200408648 /* NSNumber+SSToolkitAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 962F5F5C156CCD1200408648 /* NSNumber+SSToolkitAdditions.m */; };
+ 962F5F60156CCD1200408648 /* UIColor+SSToolkitAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 962F5F5D156CCD1200408648 /* UIColor+SSToolkitAdditions.m */; };
9EB278B114BBC4360077194C /* SSConcurrentOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = B24E9E11121DC29A0085F81E /* SSConcurrentOperation.m */; };
AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; };
B2136B3C13ABCC0C00FEBCFD /* SSBorderedView.h in Headers */ = {isa = PBXBuildFile; fileRef = B2136B3A13ABCC0C00FEBCFD /* SSBorderedView.h */; };
@@ -167,6 +170,9 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 962F5F5B156CCD1200408648 /* NSNumber+SSToolkitAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNumber+SSToolkitAdditions.h"; sourceTree = "<group>"; };
+ 962F5F5C156CCD1200408648 /* NSNumber+SSToolkitAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNumber+SSToolkitAdditions.m"; sourceTree = "<group>"; };
+ 962F5F5D156CCD1200408648 /* UIColor+SSToolkitAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+SSToolkitAdditions.m"; sourceTree = "<group>"; };
AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
B2136B3A13ABCC0C00FEBCFD /* SSBorderedView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSBorderedView.h; sourceTree = "<group>"; };
B2136B3B13ABCC0C00FEBCFD /* SSBorderedView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSBorderedView.m; sourceTree = "<group>"; };
@@ -483,6 +489,9 @@
B2601A711223079A005506D5 /* Foundation */ = {
isa = PBXGroup;
children = (
+ 962F5F5B156CCD1200408648 /* NSNumber+SSToolkitAdditions.h */,
+ 962F5F5C156CCD1200408648 /* NSNumber+SSToolkitAdditions.m */,
+ 962F5F5D156CCD1200408648 /* UIColor+SSToolkitAdditions.m */,
B24E9E05121DC29A0085F81E /* NSArray+SSToolkitAdditions.h */,
B24E9E06121DC29A0085F81E /* NSArray+SSToolkitAdditions.m */,
B24E9E07121DC29A0085F81E /* NSData+SSToolkitAdditions.h */,
@@ -726,6 +735,7 @@
B2C50814151BAB4000E94614 /* NSBundle+SSToolkitAdditions.h in Headers */,
B2F72A131534D54C00556B66 /* SSRateLimit.h in Headers */,
B2C7ED6C153A5624006ABE73 /* SSButton.h in Headers */,
+ 962F5F5E156CCD1200408648 /* NSNumber+SSToolkitAdditions.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -952,6 +962,8 @@
B2C50815151BAB4000E94614 /* NSBundle+SSToolkitAdditions.m in Sources */,
B2F72A141534D54C00556B66 /* SSRateLimit.m in Sources */,
B2C7ED6D153A5624006ABE73 /* SSButton.m in Sources */,
+ 962F5F5F156CCD1200408648 /* NSNumber+SSToolkitAdditions.m in Sources */,
+ 962F5F60156CCD1200408648 /* UIColor+SSToolkitAdditions.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
71 SSToolkit/NSNumber+SSToolkitAdditions.h
@@ -0,0 +1,71 @@
+//
+// NSNumber+SSToolkitAdditions.h
+// SSToolkit
+//
+// Created by Alexander Zats on 5/22/12.
+// Copyright (c) 2012 Sam Soffes. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSNumber (SSToolkitAdditions)
+
+///--------------
+/// @name Date from timestamp
+///--------------
+
+/**
+ Creates an instance of `NSDate` using current number as timestamp.
+ @return NSDate with current number as unix timestamp or `nil` if current number contains 0.
+ */
+- (NSDate *)dateValue;
+
+///--------------
+/// @name Localized plurals
+///--------------
+
+- (NSString *)pluralWithForms:(NSString *)pluralForms;
+
+/**
+ With given list of plural forms returns a correct one to use with specified number. So far supports only integers.
+ Following code:
+
+ <pre><code>NSLocale *enUSLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_us"];
+ NSNumber *tweetsCount;
+ for (int i = 0; i < 3; ++i) {
+ tweetsCount = [NSNumber numberWithInteger:i];
+ NSString *pluralForm = [tweetsCount pluralFormWithStringForms:@"tweet,tweets" locale:enUSLocale];
+ NSLog(@"%@ %@", tweetsCount, pluralForm);
+ }</code></pre>
+
+ produces output:
+
+ <pre><code>0 tweets
+ 1 tweet
+ 2 tweet</code></pre>
+
+ To create a truly localized experience, use `pluralFormWithStringForms:` method in conjunction with `Localizable.strings` files. It will automatically choose current system language:
+
+ Excerpt from `Localizable.strings` for English locale:
+ <pre><code>"pluralMinuteForms" = "minute,minutes";</code></pre>
+
+ Excerpt from `Localizable.strings` for Hebrew locale:
+ <pre><code>"pluralMinuteForms" = "דקה,דקותֿ";</code></pre>
+
+ Excerpt from `Localizable.strings` for Russian locale:
+ <pre><code>"pluralMinuteForms" = "минута,минуты,минут";</code></pre>
+
+ <pre><code>for (int i = 0; i < 60; ++i) {
+ NSNumber *minutes = [NSNumber numberWithInteger:i];
+ NSString *pluralForm = [minutes pluralFormWithStringForms:NSLocalizedString(@"pluralMinuteForms", @"Correct form of the world `minute` will be chosen automatically")];
+ NSLog(@"%@ %@", minutes, pluralForm);
+ }</code></pre>
+
+ @param pluralForms comma-separated list of plural forms, e.g. `@"minute,minutes"`
+ @param language Canonicalized IETF BCP 47 language identifier, e.g. `en` for English (not `en_us`).
+
+ @return One of passed stringForms that correspond to the integer representation of the number.
+ */
+- (NSString *)pluralWithForms:(NSString *)pluralForms language:(NSString *)language;
+
+@end
View
205 SSToolkit/NSNumber+SSToolkitAdditions.m
@@ -0,0 +1,205 @@
+//
+// NSNumber+SSToolkitAdditions.m
+// SSToolkit
+//
+// Created by Alexander Zats on 5/22/12.
+// Copyright (c) 2012 Sam Soffes. All rights reserved.
+//
+
+#import "NSNumber+SSToolkitAdditions.h"
+
+#define kDefaultLocaleIdetifier @"en"
+
+@interface NSNumber (SSToolkitAdditionsPrivate)
+
+// Returns correct form of the plural for spcified integer
+typedef NSUInteger(^PluralFormSelector)(NSInteger integer);
+
+- (void)initializePluralForms;
+
+@end
+
+@implementation NSNumber (SSToolkitAdditions)
+
+static NSMutableDictionary *PluralFormSolvers;
+
+- (NSDate *)dateValue
+{
+ NSTimeInterval timestamp = [self doubleValue];
+ if (!timestamp) {
+ return nil;
+ }
+ return [NSDate dateWithTimeIntervalSince1970:timestamp];
+}
+
+- (NSString *)pluralWithForms:(NSString *)pluralForms
+{
+ return [NSLocale preferredLanguages].count > 0 ? [self pluralWithForms:pluralForms language:[[NSLocale preferredLanguages] objectAtIndex:0]] : nil;
+}
+
+- (NSString *)pluralWithForms:(NSString *)pluralForms language:(NSString *)language
+{
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ [self initializePluralForms];
+ });
+
+ PluralFormSelector pluralSolver = [PluralFormSolvers valueForKey:language];
+ if (!pluralSolver) {
+ pluralSolver = [PluralFormSolvers valueForKey:kDefaultLocaleIdetifier];
+ if (!pluralSolver) {
+ return nil;
+ }
+ }
+
+ NSUInteger index = pluralSolver( [self integerValue]);
+ NSArray *components = [pluralForms componentsSeparatedByString:@","];
+ if (components.count <= index) {
+ return nil;
+ }
+ return [components objectAtIndex:index];
+
+}
+
+@end
+
+@implementation NSNumber (SSToolkitAdditionsPrivate)
+
+- (void)initializePluralForms
+{
+ PluralFormSolvers = [NSMutableDictionary dictionary];
+
+ PluralFormSelector pluralSolver;
+
+ // rule 0 (1 form) Asian (Chinese, Japanese, Korean, Vietnamese), Persian, Turkic/Altaic (Turkish), Thai, Lao
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return 0;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"zh"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"ja"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"ko"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"vi"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"fa"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"tr"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"th"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"lo"];
+
+ // rule #1 (2 forms) Germanic (Danish, Dutch, English, Faroese, Frisian, German, Norwegian, Swedish), Finno-Ugric
+ // (Estonian, Finnish, Hungarian), Language isolate (Basque), Latin/Greek (Greek), Semitic (Hebrew), Romanic
+ // (Italian, Portuguese, Spanish, Catalan)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n != 1) ? 1 : 0;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"da"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"nl"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"en"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"fo"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"fy"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"de"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"no"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"sv"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"sv"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"et"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"fi"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"hu"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"eu"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"el"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"he"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"it"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"pt"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"es"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"ca"];
+
+ // rule #2 (2 forms) Romanic (French, Brazilian Portuguese)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n > 1) ? 1 : 0;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"fr"];
+ // [PluralSolversDictionary setObject:pluralSolver forKey:@"??"];
+
+ // rule #3 (3 forms) Baltic (Latvian)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n % 10 == 1) && (n % 100 != 11) ? 1 : (n != 0) ? 2 : 0;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"lv"];
+
+ // rule #4 (4 forms) Celtic (Scottish Gaelic)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return ((n == 1) || (n == 11)) ? 0 : ((n == 2) || (n == 12)) ? 1 : ((n > 0) && (n < 20)) ? 2 : 3;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"gd"];
+
+ // rule #5 (3 forms) Romanic (Romanian)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n == 1) ? 0 : ((n == 0) || ((n % 100 > 0) && (n % 100 < 20))) ? 1 : 2;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"ro"];
+
+ // rule #6 (3 forms) Baltic (Lithuanian)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return ((n % 10 == 1) && (n % 100 != 11)) ? 0 : (((n % 10 >= 2) && (n % 100 < 10)) || (n % 100 >= 20)) ? 2 : 1;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"lt"];
+
+ // rule #7 (3 forms) Slavic (Bosnian, Croatian, Serbian, Russian, Ukrainian)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return ((n % 10 == 1) && (n % 100 != 11)) ? 0 : ((n % 10 >= 2) && (n % 10 <= 4) && ((n % 100 < 10) || n % 100 >= 20)) ? 1 : 2;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"bs"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"hr"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"sr"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"ru"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"uk"];
+
+ // rule #8 (3 forms) Slavic (Slovak, Czech)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n == 1) ? 0 : ((n >= 2) && (n <= 4)) ? 1 : 2;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"sk"];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"cs"];
+
+ // rule #9 (3 forms) Slavic (Polish)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n == 1) ? 0 : ((n % 10 >= 2) && (n % 10 <= 4) && ((n % 100 < 10) || (n % 100 >= 20))) ? 1 : 2;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"pl"];
+
+ // rule #10 (4 forms) Slavic (Slovenian, Sorbian)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n % 100 == 1) ? 0 : (n % 100 == 2) ? 1 : ((n % 100 == 3) || (n % 100 == 4)) ? 2 : 3;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"sl"];
+ // [PluralSolversDictionary setObject:pluralSolver forKey:@"??"];
+
+ // rule #11 (5 forms) Celtic (Irish Gaelic)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n == 1) ? 0 : (n == 2) ? 1 : (n >= 3) && (n <= 6) ? 2 : ((n >= 7) && (n <= 10)) ? 3 : 4;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"ga"];
+
+ // rule #12 (6 forms) Semitic (Arabic)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n == 0) ? 5 : (n == 1) ? 0 : (n == 2) ? 1 : ((n % 100 >= 3) && (n % 100 <= 10)) ? 2 : ((n % 100 >= 11) && (n % 100 <= 99)) ? 3 : 4;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"ar"];
+
+ // rule #13 (4 forms) Semitic (Maltese)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n == 1) ? 0 : ((n == 0) || ((n % 100 > 0) && (n % 100 <= 10))) ? 1 : ((n % 100 > 10) && (n % 100 < 20)) ? 2 : 3;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"mt"];
+
+ // rule #14 (3 forms) Slavic (Macedonian)
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return (n % 10 == 1) ? 0 : (n % 10 == 2) ? 1 : 2;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"mk"];
+
+ // rule #15 (2 forms) Icelandic
+ pluralSolver = [^NSUInteger(NSInteger n){
+ return ((n % 10 == 1) && (n % 100 != 11)) ? 0 : 1;
+ } copy];
+ [PluralFormSolvers setObject:pluralSolver forKey:@"is"];
+}
+
+@end
View
6 SSToolkit/UIColor+SSToolkitAdditions.m
@@ -23,9 +23,11 @@ - (NSUInteger)_hexValue {
@implementation UIColor (SSToolkitAdditions)
+ (UIColor *)colorWithHex:(NSString *)hex {
- // Remove `#`
- if ([[hex substringWithRange:NSMakeRange(0, 1)] isEqualToString:@"#"]) {
+ // Remove `#` and `0x`
+ if ([hex hasPrefix:@"#"]) {
hex = [hex substringFromIndex:1];
+ } else if ([hex hasPrefix:@"0x"]) {
+ hex = [hex substringFromIndex:2];
}
// Invalid if not 3, 6, or 8 characters
View
5 SSToolkit/UIControl+SSToolkitAdditions.h
@@ -16,4 +16,9 @@
*/
- (void)removeAllTargets;
+/**
+ Sets exclusive target for specified event, all previous targets will be removed, usefull for table cells etc
+ */
+- (void)setTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
+
@end
View
12 SSToolkit/UIControl+SSToolkitAdditions.m
@@ -16,4 +16,16 @@ - (void)removeAllTargets {
}];
}
+- (void)setTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
+{
+ NSSet *targets = [self allTargets];
+ [targets enumerateObjectsUsingBlock:^(id target, BOOL *stop) {
+ NSArray *actions = [self actionsForTarget:target forControlEvent:controlEvents];
+ [actions enumerateObjectsUsingBlock:^(NSString *action, NSUInteger idx, BOOL *stop) {
+ [self removeTarget:target action:NSSelectorFromString(action) forControlEvents:controlEvents];
+ }];
+ }];
+ [self addTarget:target action:action forControlEvents:controlEvents];
+}
+
@end
View
11 SSToolkit/UIView+SSToolkitAdditions.m
@@ -74,16 +74,11 @@ - (NSArray *)superviews {
return superviews;
}
-
- (id)firstSuperviewOfClass:(Class)superviewClass {
- UIView *view = self;
- UIView *superview = nil;
- while (view) {
- superview = [view superview];
- if ([superview isKindOfClass:superviewClass]) {
- return superview;
+ for (UIView *view = [self superview]; view != nil; view = [view superview]) {
+ if ([view isKindOfClass:superviewClass]) {
+ return view;
}
- view = superview;
}
return nil;
}

0 comments on commit 7610eea

Please sign in to comment.