Skip to content

Instantly share code, notes, and snippets.

@jcayzac
Last active December 16, 2015 00:58
Show Gist options
  • Select an option

  • Save jcayzac/5351247 to your computer and use it in GitHub Desktop.

Select an option

Save jcayzac/5351247 to your computer and use it in GitHub Desktop.
backlog

Find if the user has a system-registered facebook account

if (![SLComposeViewController class]) return NO;
return [SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook];

Get the first responder in constant time

UIResponder+JCCurrentFirstResponder.h
#import <UIKit/UIKit.h>
@interface UIResponder (JCCurrentFirstResponder)
+(UIResponder*)JCCurrentFirstResponder;
@end
UIResponder+JCCurrentFirstResponder.m
#import "UIResponder+JCCurrentFirstResponder.h"
static id JCCurrentFirstResponder;
@implementation UIResponder (JCCurrentFirstResponder)
+(UIResponder*)JCCurrentFirstResponder
{
  @synchronized(self) {
    // When to: is nil, action is sent to the first responder.
    [[UIApplication sharedApplication] sendAction:@selector(JCSetAsFirstResponder_) to:nil from:nil forEvent:nil];
    id res = JCCurrentFirstResponder;
    JCCurrentFirstResponder = nil;
    return res;
  }
}
-(void)JCSetAsFirstResponder_
{
  JCCurrentFirstResponder = self;
}
@end

Subclass classes from optional static libraries

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

