Programming Notes

Programming Notes

Table of Contents

1 Objective-C

1.1 Send asynchronous network request using NSURLSession and custom NSURLComponents

NSURLComponents *components = [[NSURLComponents alloc] init];
components.scheme = @"https"; = @"";
components.path = @"/search";
components.query = @"term=miles+davis&limit=5";

NSLog(@"URL: %@", [components URL]);

[[[NSURLSession sharedSession] dataTaskWithURL:[components URL]
                             completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (!error) {
        id json = [NSJSONSerialization JSONObjectWithData:data
        NSLog(@"%@", json);
    } else
        NSLog(@"error: %@", [error localizedDescription]);
}] resume];

1.2 Key-Value Coding Collection Operators

1.2.1 Simple Collection Operators

  • @avg, @count, @max, @min, @sum

1.2.2 Object Operators (For a single collection instance)

  • @distinctUnionOfObjects, @unionOfObjects

1.2.3 Array and Set Operators (For nested collections)

  • @distinctUnionOfArrays, @unionOfArrays, @distinctUnionOfSets

1.3 LLDB commands

  • bt will show the stacktrace for the current thread.
  • bt all will log a complete stacktrace for all threads.
  • b -[NSString stringWithFormat:] This example shows how to set a breakpoint for a particular method when it is called from anywhere in your project.
  • po [[[[UIApplication sharedApplication] delegate] window] recursiveDescription] will log all the views in the entire application that are present in the view hierarchy.
  • breakpoint list list all breakpoints.
  • breakpoint delete 1 delete breakpoint "1".
  • watchpoint set variable myVariable sets a watchpoint on a variable when it is written to.
  • watchpoint list will list all watchpoints.
  • watchpoint delete 1 will delete watchpoint "1".

1.4 Completion Block Example

Using a completion block rather than delegate when dismissing a view controller.

In your DetailViewController, expose a property for a pointer to a block:

@property (nonatomic, copy) void (^dismissBlock)(void);
// Or use a typedef
typedef void (^DismissBlock)(void)
@property(nonatomic, copy) DismissBlock myDismissBlock;

In your MasterViewController, set the block property when the DetailViewController is instantiated. The DetailViewController will hold onto this block until it needs to be dismissed:

DetailViewController *detailVC = [[DetailViewController alloc] init];
[detailVC setDismissBlock:^{[[self tableview] reloadData];}];

// Alternate way if using prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    if ([[segue identifier] isEqualToString:@"showDetail"]) {
        [[segue destinationViewController] setDismissBlock:^{[[self tableView] reloadData];}];

Pass in the dismiss block when the user taps "done" in the DetailViewController:

