EmberJS selective didUpdateAttrs

TLDR:
Register specific arguments with a callback instead of dealing with the general didUpdateAttrs.

Longer Description:
Due to the deprecation of didUpdateAttrs({newAttrs, oldAttrs}) of the arguments oldAttrs and newAttrs in version 2.12 until 2.13 we need a selective way of keeping track of old and new param changes.

The accepted use is didUpdateAttrs({attrs}) , we only look at the new attrs and compare with temp values stored locally

deprecations details: https://www.emberjs.com/deprecations/v2.x/#toc_arguments-in-component-lifecycle-hooks

This mixin allows you to register a specific attrs with a specific callback and an optional trigger value.  The callback will only be invoked if that attr changes.

Usage:

import DidUpdateAttrsNew from 'app/mixins/did-update-attrs-new';
export default Ember.Component.extend(DidUpdateAttrsNew, {

init() {
this._super(...arguments);

// will call myKeysCallback only when this.get('myKey') === 3
this.registerDidUpdateAttrsCallback('myKey', this.myKeysCallback, 3);

// will call myKeyAorBCallback everytime this.get('myKeyA') changes value
this.registerDidUpdateAttrsCallback('myKeyA', this.myKeyAorBCallback);

// can use the same callback for different key updates
this.registerDidUpdateAttrsCallback('myKeyB', this.myKeyAorBCallback);
},

myKeysCallback(newValue) {
...
},

myKeyAorBCallback(newValue) {
...
}

GitHub source: https://github.com/HelloMihai/didUpdateAttrsNew

GoPro video + gps telemetry merging

Instructions on how to merge GoPro videos with GPS telemetry.

Issue: Long GoPro recordings are split into 4gb files.  When you try to merge them the GPS telemetry is also split.  This joins all into one long video with one long telemetry path.
2018-07-04_14-05-34

Frustration:  I have tried all the solutions out there; FFmpeg, old GoPro studio, custom video editors that read metadata, RaceRender, and even edited and merged manually CSV telemetry files which was very time-consuming.  Some worked but painfully.  This is the most reliable & convenient user-friendly solution.  Hope this helps someone.

Yes I know there’s RareRender but after spending $500 on a camera I’m not spending more money on software!

Solution:  DashWare on Windows.

Software Needed:
(contact me directly if files are not available for download anymore)
(versions are posted for reference, newer software function be better)
skip steps 1 & 2 if you’re already using windows

  1. VirtualBox Version 5.2.12 r122591 (Qt5.6.3) run windows on a mac, not needed if you’re using windows.
  2. Windows 10DashWare only works on windows, but many versions as far back as XP.  I used windows 10.  Download and you don’t need to activate it.
  3. DashWare 1.9.1 video-merging software
  4. Visual C++ Package 2013vcredist_x64.exe DashWare needs it to read telemetry data from the video
  5. Visual C++ Package 2015 vc_redist.x64.exe DashWare needs it to create videos else it’ll crash on export

Detailed Steps:

  1. Installation:
    2018-07-04_13-35-17
  2. install VirtualBox and Windows 10 (unless you’re on windows) example virtual box settings for best processing:
    1. settings: system: motherboard 8192 MB
    2. settings: system: processor: 4
    3. settings: display: video memory: 256 MB
    4. settings: display: Acceleration: Enable 3D Acceleration
    5. settings: display: Acceleration: Enable 2D Video Acceleration2018-07-05_15-58-20
  3. install DashWare, then the packages in step 4 & 5.
  4. Reboot
  5. Note this will not work with Loop Video recording mode, only on regular long video recording automatically split by the camera.  The telemetry data for loop videos restarts the time counter from 0 so time cannot be synced unless you manually increment the time in the exported CSV files.
  6. Editing:
    1. make sure your video was recorded with GPS on and not Loop Videos. (can check in the Quik GoPro app)
    2. create a new project using “GoPro Template Imperial”:
      2018-07-04_13-43-35.gif
    3. merge the videos retaining telemetry data, and add data CSV file from the merge
      2018-07-04_13-49-46.gif
    4. export video with telemetry
      2018-07-04_13-58-25.gif
    5. no next step

 

Result: 😍

2018-07-04_12-55-37

Opening a view when app is launched from a local notification ios7 ios8

If you’re targeting iOS 8 and working with a navigation controller its pretty easy… just:

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    if (application.applicationState != UIApplicationStateActive) { // only if not active
    UIViewController *myDesiredView = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:@"Whatever id from storyboard"];
    [self.window.rootViewController showViewController:myDesiredView sender:self];
}

But this wasnt enough since I wanted it to work in ios7 and I had a lot of changes happening in the first views view did load. To get everything right I wanted the first view when loaded to know if to navigate to another view AND if its already loaded and a notification came in which app is not active (not foreground) to navigate to a view.

  1. set two static globally available constant strings in a class included in both the view controller and appDelegate
    static NSString* const NOTIFICATION_TRANSITIOIN = @"NOTIFICATION_TRANSITIOIN";
    static NSString* const NOTIFICATION_VIEW_READY = @"NOTIFICATION_VIEW_READY";
    
  2. in the desired viewControllers method viewDidLoad set the notifications.  Create a method to capture the notification
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(localNotificationNeedsTransitionToViewController) NOTIFICATION_TRANSITIOIN object:nil]; // listen for notifications when the app receives a notification
        [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_VIEW_READY object:nil]; // post a notification that the view is ready to receive notifications
    }
    
    -(void)localNotificationNeedsTransitionToViewController  {
        // segue to your desired view
    }
    
  3. appDelegate.m
    
    //declare in the interface to keep track if we need to transition
    @property (nonatomic) BOOL flagIndicatingWellNeedATransition;
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // wait until view controller is ready before posting if there is a uilocalnotification for transitioning
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyViewControllerOfNotification) NOTIFICATION_VIEW_READY object:nil];
            
        // check if launched from a notification
        if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey])
           self.flagIndicatingWellNeedATransition = true;
        
        return YES;
    }
    
    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
    {
        if (application.applicationState != UIApplicationStateActive) { // only if not active
            self.flagIndicatingWellNeedATransition = notification != nil;
            [self notifyMenuViewControllerOfNotification]; // by now the view controller is ready so notify away yay yeeeahhhh
        }
    }
    
    - (void)notifyViewControllerOfNotification {
        if (self.flagIndicatingWellNeedATransition)
            [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_TRANSITIOIN object:self];
        self.flagIndicatingWellNeedATransition = false; // reset it just in case
    }
    

UIScrollView

Making a simple scroll view in 5 steps.

  1. add a UIScrollView to your scene
  2. add a UIView to your UIScrollView
  3. set the size of the UIView
  4. add some content to your UIView
  5. set the UIScrollView the contentSize of its subview

In Detail

  1. add a UIScrollView to your scene
    2015-01-12_12-39-01
  2. add a UIView into your UIScrollView
    2015-01-12_12-39-56
  3. set the size of the UIView
    2015-01-12_12-51-19
  4. add some content to your UIView
  5. set the UIScrollView the contentSize of its subview
    1. create an outlet for the UIScrollView
      2015-01-12_12-55-40
    2. add code which specifies to the UIScrollView the size of its content
      -(void)viewWillAppear:(BOOL)animated
      {
          [super viewWillAppear:animated];
          
          // set scrollview content size dynamically
          CGRect contentRect = CGRectZero;
          for (UIView *view in self.scrollView.subviews) {
              contentRect = CGRectUnion(contentRect, view.frame);
          }
          self.scrollView.contentSize = contentRect.size;
      }
      

publish your app to the app store Xcode 6

I have to admit its not an easy process but ill give you the cliff notes to speed things up a bit.  This process can be different if too much time passes.  I will update it if I find changes when deploying my apps.

in short:

  1. distribution provisioning profile
  2. archive app containing output of step1
  3. iTunes Connect profile with screenshots, optional video and text
  4. submitting binary through Xcode 5 or greater