#define JC_MAKE_RUNTIME_CLASS_GLUE(theGlueClass, theRuntimeClass, theLinkTimeClass) \
  @interface theGlueClass : theLinkTimeClass \
  @end \
  @implementation theGlueClass \
  +(void)initialize { \
    Class superClass = objc_getClass(#theRuntimeClass); \
    if (self == [theGlueClass class] && superClass) class_setSuperclass(self, superClass); \
  } \
  +(id)alloc { \
    return [self allocWithZone:nil]; \
  } \
  +(id)allocWithZone:(NSZone*)zone { \
    id obj = [objc_getClass(#theRuntimeClass) allocWithZone:zone]; \
    if (obj) object_setClass(obj, [self class]); \
    return obj; \
  } \
  @end

// Example:
#import <GTMOAuth2/GTMOAuth2ViewControllerTouch.h>

@protocol GTMOAuth2ViewControllerTouchProtocol<UINavigationControllerDelegate, UIWebViewDelegate>
// Things from GTMOAuth2ViewControllerTouch we want access to at compile time
@property (nonatomic, retain) UIButton* backButton;
@property (nonatomic, retain) UIWebView* webView;
@property (nonatomic, retain) UIButton* forwardButton;
@property (nonatomic, copy) void (^ popViewBlock)(void);
+(id)controllerWithScope:(NSString*)scope
                clientID:(NSString*)clientID
            clientSecret:(NSString*)clientSecret
        keychainItemName:(NSString*)keychainItemName
       completionHandler:(void (^)(GTMOAuth2ViewControllerTouch* viewController, GTMOAuth2Authentication* auth, NSError* error))handler;
-(void)popView;
-(void)updateUI;
-(void)onBackButtonPressed;
@end

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
JC_MAKE_RUNTIME_CLASS_GLUE(GTMOAuth2ViewControllerTouchWeak, GTMOAuth2ViewControllerTouch, UIViewController<GTMOAuth2ViewControllerTouchProtocol>)
#pragma clang diagnostic pop

// Subclass GTMOAuth2ViewControllerTouchWeak
@interface MyOAuth2ViewController : GTMOAuth2ViewControllerTouchWeak
...
@end

Set a UITextField's placeholder color

JCTextFieldWithPlaceholderColor.h
#import <UIKit/UIKit.h>
@interface JCTextFieldWithPlaceholderColor : UITextField
@property (nonatomic, retain) UIColor* placeholderColor;
@end
JCTextFieldWithPlaceholderColor.m
#import "JCTextFieldWithPlaceholderColor"
@implementation JCTextFieldWithPlaceholderColor
-(void)initialWorkForJCTextFieldWithPlaceholderColor_
{
  self.placeholderColor = [UIColor colorWithWhite:0 alpha:0.7];
}

-(id)initWithFrame:(CGRect)frame
{
  self = [super initWithFrame:frame];
  [self initialWorkForJCTextFieldWithPlaceholderColor_];
  return self;
}

-(id)initWithCoder:(NSCoder*)decoder
{
  self = [super initWithCoder:decoder];
  [self initialWorkForJCTextFieldWithPlaceholderColor_];
  return self;
}

-(void)dealloc
{
  self.placeholderColor = nil;
  [super dealloc];
}

- (CGRect)placeholderRectForBounds:(CGRect)bounds {
  // Placeholder should be positionned at the same place as text
  return [self textRectForBounds:bounds];
}

-(void)drawPlaceholderInRect:(CGRect)rect
{
  // Shrink the font so it fits the controls's height
  UIFont* font = self.font;
  if (font.lineHeight > rect.size.height) {
    CGFloat lineHeightRatio = self.font.lineHeight / self.font.pointSize;
    font = [font fontWithSize:MAX(1, floor(rect.size.height/lineHeightRatio))];
  }

  [self.placeholderColor setFill];
  [self.placeholder drawInRect:rect withFont:font lineBreakMode:UILineBreakModeTailTruncation alignment:self.textAlignment];
}
@end

Getting localized string from UIKit

NSString* localizedNext = [[NSBundle bundleForClass:[UIApplication class]] localizedStringForKey:@"Next" value:@"Next" table:nil];
NSString* localizedPrevious = [[NSBundle bundleForClass:[UIApplication class]] localizedStringForKey:@"Previous" value:@"Previous" table:nil];

Get all possible strings:

$ plutil -convert json -r -o - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.1.sdk/System/Library/Frameworks/UIKit.framework/French.lproj/Localizable.strings

Build a dictionary with named variables

JCDictionaryOfVariableBindings.h
#import <Foundation/Foundation.h>
#define JCDictionaryOfVariableBindings(...) _JCDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
extern NSDictionary* NS_REQUIRES_NIL_TERMINATION _JCDictionaryOfVariableBindings(NSString *commaSeparatedKeysString, id firstValue, ...);
JCDictionaryOfVariableBindings.m
#import "JCDictionaryOfVariableBindings.h"
NSDictionary* NS_REQUIRES_NIL_TERMINATION _JCDictionaryOfVariableBindings(NSString *commaSeparatedKeysString, id firstValue, ...) {
  NSArray* names = [commaSeparatedKeysString componentsSeparatedByString:@","];
  NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:names.count];
  va_list args;
  va_start(args, firstValue);
  int index = -1;
  for (id arg = firstValue; arg; arg = va_arg(args, id)) {
    NSString* name = names[++index];
    name = [name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    dict[name] = arg;
  }
  va_end(args);
  return dict;
}
NSNumber* code = @12;
NSString* msg = @"message";
NSLog(@"Obj: %@", JCDictionaryOfVariableBindings(code, msg));
Obj: {
    code = 12;
    msg = message;
}

Dynamic framework loader

NSString+JCDynamicFrameworkLoader.h

#import <Foundation/Foundation.h>

@interface NSString(JCDynamicFrameworkLoader)
-(NSBundle*)JCFramework;
@end

NSBundle+JCDynamicFrameworkLoader.h

#import <Foundation/Foundation.h>

@interface NSBundle(JCDynamicFrameworkLoader)
+(NSBundle*)JCFrameworkWithName:(NSString*)name;
-(CFBundleRef)JCBundleRef;
-(void*)JCDataPointerForName:(NSString*)name;
-(void*)JCFunctionPointerForName:(NSString*)name;
@end

NSString+JCDynamicFrameworkLoader.m

#import "NSString+JCDynamicFrameworkLoader.h"

@implementation NSString(JCDynamicFrameworkLoader)
-(NSBundle*)JCFramework
{
  static NSString* bundlesPath = nil;
  static dispatch_once_t onceToken = 0;
  dispatch_once(&onceToken, ^{
    bundlesPath = [[[NSBundle bundleWithIdentifier:@"com.apple.Foundation"].bundlePath stringByDeletingLastPathComponent] copy];
    atexit_b(^{
      [bundlesPath release];
    });
  });
  NSBundle* bundle = [NSBundle bundleWithPath:[bundlesPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.framework", self]]];
  if (![bundle isLoaded])
    [bundle load];
  return bundle;
}
@end

NSBundle+JCDynamicFrameworkLoader.m

#import "NSBundle+JCDynamicFrameworkLoader.h"

@implementation NSBundle(JCDynamicFrameworkLoader)
+(NSBundle*)JCFrameworkWithName:(NSString*)name
{
  return [name JCFramework];
}

-(CFBundleRef)JCBundleRef
{
  return CFBundleGetBundleWithIdentifier((CFStringRef)[self bundleIdentifier]);
}

-(void*)JCDataPointerForName:(NSString*)name
{
  return CFBundleGetDataPointerForName([self JCBundleRef], (CFStringRef)name);
}

-(void*)JCFunctionPointerForName:(NSString*)name
{
  return CFBundleGetFunctionPointerForName([self JCBundleRef], (CFStringRef)name);
}
@end

Usage:

typedef CGImageSourceRef (*ImageIO_ImageSourceCreateFunc)(NSData*, NSDictionary*);
NSBundle* ImageIO = [@"ImageIO" JCFramework];
ImageIO_ImageSourceCreateFunc ImageIO_ImageSourceCreate = [ImageIO JCFunctionPointerForName:@"CGImageSourceCreateWithData"];

NSBundle* MessageUI = [@"MessageUI" JCFramework];
NSLog(@"class: %@", NSClassFromString(@"MFMessageComposeViewController"));

NSString** xxx = [MessageUI JCDataPointerForName:@"MFMessageComposeViewControllerTextMessageAvailabilityDidChangeNotification"];
NSLog(@"extern: %@", *xxx);

Add locales support

JCAdditionalLocaleSupport.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface JCAdditionalLocaleSupport : NSObject
@end

@implementation JCAdditionalLocaleSupport
+(void)deactivate
{
  @autoreleasepool {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];
  }
}

+(void)load
{
  @autoreleasepool {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deactivate)name:UIApplicationDidFinishLaunchingNotification object:nil];

    // Remove any value this app has set, so that [NSLocale preferredLanguages]
    // uses system settings:
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    // Update [NSLocale preferredLanguages] for this app
    //
    // Caution: don't use subscript (foo[0]) yet, as we're in a static initializer
    // and ARClite hasn't added the required methods to NSArray yet, on iOS 5.
    NSString* language = [[NSLocale preferredLanguages] objectAtIndex:0];
    NSString* country = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
    NSString* variant = [NSString stringWithFormat:@"%@_%@", language, country];

    // Additional languages we want to support.
    // iOS already supports en_GB, pt_PT, zh_Hans & zh_Hant.
    NSArray* additionalLanguages = @[@"es_ES" /*, @"fr_CA", @"fr_BE", etc... */];
    if ([additionalLanguages containsObject:variant]) {
      NSMutableArray* languages = [NSMutableArray arrayWithArray:[NSLocale preferredLanguages]];
      NSUInteger languagePosition = [languages indexOfObject:language];
      if (languagePosition != NSNotFound && [languages indexOfObject:variant] == NSNotFound) {
        // Insert the variant right before the normal language
        [languages insertObject:variant atIndex:languagePosition];
        [[NSUserDefaults standardUserDefaults] setObject:languages forKey:@"AppleLanguages"];
        [[NSUserDefaults standardUserDefaults] synchronize];
      }
    }
  }
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment