Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

iOS Framework that contains helper classes that can be used in most applications

README.mdown

BDApplicationHelpers Version 1

This project demonstrates the use of the BDApplicationHelper static framework. The BDApplicationHelper Framework is a static iOS framework that will provide a number of helper classes that are typically used in just about everyone's application. In this initial incarnation, it only contains the BDHelpViewController.

The project contains three targets. The BDApplicationHelper framework, scripts that product documentation, and an example application that demonstrates usage of the framework.

The BDApplicationHelper framework can be built from the existing project. Once built, right click on the BDApplicationHelpers.framework item in the products folder and choose "Show in Finder". Drag the BDApplicationHelpers.embeddedframework from the finder to the Frameworks folder of the project you want to use it in. This should deposit it in the Framework's folder as well as add it to the "Link With Binaries" table in your application target's Build Phases. Note that it is important to use the .embeddedframework because it contains the necessary resources in terms of graphics and nib files.

BDHelpViewContoller

BDHelpViewController is a self contained Webkit Based HTML Help System. There are a couple of unique features to the BDHelpViewController. First, the bottom of the view contains a toolbar with several buttons, and on the iPad version, two extra image views. These image views will automatically load the app's icons. The right hand image view can be customized with another icon that could be used, for example, for the icon of your company. The toolbar contains buttons that show the version, provide support options, and dismiss the BDHelpViewController. When pushed the version button will show the build number. To enable this, the key CFBundleVersion in the applications plist file must be used. The "Support" button, when pressed, brings up a UIAlertSheet with buttons that will either bring up an email view controller or send the user to the app's support web page via Safari.

Second, the "Done" button and "Email Support" button utilize block call backs to allow the calling viewController control over how the UIViewController is dismissed and calls an email view controller. This allows a great deal of flexibility in that the user interface idiom is not hard coded and is completely up to the developer.

Because the BDHelpViewController utilizes a UIWebView, we can make use of a UIWebView Delegate method that allow us to either utilize html files within the main bundle or to visit a website via Safari. To keep the UIWebView from launching Safari, use the BDURLRejectStringKey to set a string that must be in the title of each of your html help files. The string is set denoted by the BDURLRejectStringKey in the supportDictionary instance variable. In order to use Safari to view the App's support web page, ensure that the URL does not contain any text that is in the BDURLRejectStringKey.

Usage

Adding the BDHelpViewController is easy in the UIViewController subclass from where you want to call it, first ensure that you have imported the correct header file

#import <BDApplicationHelpers/BDApplicationHelpers.h>

I prefer to create my viewControllers lazily:

-(BDHelpViewController *)helpViewController{
   //   static 
   if (helpViewController_!=nil) {
       return helpViewController_;
   }

   helpViewController_=[[BDHelpViewController alloc] init];

   // Populate the right hand image view, if you want, with a custom icon.
   // The left hand image view is the app's icon.
   self.helpViewController.customIconFileName=@"customIcon.png";

   self.helpViewController.supportDictionary=[NSDictionary dictionaryWithObjectsAndKeys:@"http://bigdiggy.wordpress.com/support",BDSupportURLStringKey,
                                           @"Support for Wonderful app is provided by via email and via the support website.",BDSupportStringKey,
                                           @"yourSupportAddress@wonderfulapp.com",BDSupportEmailStringKey,
                                           @"app",BDURLRejectStringKey,nil];

   helpViewController_.view.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:@"background.png"]];

   return helpViewController_;
}

Then your IBAction method that opens up the help can look like this.

- (IBAction)showHelpViewController:(id)sender {
    __block __weak ViewController *weakSelf = self;
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
       // We are on an iPhone
       self.helpViewController.doneButtonPushedCallbackBlock=^{
           ViewController *strongSelf=weakSelf;
          [strongSelf dismissModalViewControllerAnimated:YES];
       };
      self.helpViewController.emailButtonPushedCallbackBlock=^{
          ViewController *strongSelf=weakSelf;
          NSLog(@"emailButtonPushedCallback Block");
          [strongSelf dismissModalViewControllerAnimated:NO];
          [strongSelf displayMailComposer];
      };
      self.helpViewController.modalTransitionStyle=UIModalTransitionStyleFlipHorizontal;
      dispatch_async(dispatch_get_main_queue(), ^{
          [self presentModalViewController:self.helpViewController animated:YES];
      });

   }else{
      // We are on an iPad
      UIButton *theButton=sender;

      // setup popoverController
      [self.popoverController setContentViewController:self.helpViewController];

      self.helpViewController.doneButtonPushedCallbackBlock=^{
          ViewController *strongSelf=weakSelf;
          [strongSelf.popoverController dismissPopoverAnimated:YES];
      };
      self.helpViewController.emailButtonPushedCallbackBlock=^{
          ViewController *strongSelf=weakSelf;
          [strongSelf.popoverController dismissPopoverAnimated:YES];
          [strongSelf displayMailComposer];
      };

      self.popoverController.popoverContentSize=self.helpViewController.view.frame.size;
      dispatch_async(dispatch_get_main_queue(), ^{
          [self.popoverController presentPopoverFromRect:theButton.bounds inView:theButton permittedArrowDirections: UIPopoverArrowDirectionAny animated:YES];
      });
  }
  [self.helpViewController loadDocument: @"appHelpScreeniPhone.html"];
}