in detail:

  1. distribution provisioning profile
    1. visit: https://developer.apple.com/account/ios/device/deviceList.action
    2. select Distribution
      2015-01-08_13-55-13
    3. select Add
      2015-01-08_13-55-49
    4. select App Store in the new screen
      2015-01-08_13-56-36
    5. 2015-01-08_13-57-20
    6. select your App ID (note wildcard vs specific id) This step assumes you have an app Id since everyone should test their app on a real device before submitting to app store.  You already paid the Apple Dev to publish so get cracking on those bugs.. if you only tested in a simulator you’ll be surprised how many bugs you’ll find on a real device.
      2015-01-08_13-58-30
    7. 2015-01-08_13-57-20
    8. select your certificate (usually your name)
      2015-01-08_14-08-18
    9. 2015-01-08_13-57-20
    10. name your profile.  Usually AppName Distribution Profile
      2015-01-08_14-10-32
    11. 2015-01-08_14-11-35
    12. 2015-01-08_14-12-00
    13. run the downloaded file and Xcode should open
  2. archive app containing output of step1
    1. open your project in Xcode
      1. remove all NSLog in your code 2015-01-08_14-32-14
      2. remove all warnings
      3. check your app runs correctly on a device
      4. make sure all icons are set and have correct dimensions
        http://makeappicon.com/ios8icon
      5. review other app store guidelines
        https://developer.apple.com/app-store/review/
    2. project code signing
      1. select your projects root
        2015-01-08_14-37-45
      2. select Build Settings
        2015-01-08_14-39-18
      3. select iOS Distribution under Identity (change this back to developer when… yup developing)
        2015-01-08_14-41-07
      4. its not necessary but select iOS Distribution for all fields
        2015-01-08_14-47-49
    3. create an archive of the app
      1. select deployment to iOS Device (or your device if connected in)
        2015-01-08_14-42-55
      2. menu > Product > Archive
        2015-01-08_14-44-28
      3. add a comment to distinguish this archive from others
        2015-01-08_14-52-09
      4. your app is now created with the correct code signing identity.  We will submit this file for review
  3. iTunes Connect profile with screenshots, optional video and text
    1. log into iTunes Connect
      https://itunesconnect.apple.com
    2. my apps
      2015-01-08_14-50-16
    3. add
      2015-01-08_14-54-41
    4. Fill out the form.
      2015-01-08_15-02-04

      1. Select the bundle ID we used in the first step to create the Distribution Certificate
        1. NOTE: the profile might not be available for a several minutes.
      2. Bundle Id Suffix: com.something.something (look in Xcode project settings > General > Bundle Identifier)
        2015-01-08_15-04-06
    5. 2015-01-08_15-10-13
    6. Select the cost tier (in another tab)
      2015-01-08_15-15-59
    7. the rest of the forms are pretty lengthy but should be self explanatory.
      1. icon has to be 1024×1024 and best to use a PNG
      2. image asset sizes
        1. 3.5 inch 640×960
        2. 4 inch 640×1136
        3. 4.7 inch 750×1334
        4. 5.5 inch 1242×2208
      3. you will need a Support URL.  If you don’t have a website for support create one for free at http://www.heeduser.com/ or something similar.
    8. After saving, make sure the status of your app indicates “Prepare for Submission”
      2015-01-08_15-52-36
  4. Submitting binary through Xcode 5 or greater
    1. open Xcodes organizer window
      2015-01-08_15-54-56
    2. select Archives
      2015-01-08_15-55-23
    3. Select the archive that we created with the Distribution Certificate
      2015-01-08_15-56-17
    4. Submit
      2015-01-08_15-56-45
    5. it will ask you to select your Id, and some subsequent steps and all should go well.
      1. on error “Your account already has a valid iOS Distribution certificate”:
        2015-01-08_16-26-15

        1. log into member services and revoke your Certificates and refresh them in Xcode
    6. All set, just submit
      2015-01-08_16-27-44
    7. Return to iTunes Connect and you’ll app should show that its “Waiting for Review”, if not select a build and click “Submit for Review”
      2015-01-08_16-33-24

Adding a new testers UDID for your app provisioning file and refreshing – crashlytics

I like test flight but I feel crashlytics.com has a much smoother process.

install crashlytics SDK and app 2014-11-04_22-17-16

to create an IPA file to submit to crashlytics

  • select iOS device 2014-11-04_22-17-54 for deployment
  • menu, product – archive
    2014-11-04_22-18-37
  • when the new IPA is created crashlytics will ask you with a popup to distribute.
    2014-11-04_22-19-15
  • if you try and distribute to someone whos UDID is not in your provisioning profile you will get a response from crashlytics with that persons UDID to add to your provisioning profile.
  • save the file from crashlytics menu or the key they provide.
  • go to
    https://developer.apple.com/account/ios/device/deviceList.action
    and sign in.
  • click the plus to add a new device 2014-11-04_22-21-40
  • either upload the file from crashlytics or add the info manually
    • ive had the window lock in the processing screen for several minutes.  I refreshed and the device was there. If not rinse and repeat.
  • now its time to refresh the profile that XCode has to build a new IPA with the new provisioning profile that will allow your mate to install
    • open Xcodes settings
    • open accounts
      2014-11-04_22-24-12
    • double click your name
      2014-11-04_22-24-33
    • click refresh in the new window2014-11-04_22-25-06
    • now create a new IPA through the archive menu and redeploy to your mate.

objc custom Back UINavigationBar BarButtonItem

If you need to replace the default iOS <Back button you’ll get annoyed but heres what worked!

You’ll need this in cases where you want to control a users desire to unwind a view with an alert or other actions.

  • create and import a blue png image for the left 1413857655_icon-ios7-arrow-back-64-blueBackButton (black tinted to #0083E2)
  • create a UIView
  • add a button to the new UIView and name it Back and set the image to the icon
    2014-10-20_17-46-43
  • drag the UIView to the left side of the navigation bar (can link this button to your IBActions)
    2014-10-20_17-45-23
  • if you need to to move the button more to the left well need some code, creating an array with a negative space on the left of the current button
        UIBarButtonItem* currentBack = self.navigationItem.leftBarButtonItem;
        UIBarButtonItem *negativeSpace = [[UIBarButtonItem alloc]
                                           initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
                                           target:nil action:nil];
        negativeSpace.width = -8;// it was -6 in iOS 6
        [self.navigationItem setLeftBarButtonItems:[NSArray arrayWithObjects:negativeSpace, currentBack, nil] animated:NO];
    

 

 

The result is almost identical to that of the default iOS back button
(custom one is on the bottom.. maybe?)

2014-10-20_17-49-45

 

 

you may find other icons for your needs on sites like this:
https://www.iconfinder.com/icons/211686/arrow_back_icon#size=64

 

 

 

If you just want to change the title of the button and keep the original styling and functionality, the parent view controller needs to set the title.
In the parent view controller add
 

- (void)viewDidLoad
{
    [super viewDidLoad];
    ...
    self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"BackTitle"
                                                                             style:UIBarButtonItemStylePlain
                                                                            target:nil
                                                                            action:nil];
}

Core Data versioning, migration, with Lightweight Migration

You created a data model, published your app, made a change to the mode and now all your clients complain about app crashes with the new app!

2014-09-16_18-15-08

Not so fast.  Should have read this first.

 

In Short

  1. version your data models
  2. make changes to the new model
  3. turn on Light Weight Migration