- (IBAction)donePressed:(id)sender {
    [[self presentingViewController] dismissViewControllerAnimated:YES completion:[self dismissBlock]];

1.5 Blocks: Typedef

typedef NSString* (^MyBlock)(NSString*);
- (void)methodThatTakesABlock:(MessageBlock)block;

typedef void(^MyBlock)(void);
@property (copy) MyBlock blockProperty;

1.6 Enable/Disable ARC for a single file

  • enable: add -fobjc-arc compiler flag in the build phases section of Xcode for that file.
  • disable: add -fno-objc-arc compiler flag in the build phases section of Xcode for that file.

1.7 NS_ENUM Macro

typedef NS_ENUM(NSUInteger, DepartmentType) {

1.8 Conditionally load AppDelegate subclass based on device type

//  main.m
//  AppDelegatesForDifferentDevices
//  Adapted from Daniel Steinberg's "A Pocket Full of Patterns" talk at CocoaConf Chicago 2013.

    #import <UIKit/UIKit.h>
    #import "AppDelegate_iOS.h"
    #import <Cocoa/Cocoa.h>

int main(int argc, char * argv[])
    @autoreleasepool {
        NSString *delegateClassName = nil;

        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { // iPhone
            delegateClassName = NSStringFromClass([AppDelegate_iPhone class]);
        } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { // iPad
            delegateClassName = NSStringFromClass([AppDelegate_iPad class]);
        } else { // Future iOS Devices
            delegateClassName = NSStringFromClass([AppDelegate_iOS class]);

        return UIApplicationMain(argc, argv, nil, delegateClassName);
    return NSApplicationMain(argc, (const char **)argv);

1.9 Shared prefix header

//  Prefix header
//  The contents of this file are implicitly included at the beginning of every source file.

#import <TargetConditionals.h>

// iOS
    #import <Availability.h>

    #ifndef __IPHONE_5_0
    #warning "This project uses features only available in iOS SDK 5.0 and later."

    #ifdef __OBJC__
        #import <UIKit/UIKit.h>
        #import <Foundation/Foundation.h>
// Mac
    #ifdef __OBJC__
        #import <Cocoa/Cocoa.h>

1.10 Three memory management idioms for setters

1.10.1 1. Retain then release

- (void)setStartDate:(NSDate *)newStartDate
    [newStartDate retain];
    [startDate release];
    startDate = newStartDate;

1.10.2 2. Check first

- (void)setStartDate:(NSDate *)newStartDate
    if (startDate != newStartDate) {
        [startDate release];
        startDate = [newStartDate retain];

1.10.3 3. Autorelease old value

- (void)setStartDate:(NSDate *)newStartDate
    [startDate autorelease];
    startDate = [newStartDate retain];

1.11 Blocks: Chaining completions for bounce animation effect

// Declare a block typedef
typedef void (^BounceBlock)();

// Create the scale animation block method
- (void)scaleViewTo:(CGFloat)scale completion:(BounceBlock)completion {
    CGFloat duration = 0.10f;
    [UIView animateWithDuration:duration animations:^{
        _myView.transform = CGAffineTransformMakeScale(scale, scale);
    } completion:^(BOOL finished) {
        if (completion) {

// Invoke the bounce effect
- (void)bounceView {
    [self scaleViewTo:1.2 completion:^{
        [self scaleViewTo:0.8 completion:^{
            [self scaleViewTo:1.1 completion:^{
                [self scaleViewTo:0.9 completion:^{
                    [self scaleViewTo:1.0 completion:nil];

1.12 Block-Backed NSImage

NSImage *newImage = [NSImage imageWithSize:myrect.size
                            drawingHandler:^BOOL(NSRect dstRect) {
                                [[NSColor yellowColor] set];
                                [[NSBezierPath bezierPathWithOvalInRect:dstRect] fill];
                                return YES;

1.13 Saving to core data from prepareForSegue:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    [self configureCell:cell atIndexPath:indexPath];

    return cell;

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
    PopularBookmark *currentObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
    cell.textLabel.text = [currentObject title];

    NSArray *tagsArray = [currentObject tags];
    NSString *tagsString = [tagsArray componentsJoinedByString:@", "];
    [[cell detailTextLabel] setText:tagsString];

    if ([currentObject hasBeenViewed])
        [cell setAccessoryType:UITableViewCellAccessoryCheckmark];
        [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    if ([[segue identifier] isEqualToString:@"showDetail"]) {
        NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        PopularBookmark *bookmarkObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
        [[segue destinationViewController] setDetailItem:bookmarkObject];

        [bookmarkObject setHasBeenViewed:YES];
        [context save:nil];

1.14 Create Singleton with dispatch_once

+ (AccountManager *)sharedManager
    static AccountManager *sharedAccountManagerInstance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedAccountManagerInstance = [[self alloc] init];
    return sharedAccountManagerInstance;

1.15 Toggle view state of a button if selected/not selected

- (IBAction)toggleButton:(UIButton *)sender {
    sender.selected = !sender.isSelected;

1.16 Dismiss the keyboard when the view outside the text field is touched

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    // Dismiss the keyboard when the view outside the text field is touched.
    [textField resignFirstResponder];
    // Revert the text field to the previous value.
    textField.text = self.string;
    [super touchesBegan:touches withEvent:event];

1.17 Blocks Memory Management Issue

// Reference to self inside a block can create a retain cycle. In this case, the block is retained by the observer and
// self is retained by the block.
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
_myObserver = [center addObserverForName:@"FetchSuccessful"
                                   queue:[NSOperationQueue mainQueue]
                              usingBlock:^(NSNotification *note) {
                                  self.person = [note object];

// Fix:
// Create a weak reference outside the block. Create a strong reference inside the block, then
// test if it exists since the weak reference could have been destroyed on another thread.
__weak typeof(self)weakSelf = self;
_myObserver = [center addObserverForName:@"FetchSuccessful"
                                   queue:[NSOperationQueue mainQueue]
                              usingBlock:^(NSNotification *note) {
                                  __strong typeof(weakSelf)strongSelf = weakSelf;
                                  if (strongSelf) {
                                      strongself.person = [note object];
  • Look out for persisting executions like NSNotifications, error callbacks, timers, handlers.
  • One-time executions like dispatch_async or enumeration of an array are not major concerns.

1.18 setNilValueForKey to set an appropriate default value when nil

- (void)setNilValueForKey:(NSString *)key
    if ([key isEqualToString:@"salary"]) {
        [self setSalary:0.0];
    } else {
        [super setNilValueForKey:key];

1.19 Insert new row at bottom of tableview when editing

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
  if ([indexPath row] >= [[[self fetchedResultsController] fetchedObjects] count]) { //Insert Row
    return UITableViewCellEditingStyleInsert;
  return UITableViewCellEditingStyleDelete;

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
  [super setEditing:editing animated:animated];

  NSInteger count = [[[self fetchedResultsController] fetchedObjects] count];
  NSIndexPath *insertRowPath = [NSIndexPath indexPathForRow:count inSection:0];

  if (!editing) {
    [[self tableView] deleteRowsAtIndexPaths:[NSArray arrayWithObject:insertRowPath] withRowAnimation:UITableViewRowAnimationFade];
  } else {
    [[self tableView] insertRowsAtIndexPaths:[NSArray arrayWithObject:insertRowPath] withRowAnimation:UITableViewRowAnimationFade];

1.20 Gift app from within app

#define APP_ID 561142687
- (void)giftApp {
    NSString *GiftAppURL = [NSString stringWithFormat:@"itms-appss://",
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:GiftAppURL]];

1.21 Using @autoreleasepool to prevent memory spikes

// Causes a memory spike
for (int i = 0; i < 1000; i++) {
    NSMutableArray *thearray = [NSMutableArray array];
    for (int j = 0; j < 100000; j++) {
        [thearray addObject:@"hello"];

// Fix the memory spike by adding an @autoreleasepool to free memory after each loop iteration
for (int i = 0; i < 1000; i++) {
    @autoreleasepool {
        NSMutableArray *thearray = [NSMutableArray array];
        for (int j = 0; j < 100000; j++) {
            [thearray addObject:@"hello"];

1.22 NSString Null Values

NSString *fullName = [NSString stringWithFormat:@"%@ %@",
    contact.firstName ?: @"", contact.lastName ?: @""];
(a ?: b is a GCC extension which stands for a ? a : b, without evaluating a twice.)

1.23 Accept only numbers from a uitextfield from the iPad keyboard

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
    static NSCharacterSet *nonNumberSet = nil;
    if (!nonNumberSet) nonNumberSet = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];

    NSRange location = [string rangeOfCharacterFromSet:nonNumberSet];
    return (location.location == NSNotFound);

1.24 Testing for an empty NSString

  • Create a category on NSString that returns a trimmed string that trims whitespace and newline characters:
- (NSString *)trimmedString {
    return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  • Test for "0" string length to see if its empty:
cell.noteImageView.image = ([[object.notes trimmedString] length] == 0) ? nil : [UIImage imageNamed:@"notepad"];

1.25 NSOpenPanel crash

1.26 Decode HTML Entities with NSAttributedString

NSString *stringWithHTMLEntity = @"Today &amp; tomorrow";
NSAttributedString *attrString = [[NSAttributedString alloc] initWithData:[stringWithHTMLEntity dataUsingEncoding:NSUTF8StringEncoding]
                                                                  options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}
// "Today &amp; tomorrow" will now be displayed as "Today & tomorrow"

1.27 Implement transposeWords: for NSTextView

@interface MyTextView ()

@implementation MyTextView

- (void)transposeWords:(id)sender
    // Get the insertion point index
    NSUInteger insertionIndex = [self selectedRange].location;
    // Get the entire string
    NSString *theString = [self string];

    // Split the string into left and right strings, cleaning out any whitespace from either ends
    NSString *leftString = [theString substringToIndex:insertionIndex];
    leftString = [leftString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    NSString *rightString = [theString substringFromIndex:insertionIndex];
    rightString = [rightString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

    // Create arrays out of both strings
    NSArray *leftArray = [leftString componentsSeparatedByString:@" "];
    NSArray *rightArray = [rightString componentsSeparatedByString:@" "];

    // Get the last object of the left array
    NSString *leftEndString = [leftArray lastObject];
    // Get the first object of the right array
    NSString *rightBeginningString = [rightArray firstObject];

    // If the insertion point is at the end of the string, set the insertion point to the beginning and return
    if ([rightString length] == 0) {
        [self moveToBeginningOfLine:nil];

    // If the insertion point is at the beginning of the string, set the insertion point to the next word and return
    if ([leftString length] == 0) {
        [self moveWordForward:nil];

    // Join the two arrays into one mutable array
    NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:[leftArray count] + [rightArray count]];
    [newArray addObjectsFromArray:leftArray];
    [newArray addObjectsFromArray:rightArray];

    // Get the indices of our desired two strings
    NSUInteger leftIndex = [newArray indexOfObject:leftEndString];
    NSUInteger rightIndex = [newArray indexOfObject:rightBeginningString];

    // Swap the array elements
    [newArray exchangeObjectAtIndex:leftIndex withObjectAtIndex:rightIndex];

    // Create a string from the arrays and set the text view
    NSString *newString = [newArray componentsJoinedByString:@" "];
    [self setString:newString];

    // Get the location of the original left string (now right) and
    // set the new insertion point to advance forward one word
    NSRange foundRange = [newString rangeOfString:leftEndString];
    [self setSelectedRange:NSMakeRange(foundRange.location + foundRange.length, 0)];

1.28 Alternating the background color of a UITableViewCell

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row % 2 == 0) {
        UIColor *altCellColor = [UIColor colorWithWhite:0.7 alpha:0.1];
        cell.backgroundColor = altCellColor;

1.29 Autolayout debugger trace

// Pause app and enter into debugger
// Any ambiguous layouts will be labeled
po [[UIWindow keyWindow] _autolayoutTrace]

1.30 Simple code timing block

NSTimeInterval start  = [[NSDate date] timeIntervalSince1970];
// Code to execute.
NSTimeInterval finish = [[NSDate date] timeIntervalSince1970];
NSLog(@"Execution took %f seconds.", finish - start);

1.31 Respond to changes in dynamic type

Register as an observer for the UIContentSizeCategoryDidChangeNotification:

[[NSNotificationCenter defaultCenter] addObserver:self

1.32 Add letterpress effect to an attributed string

Add NSTextEffectLetterpressStyle to the attributes dictionary:

NSTextEffectAttributeName : NSTextEffectLetterpressStyle

1.33 Switch with ternary operator

void ConditionalSwitchUsingNumber(int number);

int main(int argc, const char * argv[])
    return 0;

void ConditionalSwitchUsingNumber(int number)
    NSString *condString = (number == 0) ? @"foo" :
                        (number == 1) ? @"bar" :
                        (number == 2) ? @"baz" :

    NSLog(@"String: %@", condString);

1.34 Bindings detailed debug logging

defaults write com.yourdomain.yourapplication NSBindingDebugLogLevel 1
  • You can also add -NSBindingDebugLogLevel 1 to XCode arguments to be passed on launch.

1.35 Rename Xcode project

1.36 NSLog only for DEBUG

// Add to .pch file
#ifdef DEBUG
#   define NSLog(...) NSLog(__VA_ARGS__)
#   define NSLog(...)

1.37 Force specific localization at launch

[[NSUserDefaults standardUserDefaults] setObject:@[@"fr"] // French localization

1.38 Documents directory URL

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];

1.39 Format time duration

- (NSString *)formatDuration:(NSNumber *)duration
    NSUInteger interval = (NSUInteger)round([duration doubleValue]);
    NSUInteger hours = interval / 3600;
    NSUInteger minutes = (interval / 60) % 60;
    NSUInteger seconds = interval % 60;

    NSString *formatString;

    if (interval < 60)
        formatString = [NSString stringWithFormat:@"0:%02u", seconds];
    else if (interval < 3600)
        formatString = [NSString stringWithFormat:@"%u:%02u", minutes, seconds];
        formatString = [NSString stringWithFormat:@"%u:%02u:%02u", hours, minutes, seconds];

    return formatString;

1.40 Get current app version string

_versionNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey];

1.41 Collections time complexity

1.41.1 NSArray

  • Accessing an element, adding or removing an element at either end, and replacing an element tak constant time. Inserting an element into the middle of an array takes linear time.

1.41.2 NSDictionary, NSSet

  • If the keys are NSStrings, accessing an element, setting an element, and removing an element all take constant time.

1.42 NSAssert example

- (void)doSomethingWithAString:(NSString *)theString
    NSAssert(string != nil, @"String cannot be nil");
    NSAssert([string length] >= 3, @"String is too short");
    . . .

1.43 Odd/even

// Using modulus

NSInteger num;
if (num % 2)
  // odd
  // even

int main(void)
    int x;
    for (x = 0; x < 10; x++)
        if (x % 2)
            printf("%d is odd\n", x);
    return 0;

// Using bitwise
if((x & 1) == 0)

int main(void)
    int x;
    for (x = 0; x < 10; x++)
        if (x & 1)
            printf("%d is odd\n", x);
    return 0;

1.44 Genstrings - Generate strings file

find . -name \*.m | xargs genstrings -o en.lproj // xargs here puts the generated strings file into the en.lproj folder

1.45 Lazy getter

- (NSMutableArray *)nameList {
    return _nameList ? _nameList : [[NSMutableArray alloc] init];
// Or
- (NSMutableArray *)nameList {
    if (!_nameList) _nameList = [[NSMutableArray alloc] init];
    return _nameList;

1.46 Unit test property attribute

#import <objc/runtime.h>
- (void)testPropertyIsReadOnly {
    objc_property_t stringProperty = class_getProperty([MyCustomClass class], "mainString");
    const char *propertyAttrs = property_getAttributes(stringProperty);
    NSArray *attributes = [[NSString stringWithUTF8String:propertyAttrs] componentsSeparatedByString:@","];
    STAssertTrue([attributes containsObject:@"R"], @"property should be readonly");

1.47 Printing CPU registers in the debugger

// General Purpose Registers
register read

// For simulator
po $eax
po [$eax class]
po [$eax name]
po [$eax reason]

// For iOS device
po $r0

1.48 Random number test

NSCountedSet *numberCounter = [NSCountedSet set];

for (int i = 0; i < 5000000; i++) {
    NSUInteger randomIndex = arc4random_uniform(15);
    NSNumber *randomNumber = [NSNumber numberWithUnsignedInteger:randomIndex];
    [numberCounter addObject:randomNumber];

NSLog(@"%@", numberCounter);

1.49 Saving version number

- (void)saveApplicationUserData
    NSString *bundleVersionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString*) kCFBundleVersionKey];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:bundleVersionString forKey:VersionNumberKey];
- (BOOL)isOlderVersion
    BOOL isOlder = NO;
    NSString *bundleVersionString = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString*) kCFBundleVersionKey];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *archivedVersionString = [defaults objectForKey:VersionNumberKey];
    if (![archivedVersionString isEqualToString:bundleVersionString]) {
        isOlder = YES;
    return isOlder;

1.50 Shuffle an array

- (void)shuffleTest
    NSNumber *one = [NSNumber numberWithInt:1];
    NSNumber *two = [NSNumber numberWithInt:2];
    NSNumber *three = [NSNumber numberWithInt:3];
    NSArray *orderedArray = [NSArray arrayWithObjects:one, two, three, nil];
    NSUInteger arrayCount = [orderedArray count];

    NSCountedSet *testCounter = [NSCountedSet set];

    for (NSUInteger i = 0; i < 600000; i++) {
        // Reset the mutable array for every iteration of the test
        NSMutableArray *shuffledArray = [orderedArray mutableCopy];

        // Start at end of array and go backward
        for (NSUInteger i = arrayCount - 1; i > 0; i--) {
            NSUInteger n = arc4random_uniform(i + 1);
            [shuffledArray exchangeObjectAtIndex:i withObjectAtIndex:n];

        // Start at end of array and go backward using block
        /*[orderedArray enumerateObjectsWithOptions:NSEnumerationReverse
                                       usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                                           NSUInteger n = arc4random_uniform(idx + 1);
                                           [randomizedArray exchangeObjectAtIndex:idx withObjectAtIndex:n];

        // Start at beginning of array go forward
        /*for (NSUInteger i = 0; i < arrayCount - 1; i++) {
            NSUInteger nElements = arrayCount - i;
            NSUInteger n = arc4random_uniform(nElements) + i;
            [randomizedArray exchangeObjectAtIndex:i withObjectAtIndex:n];

        // Start at beginning of array and go forward with block (no speed difference)
        /*[orderedArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSUInteger nElements = arrayCount - idx;
            NSUInteger n = arc4random_uniform(nElements) + idx;
            [randomizedArray exchangeObjectAtIndex:idx withObjectAtIndex:n];

        // Naive shuffle  - Don't use this - Yields a biased distribution in counted set test
        /*for (int i = 0; i < arrayCount; i++) {
            int n = arc4random_uniform(arrayCount);
            [randomizedArray exchangeObjectAtIndex:i withObjectAtIndex:n];

        [testCounter addObject:shuffledArray];
    NSLog(@"%@", testCounter);

1.51 UIWebView force external links

#pragma mark - UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
    // Check if this was a click event and then some other criteria for determining if you want to launch Safari.
    if (navigationType == UIWebViewNavigationTypeLinkClicked) {
        [[UIApplication sharedApplication] openURL:request.URL];

        // Return false to indicate to the UIWebView to not navigate to the linked target
        return false;
    // Return true so that the UIWebView loads the link target
    return true;

1.52 NSUserDefaults

// Create a static key for the user defaults.
NSString * const DurationFormatPrefKey = @"DurationFormatPrefKey";

// Set the current user defaults.
[[NSUserDefaults standardUserDefaults] setInteger:durationFormatPreference

// Get the current user defaults.
durationFormatPreference = [[NSUserDefaults standardUserDefaults] integerForKey:DurationFormatPrefKey];

// Ensure a default value is set in the registration domain if user hasn't set one in the application domain.
+ (void)initialize
    NSDictionary *defaults = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:2]
    [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];

1.53 Wrap around array

int arrayCount = 15;
int currentIndex = 0;

// Go Forward
for (int i = 0; i < 100; i++) {
    currentIndex = (currentIndex + 1) % arrayCount;
    NSLog(@"currentIndex: %i", currentIndex);

// Go Backward
currentIndex = 0;
for (int i = 0; i < 100; i++) {
    if (currentIndex < 0)
        currentIndex = arrayCount - 1;
    NSLog(@"currentIndex: %i", currentIndex);

1.54 Validate App Store Receipt via Apple

// Use the sandbox url for testing and store url for when the app is live in the store.
// NSString *storeURLString = @"";
NSString *sandboxURLString = @"";

// Get receipt from app bundle
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];

// Convert data to base64 string
NSString *base64String = [receiptData base64EncodedStringWithOptions:0];
NSString *payloadString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", base64String];
NSData *payloadData = [payloadString dataUsingEncoding:NSUTF8StringEncoding];

// Configure the url POST request with body data
NSMutableURLRequest *postRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:sandboxURLString]];
[postRequest setHTTPMethod:@"POST"];
[postRequest setHTTPBody:payloadData];

[[[NSURLSession sharedSession] dataTaskWithRequest:postRequest
                                 completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                     if (!error) {
                                         id verificationResponse = [NSJSONSerialization JSONObjectWithData:data
                                         // The value for key "status" must be 0 or else the receipt is not valid.
                                         NSLog(@"Result: %@", verificationResponse);
                                     } else
                                         NSLog(@"Error: %@", [error localizedDescription]);
                                 }] resume];

1.55 Create an object of a particular class name using a string

id newObject = [[NSClassFromString(@"CLASS_NAME") alloc] init];

1.56 NSPredicate format string performance examples

Line 1, which uses BEGINSWITH is faster than line 2, which use CONTAINS.

1. searchString BEGINSWITH[n] "red"
2. searchString CONTAINS "red"

Line 1, which does math first rather than string searching first is faster (the computer is much faster at doing math compared to search).

1. timestamp BETWEEN (X, Y) OR searchString CONTAINS "red"
2. searchString CONTAINS "red" OR timestamp BETWEEN (X, Y)

1.57 Status bar item key

  • Add to Info.plist: key = Application is agent (UIElement) value = YES

1.58 Blocks that have escaped their scope and have been allocated to the heap

  • Look for (NSMallocBlock) in the alloctions instrument.

1.60 Resign first responder from anywhere in your app

// Make sure viewDidAppear has happened before sending this action to ensure that all links in the responder chain are connected.
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder)

1.61 Inspect the responder chain

- (void)inspectResponderChainFromSender:(id)sender
    if ([sender isKindOfClass:[UIResponder class]]) {
        NSMutableArray *responderChain = [NSMutableArray array];
        [responderChain insertObject:sender atIndex:0];

        while ((sender = [sender nextResponder])) {
            if ([sender nextResponder]) {
                [responderChain insertObject:[sender nextResponder] atIndex:0];

        NSLog(@"Responder Chain:");
        for (id obj in [responderChain copy]) {
            NSLog(@"%@\n***", obj);

2 C

2.1 Struct

struct StructName {
    type name;
    type name;
typedef struct StructName StructName;
  • Members of a struct can be other structs.

2.2 Typedef

typedef "existing type" "new type"
  • The compiler will treat any new instances of "new type" as if they were "existing type".
  • CGFloat on 32 bit is typdef as a float and typedef as a double on 64 bit machines. The current iPhone is 32 bit and the Mac is 64 bit. Using CGFloat rather than float or double improves cross-hardware compatibility in your code.

2.3 Pointers

  • A reference to a location in memory.
  • All pointer variables are the same size. OSX pointers are 64 bits, iOS pointers are 32 bits.
  • Use the * dereference operator in front of a variable to use the value stored at the memory address.
  • Variables are like constant pointers. They maintain the same memory address throughout their life, and different values can be assigned to the variable during its life.
  • To access the members of a struct through a pointer use the -> arrow operator rather than the dot operator.

2.4 Pointers to pointers

  • A variable to another pointer.
  • **pointerToPointer: get the value-at-address stored in pointer, which is also an address, and then get the value-at-address of that address.
  • There's no limit to the levels of indirection for pointers to pointers. Anything more than 3 levels can create too much mental complexity in your code.

2.5 Arrays

  • An array is constant variable and can't be reassigned. You can change the array elements but not the the value of the array itself.

2.6 Allocating and freeing memory

  • Stack variables are local to the current function and the memory is automatically freed by the system after the function completes.
  • Memory from the heap is manually allocated and freed.
  • Mac OS X Manual Page For malloc

2.7 Functions

  • All parameters and return values is C are passed in by value rather than by reference. This is handled on the stack and the compiler will automatically manage memory.
  • When a pointer is passed in to a function both the caller and the function itself have the address to the same block of memory. Changes made to the block of memory inside the function can also be seen by the caller.
  • On the other hand, if the function changes the value of the pointer, the callers pointer will NOT be changed.

2.8 Function pointers

  • Variables that you can use to call a function.
  • Values of variables are decided at runtime, which means you can decide at runtime which function to invoke.

3 Scheme

3.1 Misc

  • Two types of expressions: Atoms, Compound expressions.
  • Numbers are atom that are self-evaluating.
  • A sequence of characters like "+" or "asdf" is a symbol; which is also an atom. Atoms also know as atomic expressions.
  • Compound expression is formed by combining atoms and other compound expressions. Expressions inside of expressions are subexpressions.
  • Parenthesis mean: call this procedure with these arguments.
  • No parens means: don't call the procedure, just return the procedure itself.
  • Compared to other languages, in Scheme, procedures are things; not just actions.
  • In Scheme, everything is done by calling a procedure.
  • Single quote creates a literal.
  • Functions allow us to think about a process in plain english. They also allow us to think about a transformation of information (take in something we know, and return something we don't know). Transform something to get new results.
  • We can also think of functions as objects. Ex. using a function as an argument to another function.
  • Higher-order functions (functions of functions). Use the result of one function as an argument to another function. Write large programs by defining a number of small functions, and then mixing them with each other to produce the end results.
  • Each special form in Scheme may have its own evaluation rules.
  • You can never use the name of a special form as a parameter.
  • Keywords also can't be used as parameters or as names of procedures.
  • Think of a procedure as a description of the process for the computer to calculate a result.
  • Think of a function as the association of the starting values and the resulting values, regardless of the process of getting to that result.
  • A function is represented by a procedure. We speak of procedures when thinking about the operations the computer is working on. And we speak of functions when we are focused on the value returned.
  • Abstraction and naming: When we have a good name for a pattern, we can work with the name itself rather than thinking of its pieces.
  • Functions can only have one return value.
  • The value of a lambda expression is a procedure.
  • Recursive functions must have two things: (1) at least one case where the recursion breaks the problem down into a smaller size, (2) a base case where the result can be calculated without recursion.
  • In a recursive process, you have pending operations when the procedure calls itself. In an iterative process, there are no pending operations. Iterative proceses have fixed space complexity. For recursive processes, space grows continuously unless the language uses tail optimization. In general, iterative processes often have a lower order of growth for space than recursive processes.
  • Tail recursive: Scheme executes an iterative process in constant space. Memory does not build up for recursive calls.

3.2 REPL

  • The "read-eval-print loop" is the interaction between you and Scheme. Scheme reads your input, then evaluates it, then prints out the results, and does this over and over again.

4 Core Data

4.1 Accessing Stored Fetch Requests

NSManagedObjectContext *moc = [self managedObjectContext];
NSPersistentStoreCoordinator *psc = [moc persistentStoreCoordinator];
NSManagedObjectModel *model = [psc managedObjectModel];
NSFetchRequest *request = [model fetchRequestTemplateForName:@"myList"];
NSMutableArray *sortArray = [NSMutableArray array];
[sortArray addObject:[[NSSortDescriptor alloc] initWithKey:@"name"
[request setSortDescriptors:sortArray];

4.2 Unit testing setUp tearDown Core Data stack

    NSPersistentStoreCoordinator *_coordinator;
    NSManagedObjectContext *_context;
    NSManagedObjectModel *_model;
    NSPersistentStore *_store;

- (void)setUp {
    _model = [NSManagedObjectModel mergedModelFromBundles:nil];
    _coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_model];
    _store = [_coordinator addPersistentStoreWithType:NSInMemoryStoreType

    _context = [[NSManagedObjectContext alloc] init];
    [_context setPersistentStoreCoordinator:_coordinator];

- (void)tearDown {
    _context = nil;
    NSError *error = nil;
    STAssertTrue([_coordinator removePersistentStore:_store error:&error], @"Could not remove the persistent store: %@", error);
    _store = nil;
    _coordinator = nil;
    _model = nil;

- (void)testThatPersistentStoreExists {
    STAssertNotNil(_store, @"Store should not be nil");

4.3 Fetch random object from Core Data database

NSManagedObjectContext *context = self.managedObjectContext;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"MyEntity"];
// Add any predicates if needed
NSUInteger objectCount = [context countForFetchRequest:request error:nil];
NSUInteger randomIndex = arc4random_uniform(objectCount);
NSArray *fetchedArray = [context executeFetchRequest:request error:nil];
NSManagedObject *randomObject = fetchedArray[randomIndex];

5 Git

5.1 Ignore directories in .gitignore file

  • Add dir_to_ignore/ to .gitignore

5.3 Rename branch

  • git branch -m old_branch new_branch

5.4 Delete remote branch

  • git push origin --delete BRANCHNAME

5.5 Show detailed remote information

  • git remote show origin

5.6 Show log diffs for an individual file

  • git log -p FILENAME

5.7 Checkout a remote branch into a new local branch

  • git checkout -b test origin/test

5.8 Change remote url

  • git remote set-url origin http...

5.9 Configure email address

git config --global "EMAIL ADDRESS"

5.11 Discard changes in working directory for file

git checkout -- file.txt

5.12 Install Git autocompletion for OSX Terminal

  1. Download Git autocompletion file to home directory
    curl -o ~/.git-completion.bash
  2. Update .bash_profile
    source ~/.git-completion.bash

5.13 Remotes

  • Push all contents of local repo to a new remote repo:
    git push -u origin --all   # to push changes for the first time
  • Delete a remote
    git remote rm REMOTETODELETE

5.14 Change your last commit

git commit --amend

5.15 Git show

5.15.1 Show changes introduced by the most recent commit

git show

5.15.2 Show changes from the parent of HEAD

git show HEAD~

5.15.3 Show changes from the commit before HEAD

git show HEAD~2

5.16 Working with previous commits

5.17 Reflog

  • A safety net.
  • Recover lost commits with a detailed log using reflog and reset
    git reflog
    git reset --hard HEAD@{2}
  • Rebasing can create a large amount of entries in the reflog

5.18 Rebase

  • Do not rebase commits that you have pushed to a public repository.

5.18.1 Interactive rebase

Rebase the previous 5 commits. Use your text editor to edit the commands for each commit.

git rebase -i HEAD~5

5.19 Delete local git repository

  • Remove the hidden directory named .git
  • rm -r .git

5.20 Check for remote diffs

  1. git fetch origin to sync up the remote branch.
  2. git diff origin/master to view the diffs.
    • Use git difftool origin/master to view changes in a third-party difftool.
  3. git merge origin/master to merge in the changes.

5.23 Delete .orig files after resolving merge conflicts

  • git clean -f

6 Elisp

6.1 Org mode - Insert source code

  • <s <TAB>

6.2 Org mode - Don't sub/superscript "_" and "^".

(setq org-use-sub-superscripts nil) ;; Turn off subscripts with "_" and "^"

6.3 Org mode - Export Options

(setq org-export-html-postamble nil) ;; Remove postamble when exporting
(setq org-export-with-toc 2) ;; Limit table of contents to two levels
(setq org-export-with-section-numbers 1) ;; Limit section numbers to
;; one level - set to "nil" to remove section numbering

6.4 Hook to auto-enable automatic browser preview upon saving an HTML file

(add-hook 'html-mode-hook 'html-autoview-mode)

6.5 Clipboard

(setq x-select-enable-clipboard t)

6.6 Comment / uncomment keybinding

;; comment region
(global-set-key "\C-xc" 'comment-region)
;; uncommment region
(global-set-key "\C-xu" 'uncomment-region)

6.7 Emacs Word Count

  • Select a region. Press M-= and the results will be displayed in the mode-line.

6.8 Evaluate an entire buffer

  • M-x eval-buffer

7 Smalltalk

  • The up-arrow character on the Mac is ^ symbol.
  • No mathematical precedence (Left-to-right). Use parenthesis where needed.
  • Message syntax: Execution order
    • Unary > Binary > Keywords
  • Cascade operator ; is used to send multiple messages to the same object.
  • Conditionals are messages sent to boolean objects: ifTrue:, ifFalse:.
    1 < 5 ifTrue: [Transcript show: 'One is less than five']

8 Shell

8.1 Change Terminal directory to frontmost Xcode project window

Works best when an Xcode project is already open.

Full Terminal command:

cd `osascript -e "tell application \"Xcode\"" -e "get project directory of front project" -e "end tell"`; pwd;

Optional: Add this as an alias to .bash_profile. Then type xt in Terminal to invoke the command.

alias xt='cd `osascript -e "tell application \"Xcode\"" -e "get project directory of front project" -e "end tell"`; pwd;'

8.2 Launch Services Quarantine Download History

8.2.1 Log download history

sqlite3 ~/Library/Preferences/* 'select LSQuarantineDataURLString from LSQuarantineEvent'

8.2.2 Delete download history

sqlite3 ~/Library/Preferences/* 'delete from LSQuarantineEvent'

8.3 Disable smooth scrolling OSX

defaults write -g NSScrollAnimationEnabled -bool NO

8.4 Restore delete key as back navigation for Safari

defaults write -bool YES

8.5 Copy current path from Finder into Terminal

  1. Select a folder or file in Finder.
  2. Copy it with Command-C.
  3. Open Terminal.
  4. Type "cd" SPACE and paste the path with Command-V.
  5. You can also do this by dragging and dropping the file or folder into Terminal.

8.6 Open current Terminal directory in Finder

open .

8.7 Fix archive utility hang issue in 10.8.2

sudo killall -KILL appleeventsd

8.8 Safari 6 preference keys for fonts

defaults write Verdana
defaults write 18
defaults write Menlo
defaults write 15

8.9 Symbolic link


8.10 Delete directory interactively


8.11 Commands

  • sort
  • < redirect input - send the input from the command on the right to the command on the left
  • > redirect output - take the output of the command on the left, then write it to the command on the right.
  • >> appends output to a file - take the output of the command on the left, then write it to the file on the right
  • C-d to stop
  • less writes content of a file on the screen one page at a time.
  • head writes the first 10 lines of a file to the screen
  • tail writes the last 10 lines of a file to the screen
  • type "/" while in man or less to start a search
  • grep [keyword] [file] (see also find)
  • wc: word count
  • command1 | command2: pipe the output of command1 to the input of command 2
  • sort: sorts data
  • * wildcard matches against none or more characters
  • ? wildcard matches against one character
  • whatis COMMAND gives a one-line description of the command
  • Type "&" at the end of a command to execute it in the background
  • jobs gives the list of current processes
  • Suspend foreground process with C-z. Then type bg to send it to the background.
  • Restart a suspended process with fg %jobnumber
    • Just typing fg with no number foreground the last suspended process
  • Kill a foreground process with C-c. Kill a suspended process with kill %jobnumber
  • You can also kill a process with kill ProcessID (use -9 flag to force)
  • df disk free space
  • du used disk space
  • zcat reads gzipped files without uncompressing them
  • file * shows data type of files
  • !! recalls last command - !-3 recalls third most recent command - !12 recalls the 12th command from the history list - !grep recalls last command starting with grep

8.12 iPhone header file location

# Select your SDK then navigate to /System/library/frameworks

8.13 NSGlobalDomain plist location

  • ~/Library/Preferences/.GlobalPreferences.plist

9 Web

9.1 iOS select control that jumps to a new link

9.1.1 Javascript

function loadPage(url) {

9.1.2 HTML

<div id="iosnavigation">
    <header id="iosheader"><h1>Test</h1></header>
    <select onchange="loadPage(value)">
      <option value="">Apple</option>
      <option value="">Amazon</option>
      <option value="">CNN</option>
      <option value="">Ebay</option>
      <option value="dummypage.html">Dummy Page</option>

9.1.3 CSS

#iosnavigation {
    text-align: center;
    top: 0;
    left: 0;
    width: 100%;
    height: 80px;
    position: absolute;
    line-height: 30%;
    background-color: #EEEEEE;
/* 16px font size prevents auto-zooming for select control */
#iosnavigation select:focus { font-size: 16px; }

10 Misc

  • If you have multiple scroll views in one view controller, set scrollsToTop:NO on all the scroll views that you don't want to scroll to top. Having multiple scroll views will cause conflicts with scrollsToTop.
  • For web views, load the request in viewDidLoad and not in init.
  • Entity-relationship Modeling (Wikipedia)
  • Object Modeling (Apple Docs)

10.1 Converting Coordinates in the View Hierarchy

10.2 Unit testing view controllers

10.2.1 Three types of verification tests

  1. Return value verification
  2. State verification
  3. Behavior verification (Use mock objects)

10.2.2 Testing outlets

  1. Is outlet hooked up?
  2. Is outlet connected to the correct action?
  3. Invoke the action through a test to simulate user input.

10.3 Testing iOS App name abbreviation on home screen

  • Create a folder on the home screen and then rename it multiple times to test if letters get abbreviated.

10.4 iOS Static Library Xcode Issues

  • Drag the .xcodeproj file of the static library into your desired app as a top-level item. Do not nest that file into any group folders.
  • In your application project add -ObjC to Build Settings > Other Linker Flags.
  • Include the library name along with the header name when importing:
    #import "LibraryName/HeaderName.h"
    • Autocomplete does not seem to work when importing headers for static libraries.
  • Expose the header files for any new classes that are created in the static library. Add them under Build Phases > Copy Files.

10.5 Xcode Structured Comment Tags

  • @a = italic
  • @b = bold
  • @c = monospaced
  • /// = Description for method or property
  • @class = Class name
  • @param = parameter description
  • @return = return value description
  • @category = Category name
  • @protocol = Protocol name
  • @discussion = Discussion description

10.6 Helpful Xcode launch arguments & environment variables

10.6.1 Launch Arguments

  • -NSDoubleLocalizedStrings YES
  • -NSShowNonLocalizedStrings YES
  • 3 // (Takes value 1-3. The higher the value, the more verbose the output)
  • YES
  • -UIViewShowAlignmentRects YES, NSViewShowAlignmentRects YES // Show overlay views on top of alignment rectangles
  • -AppleLanguages (fr), -AppleLocale fr_FR // French language and locale. Create a separate scheme for each language for quick switching.

10.6.2 Environment Variables

  • NSZombieEnabled
  • NSDeallocateZombies
  • Memory Helpers: MallocScribble, MallocGuardEdges, MallocStackLogging, MallocStackLoggingNoCompact
  • NSUnbufferedIO

10.7 Share Sublime Text 3 settings with Dropbox

10.8 Prevent Xcode from opening last opened projects upon launch

10.9 Convert plist file to json

plutil -convert json -r SOURCE_FILE -o DESTINATION_FILE
# example: plutil -convert json -r MyFile.plist -o MyNewFile.json

10.10 OSX crash report location for iOS devices

  • ~/Library/Logs/CrashReporter/MobileDevice

11 Storyboards

  • In prepareForSegue you can get the current cell from sender. Then use indexPathForCell to find the object in your model.
  • In prepareForSegue the destination view controller is instantiated but not loaded. None of its IBOutlets have been set yet.

11.1 iOS 6 Storyboard Additions

11.1.1 Embed Segues

  • Used for segueing between container view controllers.
  • Simply add a container view to the source.
  • Then drag a segue to the view controller that will be contained.
  • Use prepareForSegue if needed.

11.1.2 Unwind Segues

  • You can target view controllers that are not inside the storyboard.
  • Add unwind ibaction to view controller subclasses.
  • Resolve destination at runtime
- (IBAction)done:(UIStoryboardSegue *)segue {
    // React to the impending segue
    // Pull state back, etc.
  1. Callback order
    1. Find the destination
    2. Invoke -prepareForSegue on the source
    3. Run the unwind action
    4. Perform the segue

11.2 Settings Pages With Storyboards

  • Use a static tableview rather than prototype cell based for settings page.

11.3 Loading storyboard scenes without segueing

  • add the storyboard id in interface builder. Then call instantiateViewControllerWithIdentifier:

11.4 Container View Controllers Using Storyboards

  • You can add a view controller as a child of another view controller. In interface builder, drag a "container view" out of the object library and place it on your view controller. Then connect that container view to the child view controller with an embed segue.

11.5 Convert nib to storyboard for a view controller

  1. Select the view controller's files owner in the nib.
  2. Select edit > select all.
  3. Select copy.
  4. Go to the storyboard and create an empty view controller by deleting its view.
  5. Select the empty view controller and select paste.
  6. Assign you custom view controller class to the view controller in the storyboard.
  7. Replace the IBAction methods for view controller transitions with segues using prepareForSegue.
  8. More info in Apple docs: Converting to Storyboards Release Notes

12 Autolayout

12.1 Misc

  • Constraints placed on views relative to other views.
  • NSLayoutConstraints
  • Tasks: Setting intrinsic sizes, pinning and aligning, editing the properties of constraints, setting priorities.
  • Fixed constraints are not recommended due to localization issues. Use 0 or default instead.
  • Use Editor > Size to Fit Content to clear a fixed constraint for views like labels and buttons.
  • 1000 priority on a constraint means that that constraint must be enforced.
  • In Xcode, blue constraints are user constraints and can be deleted if necessary. Purple constraints can't be deleted. They are generated by Xcode to create a sufficient layout.
  • Do geometry-related setup in viewWillLayoutSubviews if Autolayout is on. Otherwise, use viewWillAppear.
  • A view will typically have 4 constraints: width, height, position x, position y.
  • Add constraints to the nearest common anscestor.

12.2 Equation

  • x = a*y + b
    • x = some view (ex. leading edge of a view)
    • y = some other view (ex. trailing edge of a view)
    • a = some multiplier (ex. 1)
    • b = some constant (ex 10)
    • Answer: view x is 10 points to the right of view y

13 Collection Views

13.1 Deleting Items

[self.collectionView performBatchUpdates:^{
     NSArray* itemPaths = [self.collectionView indexPathsForSelectedItems];
     // Delete the items from the data source.
     [self deleteItemsFromDataSourceAtIndexPaths:itemPaths];
     // Now delete the items from the collection view.
     [self.collectionView deleteItemsAtIndexPaths:tempArray];
  } completion:nil];

13.2 Rendering Issue

  • Check minimum line spacing and cell spacing in Interface Builder.
  • You should always attach your gesture recognizers to the collection view itself and not to a specific cell or view. The UICollectionView class is a descendant of UIScrollView. Attaching your gesture recognizers to the collection view is less likely to interfere with the other gestures that must be tracked. In addition, because the collection view has access to your data source and your layout object, you still have access to all the information you need to manipulate cells and views appropriately.

13.3 Changing UICollectionView scroll direction based on current orientation

On the iPad, if you want to have vertical scrolling when the device is in portrait and horizontal scrolling when device is landcape:

  • Create a method to test if the device is iPad. Then test the current orientation and set the scroll direction accordingly:
- (void)updateScrollDirectionRelativeToCurrentOrientation {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        static UICollectionViewFlowLayout *currentFlowLayout = nil;
        if (!currentFlowLayout) currentFlowLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;

        if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
            currentFlowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
        } else
            currentFlowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;

        self.collectionView.collectionViewLayout = currentFlowLayout;
  • Call this method in viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self updateScrollDirectionRelativeToCurrentOrientation];
  • Call it again whenever the device rotates in didRotateFromInterfaceOrientation:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    [self updateScrollDirectionRelativeToCurrentOrientation];

Author: Bryan Luby

Created: 2014-02-02 Sun 14:02

Emacs (Org mode 8.2.1)