BDTabBarController

The BDTabBarController is container view controller that uses block based call backs in place of delegates. The BDTabBarController typically is used as a container for subclasses of BDInputViewController.

BDInputViewController and Protocol

The BDInputViewController is an abstract class that provides the basics for an inputViewController. The protocol provides a means for the parent view controller to interrogate the child input view controller for certain information as well as to provide it with a call back block that is used to dismiss it. To use any of the BDInputViewControllers or the BDTabBarController make sure the BDApplicatonHelpers framework has been added to your project and then make sure you import it in your viewController like this:

#import <BDApplicationHelpers/BDApplicationHelpers.h>

BDKeyPadViewController

The BDKeyPadViewController is a subclass of the abstract class BDInputViewController. BDKeyPadViewController and other subclasses of BDInputViewController like it are used as child viewControllers that allow the user to provide input. When using with the iPad use a UIPopoverController to show the key pad. When using with an iPhone or iPod touch utilize UIViewController containment. i.e. add the BDKeyPadViewController as a child view controller to a parent view controller.

Usage

Adding a BDKeyPadViewController to your app is easy. First ensure that the BDApplicationHelpers framework has been added to your project.

I like to load thing lazily, so I set up my ivar getter method like this:

-(BDKeyPadViewController *)keyPadViewController{
    if (keyPadViewController_) {
        return keyPadViewController_;
    }

    keyPadViewController_=[[BDKeyPadViewController alloc] init];

    // Set the value of the keypad's textfield
    keyPadViewController_.popoverTextFieldString=@"3.145";

    // Set the format type for the number we are working on.
    // There are values for decimal, currency, and percentage.
    keyPadViewController_.numberFormatType=BDNumberFormatterTypeDecimal;
    return keyPadViewController_;
}

BDPickerViewController

The BDPickerViewController is a subclass of the abstract class BDInputViewController. Like the other subclasses of BDInputViewController, BDPickerViewController is used as a child viewController that allow the user to provide some form of input. When used with the iPad, a UIPopoverController is used to present the picker view. When using with an iPhone or iPod touch UIViewController containment is utilized.

Usage

Adding a BDPickerViewController to your app is easy. Load it lazily like this:

-(BDPickerViewController *)pickerViewController{
     if (pickerViewController_) {
         return pickerViewController_;
     }
     pickerViewController_=[[BDPickerViewController alloc] init];

     pickerViewController_.numberOfComponentsInPickerViewBlock=^(UIPickerView *thePickerView){
          NSInteger numberOfComponents=1;

          if (thePickerView.tag==0) {
             numberOfComponents=1;
          }

          return numberOfComponents;
     };

     pickerViewController_.numberOfRowsInComponentBlock=^(UIPickerView *thePickerView,NSInteger component){
           NSInteger numberOfRows=2;
           return numberOfRows;
     };
     pickerViewController_.widthForComponentBlock=^(UIPickerView *thePickerView, NSInteger component){
          CGFloat componentWidth=thePickerView.frame.size.width-25;
          return componentWidth;
     };

     pickerViewController_.titleForRowBlock=^(UIPickerView *thePickerView, NSInteger row, NSInteger component){
          NSString *titleForRow=@"";
          switch (row) {
             case 0:
                titleForRow=@"One";
                break;
             case 1:
                titleForRow=@"Two";
                break;
          }

          return titleForRow;
     };

     pickerViewController_.didSelectRowComponentBlock=^(UIPickerView *thePickerView, NSInteger row, NSInteger component){
          NSLog(@"didselectRowCOmponentBlock");
          NSLog(@"selected row:  %d",row);
     };
     return pickerViewController_;

}

Typical Usage within the the calling ViewController

A typical use case is that a user pushes a button or touches a text field which will bring up the keypad.