In Detail

  1. version your data models
    1. select your xcdatamodeld
      2014-09-16_18-21-37
    2. add version
      2014-09-16_18-22-21
    3. name it anything that makes sense to youd like
    4. green checkmark represents current version
      2014-09-16_18-23-36
    5. select the new model as the current
      1. select the top model file
        2014-09-16_18-25-07
      2. show the utilities right tab and select the file inspector
        2014-09-16_18-25-43
      3. change the current model to the new one created and note the green checkmark update
        2014-09-16_18-27-21
  2. make changes to the new model
    1. add a new entity or rename etc
      2014-09-16_18-29-09
    2. regenerate your NSManagedObject subclasses
  3. turn on Lightweight Migration (really capable actually)
    1. can handle:
      1. adding
      2. removing
      3. renaming
      4. change relationship types
    2. open AppDelegate.m at the –(NSPersistentStoreCoordinator*)persistentStoreCoordinator method
    3. create a dictionary with migration options and pass that to the addPersistentStoreWithType call. See the highlighted lines of code.
      - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
      {
          if (_persistentStoreCoordinator != nil) {
              return _persistentStoreCoordinator;
          }
      
          NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataMigration.sqlite"];
      
          NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:
                                   [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                   [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
      
          NSError *error = nil;
          _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
          if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
      

 

If your changes cannot be handled by lightweight migration you need to do manual mapping.

source: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html

NSNotificationCenter posting and listening with data

Great way to pass data around but its not recommended for navigation, use -(void)prepareForSegue for that since object might not have had setup the listener.

store notification keys in constants to avoid typos

// notification keys
static NSString* const NOTIFICATION_NAME = @"NOTIFICATION_NAME";
static NSString* const NOTIFICATION_DICTIONARY_KEY_SOMETHING = @"NOTIFICATION_DICTIONARY_KEY_SOMETHING";
static NSString* const NOTIFICATION_DICTIONARY_KEY_SOMETHINGELSE = @"NOTIFICATION_DICTIONARY_KEY_SOMETHINGELSE";

post notification

    // dictionalry with data for posting
    NSDictionary* userInfo = @{ NOTIFICATION_DICTIONARY_KEY_SOMETHING : @"someValue",
                                NOTIFICATION_DICTIONARY_KEY_SOMETHINGELSE : @"someOtherValue" };

    // post the notification
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_NAME
                                                        object:self // whos posting
                                                      userInfo:userInfo];

listener for notification and reading data (setup early in life cycle -(void)awakeFromNib

    // listening for notifications
    [[NSNotificationCenter defaultCenter] addObserverForName:NOTIFICATION_NAME // listen for this notification name
                                                      object:nil // any object can send it
                                                       queue:nil // any queue can send it
                                                  usingBlock:^(NSNotification *note) {

                                                      note.name; // name passed
                                                      note.object; // object sending notification.  Use introspection to find out what object it is
                                                      note.userInfo; // notification specific information

                                                      // retrieve data from dictionary sent
                                                      NSString* value = note.userInfo[NOTIFICATION_DICTIONARY_KEY_SOMETHING];
                                                      NSString* anotherValue = note.userInfo[NOTIFICATION_DICTIONARY_KEY_SOMETHINGELSE];
                                                      NSLog(@"notification values %@ %@", value, anotherValue);
    }];

stop listening for notifications like in -(void)viewWillDisappear. Failure to remove will cause crashes when MVC goes off screen or dealloc.

    [[NSNotificationCenter defaultCenter] removeObserver:self];
    // or
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NOTIFICATION_NAME object:nil];

HMLadderViews utility

Ladder Views is meant to animate UIViews already on a view based on the windows size.  Can navigate next/previous with method calls or by buttons added by the controller at the top and bottom for convenience.

features:

  • top and bottom navigation buttons
  • swipe navigation
  • shrink views to percentage
  • bounce current view if at limit
  • animate with specific duration
  • top and bottom offset (gray line in example animation)
  • fade views away from center
  • block when index is changing

2014-09-09_13-03-56

Usage

  • create several views for the animation
  • files needed for import
    HMBasicAnimation.h / .m
    HMLadderViews.h / .m
    
  • import into your view controller
    #import "HMLadderViews.h"
    
  • initialize ladder with lazy instantiation and set first view in viewDidLayoutSubviews (controller will check to make sure it happens once)
    @property (strong, nonatomic) HMLadderViews* ladder;
    
    ...
    
    - (HMLadderViews*)ladder
    {
        if (!_ladder) {
            // view orders matters how they are displayed
            NSArray* views = @[ self.ladderView5,
                                self.ladderView4,
                                self.ladderView3,
                                self.ladderView2,
                                self.ladderView1 ];
            _ladder = [[HMLadderViews alloc] initWithViews:views
                                                 andHolder:self.view
                                              andTopOffset:50
                                           andBottomOffset:50
                                           useTopNavButton:YES
                                        useBottomNavButton:YES
                                      fadeViewsOutOfCenter:YES
                                    useSwipesForNavigation:YES
                                         maxEdgeViewHeight:50];
    
            // optional
            [_ladder currentIndexChangedBlock:^(int index) {
                NSLog(@"ladder current index changed to %d", index);
            }];
        }
        return _ladder;
    }
    
  • set the initial view in viewDidLayoutSubviews with either
    • HMLADDER_VIEW_ANIMATED_TO_CENTER_FIRST for the first to be animated in
    • HMLADDER_VIEW_ANIMATED_TO_CENTER_LAST for the last to be animated in
    • or any specific index to snap immediately to it
  • -(void)viewDidLayoutSubviews
    {
        [self.view layoutIfNeeded];
        [self.ladder initializeOnceToViewIndex:HMLADDER_VIEW_ANIMATED_TO_CENTER_FIRST];
    }
    
  • manually call previous or next
    [self.ladder showPrevious];
    [self.ladder showNext];
    
  • if you’re having issues with the views sizes changing when navigating to other views
    • disable auto layout (if it wont break anything in your project)
      2014-09-09_16-57-09
    • OR create constraints on all of the views for width and height
      • select all the views meant for the animation
        2014-09-09_16-42-35
    • set width and height constraints
      2014-09-09_16-43-41
    • you should see these constraints added to each view
      2014-09-09_16-49-16
    • on missing constraints warning 2014-09-09_16-44-50 add the missing constraints
      2014-09-09_16-45-25

CocoaPods using and creating your own

If youre already reading this you probably know what cocoapods are and why they are essential for iOS development.. so lets get to it.

more: http://guides.cocoapods.org/making/making-a-cocoapod.html

 

Installing / Updating

  • $ sudo gem install cocoapods
    • will prompt for your computers password
    • be patient…
  • if updating you might have to run
    • $ pod repo remove master // fixes an error of fast-forward
  • $ pod setup

 

Adding pods to my projects

  • cd to your projects folder (where .xcodeproj is located)
  • $ pod init // will create a podfile (http://guides.cocoapods.org/using/the-podfile.html)
  • open the podfile in your editor and add the libraries you desire
    • platform :ios, '7.0'
      pod 'AFNetworking', '~> 2.2'
      // for AFNetworking version 2.2
    • pod 'ABC', :git => 'https://github.com/HelloMihai/ABC.git', :commit => 'b2g4y5fe4' // add a library thats not public for a specific commit
    • pod 'HMDevKitObjc/Core/CoreA'// add a specific Subspec and not the entire library
  • close Xcode if open before installing libraries
  • $ pod install // install the libraries added to this project
  • open <ProjectName>.xcworkspace // if you open the .xcodeproj your libraries wont be seen
  • import a file with <name.h> not “name.h”
    #import <HMBasicAnimation.h>
    

 

 

Updating project libraries

  • cd to your projects folder (where .xcodeproj is located)
  • $ pod update

 

Creating your own pods

  • create a single gitHub repository for all your libraries to link to one cocoaPod // eg: https://github.com/HelloMihai/HMDevKitObjc
  • create two folders
    • Classes // add your subfolders with classes there eg: Classes/HMExample/HMExample.h and .m
    • Exclude // excluded from being pulled
  • add the LICENCE file of your choice
  • $ pod spec create HMDevKitObjc | https://github.com/HelloMihai/HMDevKitObjc.git // creates a base podspec file
  • edit the podspec file with your desired email, name etc and note the tag “0.0.1”
  • $ git init // init your git repository 
  • $ git add -A // add all files 
  • $ git commit -m "initial commit" // commit files 
  • $ git remote add origin http://github.com/HelloMihai/HMDevKitObjc.git // link to remote repo 
  • $ git push -u origin master // push changes to server 
  • $ git tag 0.0.1 // tag the current repository
  • $ git push --tags // push tags to the server
  • $ pod spec lint HMDevKitObjc.podspec // run to ensure this is a valid spec and to remove all comments including this before submitting the spec.  Should have no errors
  • $ pod lib lint // test syntax of pod file.  Should have no errors
  • end result for the file should be something similar to this
    Pod::Spec.new do |s|
      s.name          = "HMDevKitObjc"
      s.version       = "0.0.1"
      s.summary       = "ObjectiveC libraries for ios development"
      s.homepage      = "http://HelloMihai.wordpress.com"
      s.license       = { :type => 'MIT', :file => 'LICENSE' }
      s.author        = "HelloMihai"
      s.platform      = :ios, "7.0"
      s.source        = { :git => "https://github.com/HelloMihai/HMDevKitObjc.git", :tag => "0.0.1" }
      s.source_files  = "Classes", "Classes/**/*.{h,m}"
      s.exclude_files = "Classes/Exclude"
      s.requires_arc  = true
    end
    
  • push your changes again to the server
  • test your new pod
    • create a new project and init cocoapods for it
    • $ pod init // initialize cocoapods
    • edit the podfile for the test project
      platform :ios, "7.0"
      pod 'HMDevKitObjc', :git => 'https://github.com/HelloMihai/HMDevKitObjc.git'
      
    • close Xcode if open before installing libraries
    • $ pod install // install the new pod
    • open the .xcwordspace project and you should now see the following pod files
      2014-09-03_13-57-23
    • import a file with <name.h> not “name.h”
      #import <HMBasicAnimation.h>
      

 

 

Pushing to Public

  • make sure you have the latest version of CocoaPods (see install/update section above)
  • $ pod trunk register YourName@Email.com 'Your Name' --description='computer description'
  • wait for email and click the link
  • $ pod trunk me // see your current session
  • $ pod trunk push // finally push your trunk to cocoapods for public access

 

 

 

Users then can add your cocoaPod by adding to their projects podfile

pod 'HMDevKitObjc'

constants properties readonly macros define

I like to create a class that holds all my constants and common imports so I only import in my subclasses one reference and manage it all there. Its easier to manage storyboard names and macros.

// HMCommon.h

#ifndef HealthLog_Header_h
#define HealthLog_Header_h

#pragma mark : include all utilities
#import "HMBasicAnimation.h"
#import "HMGrandCentralDispatch.h"

#pragma mark : view xibs
static NSString* const XIB_NAME = @"MyXIBName";

#pragma mark : segues
static NSString* const SEGUE_TO_TODAY_MAIN = @"segueToMain";

#pragma mark : view identifiers
static NSString* const IDENTIFIER_VIEW_MAIN = @"MainViewController";

#pragma mark : core data
static NSString* const CORE_DATA_ITEM_XXX_NAME = @"XXXX";

#pragma mark : navigation view controller
static BOOL const HIDE_NAVIGATION_VIEW_CONTROLLER_BAR =  YES;

#pragma mark : table cells
static NSString* const SOME_CELL = @"SomeCellName";

#pragma macros
/** convert colors from hex to UIColor */
#define UIColorFromHex(hex) [UIColor colorWithRed:((float)((hex & 0xFF0000) >> 16))/255.0 green:((float)((hex & 0xFF00) >> 8))/255.0 blue:((float)(hex & 0xFF))/255.0 alpha:1.0]

#pragma mark : fonts
#define SomeFont [UIFont fontWithName:@"HelveticaNeue-Bold" size:14]

#pragma mark : colors

#endif
  • readOnly property (for example in the .h for public readOnly)
    @property (nonatomic, readonly) NSInteger myValue;
    
  • change a readonly property to readwrite, redeclare it in the .m
    @interface MyClass()
    @property (nonatomic, readwrite) NSInteger myValue;
    @end
    
  • constants – local constants just remove static (need to import class where constants are declared)
    • where to declare them? declare them outside of @interface and @implementation
      @interface
      ...
      @end
      
      // declare here
      
      @implementation
      ...
      @end
      
    • #define
      #define MY_INTEGER 2
      #define MY_STRING @"yay"
      
    • my preferred way
      static const int MY_INTEGER = 2;
      static NSString * const MY_STRING = @"yay";
      
  • defining macros (color example)
    /** convert colors from hex to UIColor */
    #define UIColorFromHex(hex) [UIColor colorWithRed:((float)((hex & 0xFF0000) >> 16))/255.0 green:((float)((hex & 0xFF00) >> 8))/255.0 blue:((float)(hex & 0xFF))/255.0 alpha:1.0]
    
    /** usage */
    #define MyFontColor UIColorFromHex(0xFFFFFF)
    

HMBasicAnimation utility

Background

I needed a wrapper to clean up my animation code.  I prefer this to the UIView animateWithDuration because i can run multiple animations and control time and delay of each one independently.  I also don’t like how most people use CABasicAnimation.

A common issue is that animations changes the Presentation Layer and not the Model Layer.  So button taps for example will be captured where the model layer is not where the button animated to. This utility updates both seamlessly so you have to worry about leaving animations on the stack with:

myAnimation.removeOnCompletion = NO;
myAnimation.fillMode = kCAFillModeForwards;

Instead, set the values to the model (updating the UI) and do the animation From the original value to the new value.

    id currentValue             = [layer valueForKeyPath:keyPath];
    CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:keyPath];
    animation.fromValue         = currentValue;
    animation.toValue           = toValue;
    animation.duration          = duration;
    [layer addAnimation:animation forKey:keyPath]; // start animation

    [layer setValue:toValue forKeyPath:keyPath]; // update the model so the animation doesn't seem to dissapear

If you have multiple animations its easier to group all of them into an animation group if they share similar triggers

    CAAnimationGroup* group = [CAAnimationGroup animation];
    group.animations = @[myAnimation1, myAnimation2, myAnimation3];
    group.duration = 1.5;

    [layer addAnimation:group forKey:@"animations"];

A note, if your view is added as a child of UIWindow there will be cropping issues before the animation ends for “transform.rotation.x / y”.  If that is an issue just make sure its added to a UIView thats on the window thats not animating (this was a fun little issue to debug)

 

The Utility

To make all of this easier use the HMBasicAnimation utility
source: https://github.com/HelloMihai/HMDevKitObjc/tree/master/Classes/BasicAnimation
or use my CocoaPods : pod 'HMDevKitObjc'

Usage:

  • import
    #import "HMBasicAnimation.h";
    
  • 3D rotate a UIView by degrees
    [HMBasicAnimation rotate3D:myView.layer
                 toDegreeAngle:45
                      duration:1.5
                  delaySeconds:0.5
                       keyPath:HMBasicAnimation_ROTATION_Y];
    
  • to change the X coordinate relatively by 50 points
    [HMBasicAnimation animate:myView.layer
                      toValue:@50 // @ to be an number object
                     duration:1.5
                 delaySeconds:0
                      keyPath:HMBasicAnimation_TRANSLATION_X];
    
  • to change to an absolute Y coordinate of 100 points
    [HMBasicAnimation animate:myView.layer
                      toValue:@100 // @ to be an number object
                     duration:1.5
                 delaySeconds:0
                      keyPath:HMBasicAnimation_POSITION_Y];
    
  • to chain several animations play with the delay and duration values
    [HMBasicAnimation animate:myView.layer toValue:@100 duration:1.0 delaySeconds:0 keyPath:HMBasicAnimation_POSITION_Y];
    [HMBasicAnimation animate:myView.layer toValue:@150 duration:1.0 delaySeconds:1 keyPath:HMBasicAnimation_POSITION_Y];
    
  • rotate with easing
    [HMBasicAnimation rotate3D:myView.layer toDegreeAngle:50 duration:3 delaySeconds:1 keyPath:HMBasicAnimation_ROTATION_Y withEase:HMBasicAnimation_EASING_EASE_IN];
    
  • other properties that can be manipulated (add your own)
    NOTE: translation is relative the current x,y,z value and not an absolute movement

    HMBasicAnimation_OPACITY
    HMBasicAnimation_ROTATION
    HMBasicAnimation_ROTATION_Y
    HMBasicAnimation_ROTATION_X
    HMBasicAnimation_ROTATION_Z
    HMBasicAnimation_SCALE_X
    HMBasicAnimation_SCALE_Y
    HMBasicAnimation_SCALE_Z
    HMBasicAnimation_TRANSLATION_X // relative change from current x
    HMBasicAnimation_TRANSLATION_Y // relative change from current y
    HMBasicAnimation_TRANSLATION_Z // relative change from current z
    HMBasicAnimation_POSITION_X // center is registration point
    HMBasicAnimation_POSITION_Y // center is registration point
    
  • other easing methods to use
    HMBasicAnimation_EASING_NONE
    HMBasicAnimation_EASING_LINEAR
    HMBasicAnimation_EASING_EASE_IN
    HMBasicAnimation_EASING_EASE_OUT
    HMBasicAnimation_EASING_EASE_IN_OUT
    HMBasicAnimation_EASING_DEFAULT
    

HMGrandCentralDispatch utility

I needed a wrapper for GCD so that code is cleaner and easier to manage.  The result is

HMGrandCentralDispatch

example and source: https://github.com/HelloMihai/HMSideDrawerDirectional
or use my CocoaPods : pod 'HMDevKitObjc'

Usage:

  • import
    #import "HMGrandCentralDispatch.h"
    
  • calling method on delay on main queue
    [HMGrandCentralDispatch delayCallbackOnMainQueueWithDelay:delaySeconds block:^{
            // call methods here
        }];
    
  • calling methods on delay on a specific thread
    [HMGrandCentralDispatch delayCallbackDelay:delaySeconds onQueue:myQueue block:^{
            // my code
        }];
    

HMSlideInView for custom UIView as an inputView replacement

create a custom view to slide in and out from the bottom.  This can be expanded to slide in from any side of the window.

notes.  The view is added to the UIWindow so it works with any viewControllers view, even scroll views etc.

 

gitHub source : https://github.com/HelloMihai/HMSlideInView

2014-08-26_16-02-04

to create your own:

  • import the files into your project
    • HMSlideInView.h and HMSlideInView.m
  • in the desired view controller, say “FirstViewCroller” for this source code example,
    • add a UIView
    • size as desired
      • save the height of the view in mind, in this case 232
        2014-08-26_15-53-27
    • fill it with elements
    • create a STRONG IBOutlet in the viewController (by default its weak)
      • @property (strong, nonatomic) IBOutlet UIView *popupView;
        
  • on view load hide the popup until the user asks for it
    • in FirstViewController.m
      • import the class
        #import "HMSlideInView.h"
        
      • find the method <code>- (void)viewDidLoad</code> and add
        - (void)viewDidLoad
        {
            [super viewDidLoad];
            // Do any additional setup after loading the view.
        
            [HMSlideInView hideAtBottomView:self.popupView viewHeight:232 withAnimationDuration:0]; // hide the view instantly initially.  Note animationDuration 0
        }
        
      • override the method <code>- (void)viewWillDisappear:(BOOL)animated</code> to hide the view when the view controller hides
        - (void)viewWillDisappear:(BOOL)animated
        {
            [super viewWillDisappear:animated];
            [HMSlideInView hideAtBottomView:self.popupView viewHeight:POPVIEW_HEIGHT withAnimationDuration:0];
        }
        
      • add a show button to your storyboard view and an tap IBAction to show the view
        - (IBAction)popupShowTapped:(id)sender
        {
            [HMSlideInView showAtBottomView:self.popupView viewHeight:232 withAnimationDuration:0.5 onViewController:self];
        }
        
      • add a hide button to your storyboard view and a tap IBAction method to hide the view
        - (IBAction)popupHideTapped:(id)sender
        {
            [HMSlideInView hideAtBottomView:self.popupView viewHeight:232 withAnimationDuration:0.5];
        }
        

HMUIButtonOnTouch block extension for your UIButton touchUpInside

I wanted a block callback for UIButtons that can be more readable so I created HMUIButtonOnTouch.

source: https://github.com/HelloMihai/HMUIButtonOnTouch
or use my CocoaPods : pod 'HMDevKitObjc'

[self.myButton onTouchInside:^{
     NSLog(@"button tap");
}];

cant get easier than that

remember to destroy it when done and refer to the retain cycle post to avoid memory leaks

[self.myButton onTouchInside:nil];

objc blocks

I love blocks but keep an eye out for the memory retain cycle.

Usage cheat sheet:

property: (need to be copy)

@property (nonatomic, copy) returnType (^blockName)(parameterTypes);

as a local variable:

returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};

as a method parameter:

- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;

as an argument to a method call:

[someObject someMethodThatTakesABlock: ^returnType (parameters) {...}];

as a typedef:

typedef returnType (^TypeName)(par ameterTypes);
TypeName blockName = ^returnType(parameters) {...};

invoking a block:

blockName();

example:

(local block declaration, passing to a method and executing the block)
2014-08-22_15-06-50

readonly issue

local variables declared outside the block can used inside the block but are read only
2014-08-22_15-22-56

to make it read and write use __block, strong pointer is created and stays until block goes out of scope.
2014-08-22_15-26-58

 

 

objc retain cycle

I love blocks but they can easily lead a developer into a retain cycle where objects arent released.  Objects referenced inside a block will stay in the heap as long as the block does.  Block keeps a strong pointer to the objects.  Every time the block is copied the objects ref count increases leading to a memory leak.  Essentially A holds onto B and B onto A.  Lets see some simple ways to fix this.

This block calls self causing a retain cycle when the block is passed around.

-(void) classMethod
{
    void (^myBlock)() = ^{
        [self myMethodInClassCalledFromBlock]; // retain cycle causing memory leak
    };

    [self.objectUsesBlock useBlock:myBlock]; // send block out in the world
}

-(void) myMethodInClassCalledFromBlock
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