- (IBAction)buttonPushed:(id)sender {

    UIViewController *viewController=nil;
    CGSize viewControllerSize=CGSizeZero;

    NSInteger buttonInputViewType=((UIButton *)sender).tag;

    switch (buttonInputViewType) {
        case BDInputViewTypeNumberKeyPad:
           viewController=self.keyPadViewController;
           viewControllerSize=self.keyPadViewController.view.frame.size;
           self.keyPadViewController.keyPadTitleLabel.text=@"KeyPad only";
           break;
        case BDInputViewTypePicker:
           viewController=self.pickerViewController;
           viewControllerSize=self.pickerViewController.view.frame.size;
           break;
        case BDInputViewTypeCombinedViewController:
           viewController=self.tabBarController;
           viewControllerSize=self.tabBarController.view.frame.size;
           self.keyPadViewController.keyPadTitleLabel.text=@"Combined View KeyPad";
           break;
    }

    if (buttonInputViewType==BDInputViewTypeNumberKeyPad || buttonInputViewType==BDInputViewTypeCombinedViewController) {
        self.keyPadViewController.popoverTextFieldString=self.keyPadOutputLabel.text;
        __weak __block ViewController *weakSelf=self;
        self.keyPadViewController.outputCallbackBlock=^(NSString *stringText,NSInteger tag){
            ViewController *strongSelf=weakSelf;
            strongSelf.keyPadOutputLabel.text=stringText;
        };

    }    
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        // Call the method that will slide the BDKeyPadViewer into place on the iPhone.
        [self slideIntoPlaceInputViewController:viewController];
    }else {
        [self.popoverController setContentViewController:viewController];
        self.popoverController.popoverContentSize=viewControllerSize;
        [self.popoverController presentPopoverFromRect:((UIButton *)sender).bounds inView:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    }

}

The slideIntoPlaceInputViewController: method is implemented to take advantage of iOS's new container view methods. Because we are not using a popover, we need a way of dismissing the input viewController a "Done" button is provided. We need to provide a block that will dismiss the viewController when the done button is pressed.

-(void)slideIntoPlaceInputViewController:(UIViewController *)viewController{

    // Provide a block that will dismiss the view controller when the "Done" is pressed.
    __weak __block ViewController *weakSelf=self;
    [((BDInputViewController *)viewController) setDismissViewControllerCallbackBlock:^{
        ViewController *strongSelf=weakSelf;
        [strongSelf slideOutOfPlaceInputViewController: viewController];
    }];

    // Get the size of the Input ViewController so that we can first place it off screen
    // then animate it into place.
    CGRect inputViewFrame=viewController.view.frame;
    CGFloat inputViewHeight=inputViewFrame.size.height;

    CGRect newFrame=CGRectMake(0, self.view.frame.size.height, inputViewFrame.size.width, inputViewFrame.size.height);

    viewController.view.frame=newFrame;

    // Utilize the container methods to put the input viewController into the parent view controller.
    [self addChildViewController:viewController];

    // Define the offset that the input viewController will be animated to.
    CGRect offSetRect=CGRectOffset(newFrame, 0, -inputViewHeight);

    [self.view addSubview:viewController.view];
    [UIView animateWithDuration:0.2 animations:^{
        viewController.view.frame=offSetRect;
    }
                     completion:^(BOOL finished){
                     // one of the key things with UIViewController containment is communicating the actual
                     // containment to the viewController.
                         [viewController didMoveToParentViewController:self];
                     }];
}   

Of course, after the data input is completed, we need to be able to dismiss the input viewController.

-(void)slideOutOfPlaceInputViewController:(UIViewController *)viewController{
    // Get the size of the Input ViewController so that we can first place it off screen
    // then animate it into place.
    CGRect inputViewFrame=viewController.view.frame;
    CGFloat inputViewHeight=inputViewFrame.size.height;

    // Define the offset that the input viewController will be animated to.
    CGRect offSetRect=CGRectOffset(inputViewFrame, 0, +inputViewHeight);

    // one of the key things with UIViewController containment is communicating that
    // we are removing the viewController from it's parent. We use the willMoveToParentViewController:
    // method, passing nil to signal that we are removing it from the parent viewController
    [viewController willMoveToParentViewController:nil];
    [UIView animateWithDuration:0.2 animations:^{
         viewController.view.frame=offSetRect;
    }
                     completion:^(BOOL finished){
                         [viewController.view removeFromSuperview];
                         // Communicate to the viewController that it's removal from the container is complete.
                         [viewController removeFromParentViewController];
                         // We also need to make sure that the viewControllers are no longer retained by the 
                         // BDTabBarController because we will get an exception if they have more than
                         // one rootViewController.  i.e. if we try to use the BDKeyPadViewController by
                         // itself just after using the BDTabBarController
                         if ([viewController isKindOfClass:[BDTabBarController class]]) {
                             NSLog(@"setting tabBarController to nil");
                             self.tabBarController=nil;
                         }
                     }];
}

Credits

This project makes use of Karl Stenerud's iOS-Universal-Framework

History

Version 2

Second Release with the BDKeyPadViewController

Version 1

First Release with just the BDHelpViewController

License

The below license is the new BSD license with the OSI recommended personalizations. http://www.opensource.org/licenses/bsd-license.php

Copyright (C) 2011 Big Diggy SW. All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  • Neither the name of Tim Taylor nor Big Diggy SW may be used to endorse or promote products derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY Big Diggy SW "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Something went wrong with that request. Please try again.