instead use Lifetime Qualifiers to specify weak to break the cycle transitioning INTO the block. Inside the block, create a strong reference to the object so it doesn’t become nil inside that block if the block is complex enough (want a debugging headache do yah??). The blocks strongLocalSelf reference will be collected at the end of the block automatically.

-(void) classMethod
{
    __weak __typeof(self) weakSelf = self; // weak version
    void (^myBlock)() = ^{
        __strong __typeof(weakSelf) strongLocalSelf = weakSelf; // strong instance to not be garbage collected
        [strongLocalSelf myMethodInClassCalledFromBlock]; // use the local strong version
    };

    [self.objectUsesBlock useBlock:myBlock]; // send block out in the world
}

-(void) myMethodInClassCalledFromBlock
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

This applies to any object that is used within a block. Just redeclare a new instance with __weak outside the block. In the bloc declare a __strong instance and use that. Easy peasy!

If you think thats messy and want a more elegant way to do your dance there is a way! It does the same thing but prettier.
Macros are provided by libextobjc. Available in CocoaPods libextobjc or if you just want the weakify then add libextobjc/EXTScope.

#import <EXTScope.h>

..

(void) classMethod
{
    @weakify(self) // weak version to break the cycle
    void (^myBlock)() = ^{
        @strongify(self) // strong instance to not be garbage collected
        [self myMethodInClassCalledFromBlock]; // use the local strong version
    };

    [self.objectUsesBlock useBlock:myBlock]; // send block out in the world
}

-(void) myMethodInClassCalledFromBlock
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

-(void)myMethodInClass
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

Good Apple read:
Transitioning to ARC Release Notes

Unit testing in xcode

Test driven development is a great way to make sure that code changes in the future doesn’t break your build.

Lets look at the default failing test that comes with a new project.  Create a new test and class that fail and then fix it.

  1. create a new project named UnitTestingExample
  2. run the test that came with the new project
    2014-08-21_22-02-55

    1. Note that the test will fail.  This is intentional because no tests have been customized to do anything.
    2. if you open the file “UnitTestingExampleTests” (shortcut Command+Shift+o)
      2014-08-21_15-44-58
    3. you will notice the intended failed test on the testExample method
      2014-08-21_15-46-31
  3. lets create a test
    1. first lets create a class for us to test results on
      1. create a new Objective-C class named “IdClassForTesting”
        2014-08-21_23-08-59
      2. update the .h header interface
        @interface IdClassForTesting : NSObject
        
        @property (nonatomic) NSUInteger userId;
        
        -(void)calculationAffectingUserId;
        
        @end
        
      3. update the .m implementation @implemetation for a test to fail
        @implementation IdClassForTesting
        
        -(void)calculationAffectingUserId
        {
            self.userId++; // increment the user id for the test to fail
        }
        
        @end
        
    2. select the tests folder, create a new Objective-C test case class named “IdTest” extending XCTestCase
      2014-08-21_22-18-08
    3. in IdTest.m import the class were testing
      #import "IdClassForTesting.h"
      
    4. replace the method – (void)testExample and replace with:
      - (void)testUserId
      {
          IdClassForTesting* testee = [[IdClassForTesting alloc] init]; // instantiate a new object
          NSUInteger userId = 1234; // store the value for comparison
          testee.userId = userId; // set the desired value
          [testee calculationAffectingUserId]; // call the methods we need to test
          XCTAssertEqual(userId, testee.userId, @"expected equal"); // test our value with the value in the class
      }
      
    5. Note: you can create as many test methods -(void) testSomeName
    6. run the new test.  Can run the single test or rerun all by selecting the topmost >  2014-08-21_16-11-13
      2014-08-21_23-47-52

      1. this test failed since we changed the userId in the class but expected the values to be equal.
        2014-08-21_16-26-04
      2. lets fix this by not changing the userId
        @implementation IdClassForTesting
        
        -(void)calculationAffectingUserId
        {
            self.userId; // NOT changing the user id for the test to pass
        }
        
        @end
        
      3. rerun the test and now it will pass

There is more to Unit testing such as cleaning up in the – (void)tearDown and much much more but this should be enough to get you started.

useful methods for comparison found in XCTestAssertions.h

XCTAssert(expression, format...)
XCTAssertEqual(a1, a2, format...)
XCTAssertEqualObjects(a1, a2, format...)
XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)
XCTAssertFalse(expression, format...)
XCTAssertNotEqual(a1, a2, format...)
XCTAssertNotEqualObjects(a1, a2, format...)
XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...)
XCTAssertNoThrow(expression, format...)
XCTAssertNoThrowSpecific(expression, specificException, format...)
XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)
XCTAssertNotNil(a1, format...)
XCTAssertThrows(expression, format...)
XCTAssertThrowsSpecific(expression, specificException, format...)
XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)
XCTAssertTrue(expression, format...)
XCTFail

download the GitHub source

HMSideDrawerDirectional side drawer or basement for the hamburger menu

Call it what you want; side menu, navigation drawer, side drawer, basement, hamburger menu…

I wanted a static class that worked well for left and right with a view controller from the main storyboard.  The result is HMSideDrawerDirectional.

source and example:

https://github.com/HelloMihai/HMSideDrawerDirectional

HMSideDrawerDirectionalExample3D      example      HMSideDrawerDirectionalExampleNoShrink      HMSideDrawerDirectionalExampleLeft

protocols aka interfaces

Great for returning data from a presented view controller to a presenter or anything else.

  1. To define the delegate, in the desired header .h file say for example “MyClass”
    1. Just under the includes specify the optional and required methods:
      @protocol MyClassDelegate <NSObject>
      
      @required
      -(void)delegateRequiredMethodA:(NSString*)myString;
      -(void)delegateRequiredMethodB;
      
      @optional
      -(void)delegateOptionalMethodA:(NSString*)myString andValue:(NSString*)value;
      -(void)delegateOptionalMethodB;
      
      @end
      
    2. In the header @interface add an assign delegate property
      @property (nonatomic, assign) id <MyClassDelegate> delegate;
      
    3. in the implementation file, call the delegate methods in the desired places.  Note: make sure you call all the methods in the protocol.
      -(void) myMethodExample
      {
          [self.delegate delegateRequiredMethodA:@"my string"];
      }
      
  2. In the desired implementing class, say ViewController
    1. import the class where the protocol is specified, for this example MyClass.h
      #import "MyClass.h"
      
    2. Indicate that this class conforms to the protocol by appending to the @interface line in either the header or implementation as needed
      1. if in the .h header
        @interface ViewController () <MyClassDelegate>
        
      2. or if in the .m implementation file
        @interface ViewController : UIViewController <MyClassDelegate>
        
      3. NOTE: you can conform to multiple delegates separating by comma
        @interface ViewController : UIViewController <ADelegate, BDelegate>
        
    3. in the implementation .m file create the required methods and any of the optional as desired
      
      #pragma mark : my delegate
      
      -(void)delegateRequiredMethodA:(NSString*)myString;
      {
          NSLog(@"delegate required A called");
      }
      
      -(void)delegateRequiredMethodB;
      {
          NSLog(@"delegate required B called");
      }
      
      -(void)delegateOptionalMethodB;
      {
          NSLog(@"delegate optional B called");
      }
      
    4. set the delegate instance to receive the calls, when the MyClass object is instantiated or in viewDidLoad.  For Example in your ViewController.m
      - (void)viewDidLoad
      {
          [super viewDidLoad];
          // Do any additional setup after loading the view, typically from a nib.
          MyClass* myClass = [[MyClass alloc] init]; // instantiate the class if needed
          myClass.delegate = self; // set self as delegate to receive the calls
      }
      

extend your classes with associated objects

say you’d like all your classes that extend UIViewController to have a new dynamic property but defined in one class.

  1. create a new class named “UIViewControllerExtension” extending NSObject
    2014-08-19_17-46-37
  2. in “UIViewControllerExtension.h”
    1. import runtime.h
      #import <objc/runtime.h>
      
    2. add a new interface just under the #imports (or anywhere in parallel to the @interface)
      @interface UIViewController (UIViewControllerExtension) // extends all that implement UIViewController
      @property (nonatomic, weak) NSString* testProperty;
      @end
      
  3. in “UIViewControllerExtension.m” add an @implementation just under the #imports (or anywhere in parallel to the @implementation)
    @implementation UIViewController (UIViewControllerExtension)
    
    // setter method
    -(void)setTestProperty:(NSString*)value
    {
        // sets the string to the current class (at runtime) this is associated with
        objc_setAssociatedObject(self, @selector(testProperty), value, OBJC_ASSOCIATION_ASSIGN);
    }
    
    // getter method
    -(NSString*) testProperty
    {
        // retrieves the associated value
        NSString* value = objc_getAssociatedObject(self, @selector(testProperty));
        if (!value) {
            value = self.parentViewController.testProperty;
        }
        return value;
    }
    
    @end
    
  4. in any class that extends UIViewController (if a blank new project ViewController.m)
    1. include the extension header “UIViewControllerExtension.h”
      #import "UIViewControllerExtension.h"
      
    2. the new property will now be available for use.
      -(void) myMethod
      {
          NSLog(@"val = %@", self.testProperty); // prints out "val = test property"
      }
      
  5. the property will be null until its set, usually by an external object.  For view controllers I normally capture them in the prepareForSegue:sender: method in the presenting view controller

objc properties with accessors (getters, setters)

Quite surprised at how many fairly recent projects I open up and still see this done incorrectly.

 

usage of a public property, local private property with accessors.
note: can create accessors for the public property as well.  Adding a setter will require adding @synthesize.
2014-08-19_13-10-51

 

usage of a local private property with only a getter not requiring @synthesize.
2014-08-19_13-16-59

objc Xcode custom to segue and unwind for specific animations

In short, you need 2 custom classes for segue in and unwind segues.  You’ll also need to modify the presenting and presented view controller to trigger and manipulate the segues.  You learn to pick which segue to use from the storyboard and how to select the unwind segue programmatically.  There are many animations you can do with the views so go nuts.

Its also possible to create properties on the segues header classes to set properties such as the center point for custom animations.

  1. add a button in the presenting view controller
    2014-08-18_16-32-58
  2. create a new scene to segue to
    1. create a new view controller class named “MyViewController”
      2014-08-22_00-00-02
    2. drag out a view controller and link it to the new class MyViewController
      2014-08-22_00-07-10
  3. add a button in the new view controller for unwinding
    2014-08-18_16-36-03
  4. create a new class extending UIStoryBoardSegue
    2014-08-18_16-42-02
  5. customize the segue
    1. in MyCustomSegue.m implement the -(void)perform method. Where animations can be setup
      - (void)perform
      {
          UIView* sourceView = ((UIViewController*)self.sourceViewController).view; // from view controller
          UIView* destinationView = ((UIViewController*)self.destinationViewController).view; // to view controller
      
          UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
          // set the starting center of the controller
          destinationView.center = CGPointMake(sourceView.center.x + sourceView.frame.size.width, destinationView.center.y);
          [window insertSubview:destinationView aboveSubview:sourceView];
      
          [UIView animateWithDuration:1.5
              animations:^{
                  // custize animations
                  destinationView.center = CGPointMake(sourceView.center.x, destinationView.center.y);
                  sourceView.center = CGPointMake(0 - sourceView.center.x, destinationView.center.y);
              }
              completion:^(BOOL finished) {
                  // when the animation is completed
                  [[self sourceViewController] presentViewController:[self destinationViewController] animated:NO completion:nil];
              }];
      }
      
  6. set the custom segue
    1. control drag from the presenting view controller segue button to the new view controller and select MyCustomSegue
      2014-08-22_00-15-06
  7. unwinding from a custom segue
    1. in the presenting view controller.m (controller where the “Custom Segue” button lies) add a method with one parameter UIStoryboardSegue* to be called when exiting the second scene.
      -(IBAction)unwindFromCustomSegue:(UIStoryboardSegue*)sender
      {
          NSLog(@&quot;unwound from custom segue controller&quot;);
      }
      
    2. control drag from the view controller to the exit of the scene.  (not doing from the button since we want to trigger it programmatically and might be linked to UI controls not yet in the view.)
      and select the IBAction method we wrote in the presenting view controller “unwindFromCustomSegue”
      2014-08-22_00-17-59
    3. select the unwind segue from the document outline and set a unique identifier “CustomSegueUnwindIdentifier”
      2014-08-22_00-20-54
    4. create a new class extending UIStoryBoardSegue for the unwind named “CustomUnwindSegue”
      2014-08-18_17-36-25
    5. in CustomUnwindSegue.m implement the same as before -(void)perform method bit with “dismissViewControllerAnimated” instead. Where animations can be setup.
      - (void)perform
      {
          UIViewController *sourceViewController = (UIViewController *)self.sourceViewController;
          UIViewController *destinationViewController = (UIViewController *)self.destinationViewController;
      
          [UIView transitionWithView:sourceViewController.view
                            duration:1.5
                             options:UIViewAnimationOptionTransitionFlipFromTop
                          animations:^{
                              [sourceViewController.view addSubview:destinationViewController.view];
                          }
                          completion:^(BOOL finished){
                              [sourceViewController dismissViewControllerAnimated:NO completion:nil];
                          }];
      }
      
    6. in the presenting view controller ViewController.m over-ride the segueForUnwindingToViewController method.
      1. import the segue
        #import &quot;CustomUnwindSegue.h&quot;
        
      2. over-riding the segueForUnwindingToViewController method to specify which unwind segue to use
        - (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController
                                              fromViewController:(UIViewController *)fromViewController
                                                      identifier:(NSString *)identifier {
        
            if ([identifier isEqualToString:@&quot;CustomSegueUnwindIdentifier&quot;]) {
                CustomUnwindSegue *segue = [[CustomUnwindSegue alloc]
                                              initWithIdentifier:identifier
                                              source:fromViewController
                                              destination:toViewController];
                return segue;
            }
        
            return [super segueForUnwindingToViewController:toViewController
                                         fromViewController:fromViewController
                                                 identifier:identifier];
        }
        
    7. create an IBAction from the button triggering the call
      1. select the second scene, change to split view and select the MyViewController.m class
        2014-08-22_00-26-37
      2. control drag a tap action from the unwind button and name it “unwindTapped”
        2014-08-22_00-30-41
      3. unwind from the new method
        - (IBAction)unwindTapped:(id)sender
        {
            [self performSegueWithIdentifier:@&quot;CustomSegueUnwindIdentifier&quot; sender:self];
        }
        

final product

2014-08-22_00-36-26

download GitHub source example

AlamoFire, swift upgrade for AFNetworking

2014-08-18_12-27-42

its not a port but  a complete rebuild!
Prereqs: Xcode 6

https://github.com/Alamofire/Alamofire

Get Request

Alamofire.request(.GET, "http://httpbin.org/get")

With Parameters

Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])

With Response Handling

Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
         .response { (request, response, data, error) in
                     println(request)
                     println(response)
                     println(error)
                   }

With Response String Handling

Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
         .responseString { (request, response, string, error) in
                  println(string)
         }

[mac] alfred snippets for xcode

to use by shortcut : command+option+C

2014-08-15_16-21-20

to edit snippets in Alfred (Features, Clipboard, Snippets)

2014-08-15_15-49-04

Tip: to produce the Xcode auto tab to editing 2014-08-15_15-54-16 use <#anyNameHere#> tag

annoyingly there is no way to control where the cursor is placed after the snippet is pasted so I use Shift+Tab to cycle back through.


name: xcode function
snippet:

-(<#return type#>) <#function name#>
{
    <#statements#>
}

name: xcode property
snippet:

@property (strong, nonatomic) <#type#>* <#instance name#>;

NSLog statement printing out the current method and class : [ClassName methodName:]
name: xcode nslog function
snippet:

NSLog(@"%s", __PRETTY_FUNCTION__);

name: xcode table delegate dataSource
snippet:

#pragma mark - Table view data source
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Potentially incomplete method implementation.
    // Return the number of sections.
    return 0;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete method implementation.
    // Return the number of rows in the section.
    return 0;
}
 
/*
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<#@"reuseIdentifier"#> forIndexPath:indexPath];
 
    // Configure the cell...
 
    return cell;
}
*/
 
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}
*/
 
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }
}
*/
 
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
 
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the item to be re-orderable.
    return YES;
}
*/

 


UIViewController with UITableView delegate and dataSource

Creating

  1. drag a new UIViewController on your storyboard (or use an existing one).
    1. create a class inheriting from UIViewController with any name such as “MyViewController”
      2014-08-13_14-12-28
    2. link new class to the scene
      2014-08-13_14-13-24
  2. drag a UITableViewController into your scene (view controller)
    1. drag out and position
      2014-08-13_14-14-37
    2. set constraints (recommended but optional)
      2014-08-13_14-15-50
  3. create an IBOutlet for the UITableView
    1. select split view
      2014-08-13_14-16-46
    2. select the UITableView in the storyboard
      2014-08-13_14-22-37
    3. make sure the correct file is selected
      2014-08-13_14-21-192014-08-13_14-21-59
    4. control drag to the interface for an outlet
      2014-08-13_14-23-25
    5. name it “tableView”
      2014-08-13_14-24-00
  4. set the controller as the table delegate and data source
    1. implement UITableViewDelegate and UITableViewDataSource
      2014-08-13_14-24-45
    2. set the delegate and data source
      2014-08-13_14-26-17
  5. implement the delegate and data source methods needed
#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Potentially incomplete method implementation.
    // Return the number of sections.
    return 0;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete method implementation.
    // Return the number of rows in the section.
    return 0;
}

/*
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<#@"reuseIdentifier"#> forIndexPath:indexPath];
 
    // Configure the cell...
 
    return cell;
}
*/

/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}
*/

/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }
}
*/

/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/

/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the item to be re-orderable.
    return YES;
}
*/

 

Example Implementation

  1. create a prototype cell for storing your data
    1. select the table in the storyboard
      2014-08-13_14-38-35
    2. increment the number of prototype cells
      2014-08-13_14-39-01
    3. give the new cell a unique name to be referenced later in code
      1. select the new cell
        2014-08-13_14-40-53
      2. give it a unique name “MyCellId”
        2014-08-13_14-41-47
    4. create a class for the new cell
      1. 2014-08-13_14-42-16
      2. MyCellTableViewCell which inherits from UITableViewCell
        2014-08-13_14-43-03
      3. select the cell in the storyboard
        2014-08-13_14-40-53
      4. set its class
        2014-08-13_14-44-50
    5. add a label to the new cell
      2014-08-13_14-39-51
    6. create an IBOutlet for the label
      1. select split view 2014-08-13_14-16-46
      2. select the UITableView in the storyboard
        2014-08-13_14-22-37
      3. make sure the correct header file is selected
        2014-08-13_14-21-192014-08-13_14-47-32
      4. control drag an outlet from the label into the header
        2014-08-13_14-48-29
      5. name it
        2014-08-13_14-49-07
  2. lets create a dummy data source and display it in our new table and cell
    1. navigate to MyViewController.m
    2. create a new tableData property
      2014-08-13_14-51-22
    3. set some values in viewDidLoad
      2014-08-13_14-53-26
    4. update numberOfSectionsInTableView to have 1 section
      2014-08-13_14-54-18
    5. update numberOfRowsInSection to have the same number of cells as our data source
      2014-08-13_14-54-45
    6. configure the cell when one is requested
      1. import the cell controller MyCellTableViewCell.h
        2014-08-13_14-56-43
      2. uncomment cellForRowAtIndexPath
        2014-08-13_14-56-18
      3. update the dequeue line to instantiate your cell not a generic one by setting the type and reusable cell id which we did before with the name “MyCellId”
        2014-08-13_14-59-07
      4. add a value to the label we created from our data source using indexPath.row to know which index in the array we need
        2014-08-13_15-00-36
  3. your project should look like this now
    1. 2014-08-13_15-01-32
  4. lets allow deletion of rows
    1. uncomment the method commitEditingStyle forRowAtIndexPath
      2014-08-13_15-02-46
    2. upon deletion you need to update the data source FIRST (delete the item from the data source) since the table will refresh and if the row count doesn’t decrease you will get and “signal SIGABRT” 2014-08-13_15-05-43 error
      2014-08-13_15-04-30
    3. swipe left and delete a row
      1. 2014-08-13_15-06-52

commonly used GIT commands

    • overwriting master with another detached branch
      • git checkout currentBranch  // move to target branch
      • git merge -s ours master  // merge using ours over master
      •  // vim will open for the commit message
      • git checkout master  // move to master
      • git merge currentBranch  // merge resolved changes into master
    • calling multiple commands by separating with ;
    • overwriting local with remote branch
      • git fetch origin  // get change updates (doesnt change anything)
      • git reset --hard origin/yourBranch // overwrite local changes with remote branch
    • change username and email
      • for a specific repository
        • git config user.name "YOUR NAME"
        • git config user.email "YOUR EMAIL"
      • for all repositories
        • git config --global user.name "YOUR NAME"
        • git config --global user.email "YOUR EMAIL"
    • made a commit but forgot a change… add this new change to the previous commit
      • git add -A
      • git commit --amend
    • committing
      • git add -A & git commit -a -m “comment”  // add all new files and commit with comment
      • git commit --amend -m “some other message” // change the last commit comment // DO NOT USER CANNOT PUSH TO REMOTE ANYMORE (non-fast-forward issue)
    • showing branches
      • git branch -a // all remote and local branches
      • git branch -r // all remote branches
    • commit new changes to a new branch
      • {edit files in current branch}
      • git checkout -b newBranch // creates a new branch
      • git add -A & git commit -a -m “comment” // commits new changes to new branch
    • commit errors
      • git push origin myBranch // produces error
        ! [rejected]        621MultiBroad -> 621MultiBroad (non-fast-forward)
        error: failed to push some refs to https://user@remoterepo.something/user/reponame.git
        To prevent you from losing history, non-fast-forward updates were rejected
        Merge the remote changes (e.g. ‘git pull’) before pushing again.  See the
        Note about fast-forwards’ section of ‘git push –help’ for details.

        1. git pull bitbucket branchName
        2. //fix merge conflicts
        3. commit changes
        4. push to branch
    • checking out new branches from a remote location
      • git remote show bitbucket
      • git fetch bitbucket // if the remote branch you want to checkout is under “New remote branches” and not “Tracked remote branches”
      • git checkout -b local-name bitbucket/<remote_branch_name>
    • log
      • git log -1 -p  // show code log for only the last commit
      • git log --grep=searchString  // show log for a searchString
      • git log --grep=searchString -p  // show log for a searchString with code
      • git log -1 --stat  // see last commited files
      • git diff --stat  // show which files have changed
      • gitk filepath/name  // visual show of all the changes to a file
    • branching view
      • git branch -a  // show all branches
    • branching and merging
      • git branch myBranch
      • git checkout myBranch
      • edit files
      • git add -A
      • git commit -a -m “comment”
      • git checkout master
      • git merge myBranch
      • if conflicts, go edit the files and add/commit
    • branching, remove branches that are local but have been deleted in the remote
      • git remote show bitbucket // display info on bitbucket
      • git remote prune bitbucket --dry-run // test run without making changes
      • git remote prune bitbucket  // if dry run was ok then execute
    • pushing
      • git push bitbucket master // push master branch to bitbucket remote
      • git push bitbucket :branchToDelete // deletes branch from bitbucklet
    • merging (installed difftool visual merger on osx)
      • git difftool file.m  // diff the local file.m against the checked-in version
      • git difftool some-feature-branch file.m // diff the local file.m against the version in some-feature-branch
      • git difftool Build-54..Build-55 file.m // diff the file.m from the Build-54 tag to the Build-55 tag
      • git mergetool  // To resolve merge conflicts
    • comments
      • git commit --amend -m “new comment” // change the last commits comment – will need to do pull and push again
    • git ignore
      • update .gitignore file with what to ignore
      • git rm -r --cached .  // remove all files currently tracked
      • git add . // add new files not ignored
      • git commit -am “removed ignored fileds” // commit without ignored files
    • git ignore
      • update .gitignore file with what to ignore
      • git rm -r --cached . // remove all files currently tracked
      • git add . // add new files not ignored
      • git commit -am “removed ignored fileds” // commit without ignored files
    • forked from a private repo and want to update yours to keep up with changes in the main
      • git remote add --track master mainRepoName git@git.corp.something/url  // add track
      • git checkout master  // make sure youre on master branch
      • git fetch mainRepoName  // fetch new changes
      • git merge mainRepoName/master  // merge their changes locally
    • act as if a file has not changed
      • git update-index --assume-unchaged file.md  // file wont be picked up by editors such as sourceTree when committing
    • deleting all git history
      • rm -rf .git // remove git folder and information
      • git init // create a new git repo
      • git add . // add all files
      • git commit -m "initial commit" // commit files
      • git remote add origin <git-uri> // set remote URI
      • git push -u --force origin master // push and replace whats on the server
      • or in one line
        • rm -rf .git; git init; git add .; git commit -m "Initial commit"; git remote add origin <github-uri>; git push -u --force origin master

git readme.md markdown basics

# heading 1 2015-07-24_18-56-01
## heading smaller 2015-07-24_18-56-17
### heading even smaller 2015-07-24_18-56-32

Will become a heading
==============
2015-07-24_18-56-51

Will become a sub heading
————–
2015-07-24_18-57-10

image and link
[![Build Status](http://www.yourdomain.com/image.jpg)%5D(http://www.yourdomain.com/yourLink.html)
[a link](https://github.com/user/repo/blob/branch/other_file.md)
[a relative link](other_file.md)
2015-07-24_18-57-30


line separator (3 dashes)
2015-07-24_18-57-49

– list item
– list item
– sub list item (2 spaces in front)
– sub list item (4 spaces in front)
2015-07-24_18-58-11

– [x] selected checkbox
– [ ] unselected checkbox
2015-07-24_18-58-28

**bold text**
*italic text*

Add an indentation and this will end up as code (4 spaces in front)

Table

header1    |     header3 |  header4
——-       |    ———:  |  :——:
left lighn  |  right align |  center
text          |   text          | text
2015-07-24_18-58-43

Passing data back from a presented UIViewController via delegate

create a prototype for the delegate in the sub view controller in the .h header
delegate should be WEAK typed!

#import "ViewController.h"
@class SubViewController; 
// need this since declared in protocol - error otherwise
@protocol SubViewControllerDelegate <NSObject> // delegate definition
// delegate method called form subview to implementing main view
-(void)passDataBackString:(NSString *)someString wihtInt:(int)someInt andController:(SubViewController *)viewController; 
@end

@interface SubViewController : ViewController
// delegate to be set as self from the main view
@property (weak, nonatomic) id<SubViewControllerDelegate> delegate; 
@end 

call the delegate method from the subview when its time to pass data back to the main controller

- (IBAction)backTapped:(id)sender{
    [self.delegate passDataBackString:@"yay" wihtInt:3 andController:self]; 
} 

implement protocol in main view controller – and import

@interface ViewController () <SubViewControllerDelegate> 
implement protocol method to receive the data
-(void)passDataBackString:(NSString *)someString wihtInt:(int)someInt andController:(SubViewController *)viewController
{
    NSLog(@"passed string back is %@", someString); 
} 

set main view controller as the delegate to get the call

subViewController.delegate = self; 

remove the delegate when done with the view controller

Image Masking with Animation – similar to twitters

import 3 images square, splashscreen, duck

-(void)viewDidLoad
{
    [super viewDidLoad];
     
    UIImageView *square = [[UIImageView alloc] initWithFrame:self.view.frame];
    square.image = [UIImage imageNamed:@"square"];
    [self.view addSubview:square];
     
    UIImageView *imageview = [[UIImageView alloc]initWithFrame:self.view.frame];
    imageview.image = [UIImage imageNamed:@"splashscreen"];
    [self.view insertSubview:imageview aboveSubview:square];
    [self.view addSubview:imageview];
     
    CALayer *mask = [CALayer layer];
    mask.contents = (id)[[UIImage imageNamed:@"duck"] CGImage];
    mask.bounds = CGRectMake(0, 0, 70, 70);
    mask.anchorPoint = CGPointMake(.5, .5);
    mask.position = CGPointMake(imageview.frame.size.width/2, imageview.frame.size.height/2);
    imageview.layer.mask = mask;
    imageview.layer.masksToBounds = YES;
     
    [CATransaction begin]; {
        [CATransaction setCompletionBlock:^{
            [square removeFromSuperview];
            [imageview removeFromSuperview];
                    }];
        CAKeyframeAnimation *keyframanimation = [CAKeyframeAnimation animationWithKeyPath:@"bounds"];
        keyframanimation.duration = 1;
        keyframanimation.timingFunctions =  [[NSArray alloc] initWithObjects:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], nil];
        CGRect initalbounds = CGRectMake(0, 0, 70, 70);
        CGRect secondbounds = CGRectMake(0, 0, 50, 50);
        CGRect finalbounds = CGRectMake(0, 0, 1800, 1800);
        keyframanimation.values = [[NSArray alloc] initWithObjects:[NSValue valueWithCGRect:initalbounds],[NSValue valueWithCGRect:secondbounds],[NSValue valueWithCGRect:finalbounds], nil];
        keyframanimation.keyTimes = @[@0,@.3,@1];
        [imageview.layer.mask addAnimation:keyframanimation forKey:@"bounds"];
        imageview.layer.mask.bounds = CGRectMake(0, 0, 1800, 1800);
    } [CATransaction commit];
}

Customising Xcode

  • xcode package manager with multiple plugins (plugin manager)
  • code autocomplete
    • install
      • menu – window – package manager
        FuzzyAutoComplete
  • code formatting
    • install and setup
      • menu – window – package manager
        ClangFormat
      • Menu – edit – Clang Format – WebKit
    • Shortcut
      • System preferences – keyboard – shortcuts – app shortcuts – add ( + )
        • Application: XCode
        • Name : “Format File in Focus”
        • Shortcut : ^I (control i)
    • Usage
      • In any xcode file hit ctrl+i and everythign will be arranged nicely
        For functions broken on multiple lines by arguments, just set the FIRST argument on another line and the rest will be auto formatted the same
  • Hide debugger panel when starting to type
    • install
      • menu – window – package manager
        BBUDebuggerTuckAway
  • String formatting
    • install
      • menu – window – package manager
        HOStringSense
  • Image name autocompletion (this is Kents plugin)
    • install
      • menu – window – package manager
        KSImageNamed
  • Todo in code
    • install
      • menu – window – package manager
        XToDo
  • Color theme
    • install
      • menu – window – package manager
        Monokai
      • Restart xcode
      • Xcode – preferences – fonts and colors
        Monokai

objc UIAlertView show and react to response

2014-08-04_13-33-53

@interface MyViewController() <UIAlertViewDelegate> // add UIAlertViewDelegate protocol

// show alert
[[[UIAlertView alloc] initWithTitle:@"title" message:@"are you sure you want to..." delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil] show];

// capture user responses
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    switch (buttonIndex) {
        case 0: // cancel
            break;
        case 1: // ok
            break;
    }
}

And what happens if you have multiple alerts… we’ll use an enum to differentiate between them

@interface MyViewController () <UIAlertViewDelegate>
// all the types of alerts you want to handle
typedef enum AlertTagTypes {
    ALERT_TAG_UPDATE,
    ALERT_TAG_DELETE,
} AlertTag;
@end

// show your alert with one tag
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"update" message:@"are you sure...?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];
alert.tag = ALERT_TAG_UPDATE;
[alert show];

// another alert with another tag
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"delete" message:@"are you sure...?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];
alert.tag = ALERT_TAG_DELETE;
[alert show];

// delegate method handling multiple alert responses
- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1) { // user tapped OK
        switch (alertView.tag) {
        case ALERT_TAG_UPDATE:
            [self updateMessage];
            break;
        case ALERT_TAG_DELETE:
            [self deleteMessage];
            break;
        default:
            break;
        }
    }
}

dismissing keyboard, UIToolbar and more

dismissing keyboard for a specific to textfield

[self.myTextField1 resignFirstResponder];
[self.myTextField2 resignFirstResponder];
[self.myTextField3 resignFirstResponder];

 

 

dismissing keyboard for the entire application

[[[UIApplication sharedApplication] keyWindow] endEditing:YES];

 

 

dismissing keyboard for the entire view

[self.view endEditing:YES];

 

 

forcing keyboard for a textfield

[self.textField becomeFirstResponder];

 

 

clear button while user is editing, other modes available

self.textField.clearButtonMode = UITextFieldViewModeWhileEditing;

 

 

events when the keyboard appears or hides

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];

...

- (void)keyboardWillHide:(NSNotification*)notification
{
    NSLog(@&quot;keyboard will hide&quot;);
}

-(void)keyboardWillShow:(NSNotification*)notification
{
    NSLog(@&quot;keyboard will show&quot;);
}

 

 

ignore user input

    [[UIApplication sharedApplication] beginIgnoringInteractionEvents]; // stop user interaction
    [[UIApplication sharedApplication] isIgnoringInteractionEvents]; // check if interaction is being ignored
    [[UIApplication sharedApplication] endIgnoringInteractionEvents] // resume user interaction

 

 

finding the keyboard view ONLY when its present on screen (null other times)

-(UIView*)findKeyboardView
{
    for (UIWindow* window in [UIApplication sharedApplication].windows)
        for (UIView *possibleKeyboard in window.subviews)
            if ([[possibleKeyboard description] hasPrefix:@&quot;&lt;UIPeripheralHostView&quot;])
                return possibleKeyboard;
    return nil;
}

 

 

different keyboard types

mytextfield.keyboardAppearance = UIKeyboardAppearanceAlert; // dark background
mytextfield.keyboardAppearance = UIKeyboardAppearanceDefault;
mytextfield.keyboardAppearance = UIKeyboardAppearanceLight;
mytextfield.keyboardAppearance = UIKeyboardAppearanceDark;

 

 

For a Number Pad type keyboard you cannot add a done button… thanks apple!!!!
so the only solution is to create a toolbar with a dismiss type button.
if you create the UIToolBar in the storboard view, youll need to hide it before the keyboard takes control

-(void)viewDidLoad
{
    [super viewDidLoad];
    [self.toolbarDone removeFromSuperview];
...

 

Adding a toolbar created in code on top of the keyboard
with UIBarButtonItem created from UIButton
2014-09-11_17-28-01

    // create the toolbar
    UIToolbar* toolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0,0,320,50)];
    toolbar.barStyle = UIBarStyleBlackTranslucent;
    [toolbar sizeToFit];
    self.textFieldForCodeMadeToolbar.inputAccessoryView = toolbar;
    self.textFieldForCodeMadeToolbar.keyboardAppearance = UIKeyboardAppearanceAlert;

    // create the buttons for the toolbar
    NSMutableArray* toolbarButtons = [[NSMutableArray alloc] init]; // mutable array
    for (int i=0; i&lt;10; i++) {
        UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.frame = CGRectMake(0, 0, 20, 30);
        button.layer.backgroundColor = [UIColor grayColor].CGColor;
        button.layer.borderColor = [UIColor grayColor].CGColor;
        button.layer.borderWidth = 1.0f;
        [button setTitle:[NSString stringWithFormat:@&quot;%d&quot;, i] forState:UIControlStateNormal];
        [button addTarget:self action:@selector(barButtonTapped:) forControlEvents:UIControlEventTouchUpInside];

        UIBarButtonItem* barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
        [toolbarButtons addObject:barButton];
    }
    toolbar.items = toolbarButtons; // set the buttons to the immutable array

...

-(IBAction)barButtonTapped:(UIButton*)btn
{
    NSLog(@&quot;button tapped %@&quot;, btn.titleLabel.text);
}

 

 

You may (and I prefer to) also create a toolbar in storyboard and link to a textfields keyboard action
2014-09-11_17-25-35
Make sure your toolbar outlet created is Strong

@property (strong, nonatomic) IBOutlet UIToolbar *keyboardToolbar;

Remove it from the superview (issue with ios8)

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.myTextField.inputAccessoryView = self.keyboardToolbar;
    [self.keyboardToolbar removeFromSuperview]; // needed in ios8
    ...
}