Category Archives: Uncategorized

Adjusting View so Keyboard doesn’t hide a UIControler

I have been working on an application that has a screen where a user can view and edit their profile information. One of the problems that I have had to confront is that certain fields were getting covered by the keyboard and making it impossible for the user to be able to enter or update information.

Certainly this had to be answered by someone on Stack Overflow or somewhere else in the land of the internet that Google has had a chance to index. I found ideas and some samples out there but nothing that really dealt with a way to handle a screen that might have multiple UIControls getting hidden.

I found How to make UITextField move up when keyboard is present which gave some ideas and helped to serve how I resolved my situation. One of the areas of interest is the demonstration about how to get the size of the keyboard. With today’s varying size iPhones this is important because for each device the keyboard changes in its display size. It actually is a great read so if you haven’t already reviewed it let me give you a few minutes to go check it out.

In the sample application I am going to create a view that has two UITextFields that will be visible when the keyboard and another 2 that are hidden when the keyboard comes up.

Screen Shot 2015-02-25 at 6.27.59 PMScreen Shot 2015-02-25 at 6.28.10 PM

Create Project

Let’s start off with creating a Single View Application:

Screen Shot 2015-02-26 at 7.08.06 AM

Screen Shot 2015-02-26 at 7.08.35 AM

 

Layout the Screen Design

Once the project is setup click on the projects storyboard (Main.storyboard) add a Scroll View to the view that is presented. You will want to stretch it out so that it uses the entire display for the view.

Once that is complete go ahead and add some Text Fields as children to the Scroll View as well as some labels. Later I am going to explain how to use the scroll view to bring controls which have the focus of the keyboard into view by the user.

For a quick demonstration in this example I designed the following screen:

Screen Shot 2015-02-26 at 10.21.04 PM

Once you have the view setup go ahead and run the application and you will notice whenever you click on a Text Field that once the keyboard comes the bottom Text Fields are covered.

You might also notice that there is no way to dismiss the keyboard.

Dismissing the Keyboard

Whenever someone taps outside of Text Field we need to make sure that the keyboard gets dismissed.

Click on the ViewController.m file and add a UITapGestureRecognizer to the view.

- (void)viewDidLoad {
    [super viewDidLoad];

    UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];
    [[self view] addGestureRecognizer:recognizer];
}

I am going to have it call a method called hideKeyboard which I will use later in other places as well.

-(void)hideKeyboard {
    [[self view] endEditing:YES];
}

Go ahead and run the application again and you should notice now that when you click outside of a Text Field the keyboard is now getting dismissed.

Still haven’t resolved that problem about the keyboard covering up the Text Fields at the bottom so lets start resolving that situation.

View Scrolling with help from NSNotification and Delegates

First step is to wire up the controls to properties in ViewController.h.

Setting up Properties

Click on the storyboard again and when that is up click on the Assistant Editor so that both the storyboard and ViewController.h are displayed.

Screen Shot 2015-01-25 at 3.30.55 PM

Then control drag and create properties for the Scroll View and each of the Text Fields:

@property (weak, nonatomic) IBOutlet UITextField *hiddenTextField;
@property (weak, nonatomic) IBOutlet UITextField *hiddenTextField2;

@property (weak, nonatomic) IBOutlet UITextField *visibleTextField;
@property (weak, nonatomic) IBOutlet UITextField *visibleTextField2;

@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;

After that is done you can close the Assistant Editor so that the ViewController.h is still displayed in the editor.

Add one more property that will be used to track the active control which has the focus of the keyboard:

@property (weak, nonatomic) IBOutlet UIControl *activeField;

 Setting up Delegates for Text Fields

Click on ViewController.m and lets add support for the UITextFieldDelegate.

@interface ViewController ()
<UITextFieldDelegate>

@end

Next we are going to extend the viewDidLoad method so that the ViewController will act as a delegate for each of the UITextField properties that we created:

- (void)viewDidLoad {
    [super viewDidLoad];

    UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];
    [[self view] addGestureRecognizer:recognizer];
    
    [[self hiddenTextField] setDelegate:self];
    [[self hiddenTextField2] setDelegate:self];
    [[self visibleTextField] setDelegate:self];
    [[self visibleTextField2] setDelegate:self];
}

Next we add code to support the textFieldDidBeginEditing method and textFieldDidEndEditing.

We are going to use these methods to help set the active UIControl field which has the focus of the keyboard. In the textFieldDidEndEditing we are also going to hide the keyboard which you will understand as we start dealing with NSNotification for the keyboard events.

-(void) textFieldDidBeginEditing:(UITextField *)textField {
    [self setActiveField:textField];
}

-(void) textFieldDidEndEditing:(UITextField *)textField {
    [self hideKeyboard];
    [self setActiveField:nil];
}

Setting up Notifications for the Keyboard

The final remaining piece is to get notifications for whenever the keyboard is displayed so that the control which is in focus can be scrolled to an area on the screen that is visible to the user.

So that we are properly handling notifications of the appearance of the keyboard for our view we are going to subscribe to UIKeyboardDidShowNotification and UIKeyboardWillHideNotification whenever our view is displayed and when it is no longer being displayed we will remove the subscriptions.

-(void) viewDidAppear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
}

-(void) viewDidDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

When the keyboard is displayed the goal is to scroll the view if the active control is being hidden by the keyboard. To achieve this use the size of the keyboard as its displayed, this information is include with the userInfo that comes with the notification event. Then compute a CGRect which represents the viewable area and check to see if the active control is inside of it.

If the active control is not in the viewable area then you will want to move the scroll view up so that the active control is positioned just above the keyboard.

- (void)keyboardWasShown:(NSNotification *)notification {
    NSDictionary* info = [notification userInfo];
    
    CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    
    CGPoint hiddenField = [[self activeField] frame].origin;
    CGFloat hiddenFieldHeight = [[self activeField] frame].size.height;
    
    CGRect visibleRect = [[self view] frame];
    visibleRect.size.height -= keyboardSize.height;
    
    if (!CGRectContainsPoint(visibleRect, hiddenField)) {
        CGPoint scrollPoint = CGPointMake(0.0, hiddenField.y - visibleRect.size.height + hiddenFieldHeight);
        [[self scrollView] setContentOffset:scrollPoint animated:YES];
    }
}

When a notification is received for the keyboard being closed the scroll view needs to be positioned back to its normal position.

- (void)keyboardWillBeHidden:(NSNotification *)notification {
    [[self scrollView] setContentOffset:CGPointZero animated:YES];
}

If you run the app you will notice now that the text fields normally hidden by keyboard are scrolling to just above the keyboard when they receive focus. The only work that remains now is giving the user ability to switch from one Text to another using the return key on the keyboard.

Enabling the Return Key for Text Fields

This is an added feature that I like to use whenever I have a screen with multiple text fields and I want the user to be able to flow from one field to another with only having to using the Return key.

Open the storyboard and for each Text Field on the view you will want to set the Auto-enable Return Key under the Attributes Selector.

Screen Shot 2015-02-27 at 3.02.52 PM

Screen Shot 2015-02-27 at 3.03.36 PM

Then you will want to add a method to handle textFieldShouldReturn that will be called for your view.

-(BOOL) textFieldShouldReturn:(UITextField *)textField {
    if (textField == [self visibleTextField]) {
        [self hideKeyboard];
        [[self visibleTextField2] becomeFirstResponder];
    } else if (textField == [self visibleTextField2]) {
        [self hideKeyboard];
        [[self hiddenTextField2] becomeFirstResponder];
    } else if (textField == [self hiddenTextField2]) {
        [self hideKeyboard];
        [[self hiddenTextField] becomeFirstResponder];
    } else if (textField == [self hiddenTextField]) {
        [self hideKeyboard];
    }
    
    return YES;
}

If you run the app now you will notice that as you type in each text field the Return key is enabled and if you click it they focus for the keyboard moves to the next field on the form. At the last Text Field I am currently hiding the keyboard but you could easily add some field validations here or anything else you might want to do when the user is done.

For now you can find the source here and as always Happy Coding!!!

UIActionSheet with Blocks

A very simple solution to a problem that bugged for me for sometime.

So I was recently doing some work on DNISSwiperCell when I came across the need to be able to access a custom UITableViewCell (DNISItemCell) from the event of a selection made from a UIActionSheet. Since the UIActionSheet was displayed from a button click on the cell I had access to the cell but once I left the scope of the event for the button being clicked I could not find a way to get that cell object.

I had thought of several different ways to stuff the object in various places but all the solutions really had the appearance of a hack that I knew I would hate myself for later. What I wanted was a way to pass in a callback like I do in javascript that would at the same time give me access to that object (Closure).

Luckily I had been playing with Blocks a couple of months back and I was realizing this would be a great way to solve my problem. So I first created a simple implementation of a block that I could assign to a property in the class.

It looked something like the following:

typedef void (^simpleBlock)(void);
simpleBlock callBack;

Then when I created the action sheet I could assign a block to that variable:

UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:NULL delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:NULL otherButtonTitles:@"Action Button", nil];

callBack = ^{
    // add code here for the block
};

[actionSheet setActionSheetStyle:UIActionSheetStyleDefault];
[actionSheet showInView: [self view]];

Then all that remained was handling the delegate for the action sheet so that when it was dismissed it would call that block.

-(void) actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    if (callBack != nil) {
        callBack();
    }
}

Done!!! Oh but wait this solution is really no different than the other hacks that I was trying to avoid plus how do I know which button on the action sheet was pressed? What about the other delegates for UIActionSheet?

So let’s take this a step further and actually find a way to handle all the delegates for UIActionSheetDelegate and at the same time handling the information that comes back with each. By inspecting the different delegates that UIActionSheetDelegate offers we can see that while there are six different delegates they can actually be grouped into two common patterns that we can with the following:

typedef void(^DNISActionSheetButtonIndex)(UIActionSheet * actionSheet, NSInteger buttonIndex);
typedef void(^DNISActionSheet)(UIActionSheet * actionSheet);

With the pattern for each delegate typedef as a block that we can use let’s proceed with bringing them into a object that we can use for identifying a property that will represent each of the delegates for UIActionSheetDelegate.

typedef void(^DNISActionSheetButtonIndex)(UIActionSheet * actionSheet, NSInteger buttonIndex);
typedef void(^DNISActionSheet)(UIActionSheet * actionSheet);

@interface DNISActionSheetBlocks : UIActionSheet

@property (strong) DNISActionSheetButtonIndex blockClickedButton;
@property (strong) DNISActionSheet blockWillPresentActionSheet;
@property (strong) DNISActionSheet blockDidPresentActionSheet;
@property (strong) DNISActionSheetButtonIndex blockDidDismissWithButton;
@property (strong) DNISActionSheetButtonIndex blockWillDismissWithButton;
@property (strong) DNISActionSheet blockActionSheetCancel;

@end

Then move on to the implementation of the delegates and plug each of them into the properties that we have just defined. The plan is that as each delegate is called that a log message will be printed to allow me to debug the application.

@interface DNISActionSheetBlocks () 

@end

@implementation DNISActionSheetBlocks

#pragma responding to actions
-(void) actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    NSLog(@"blockClickedButton was called");
    if([self blockClickedButton] != nil) {
        [self blockClickedButton](actionSheet, buttonIndex);
    }
}

#pragma customizing behavior
-(void) willPresentActionSheet:(UIActionSheet *)actionSheet
{
    NSLog(@"blockWillPresentActionSheet was called");
    if ([self blockWillPresentActionSheet] != nil) {
        [self blockWillPresentActionSheet](actionSheet);
    }
}

-(void) didPresentActionSheet:(UIActionSheet *)actionSheet
{
    NSLog(@"blockDidPresentActionSheet was called");
    if ([self blockDidPresentActionSheet] != nil) {
        [self blockDidPresentActionSheet](actionSheet);
    }
}

-(void) actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    NSLog(@"blockWillDismissWithButton was called");
    if ([self blockWillDismissWithButton] != nil) {
        [self blockWillDismissWithButton](actionSheet, buttonIndex);
    }
}

-(void) actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    NSLog(@"blockDidDismissWithButton was called");
    if ([self blockDidDismissWithButton] != nil) {
        [self blockDidDismissWithButton](actionSheet, buttonIndex);
    }
}

#pragma cancelling
-(void) actionSheetCancel:(UIActionSheet *)actionSheet
{
    NSLog(@"blockActionSheetCancel was called");
    if ([self blockActionSheetCancel] != nil) {
        [self blockActionSheetCancel](actionSheet);
    }
}

@end

I begin to use this as is when I realize that my basic implementation won’t work because the initWithTitle method requires the delegate to be passed into it. So as I begin to quickly code up an init method it dawns on me how I am going to handle the various ways buttons can be added to an actionsheet? Ok so first things let’s just create an actionsheet without any buttons:

-(id) initWithTitle: (NSString *) title;

Implementation:

-(id) initWithTitle: (NSString *) title
{
    self = [self initWithTitle:title delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil, nil];
    
    return self;
}

Yes I know that an actionsheet without any buttons doesn’t do any good but for now I can test things out. Also with a basic actionsheet created I can manually add the buttons with a call to addButtonWithTitle. With a quick test I can now see things falling into place.

Like I said creating an actionsheet without any buttons really does no good. It’s comparable to building a car but not adding any wheels to it. How do I drive this thing? So let’s add a couple of more init methods.

  • initWithTitleAndButtons – actionsheet with a title and all the buttons we want displayed.
  • initWithButtons – the title is great and all but sometimes I can do without it, just show some buttons.

Both methods are almost identical with the only exception being the title information. Because of this I am only going to talk about initWithTitleAndButtons. For a complete look at the code you can go here.

Definition:

-(id) initWithTitleAndButtons:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle destructiveButtonTitle:(NSString *)destructiveButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ...;

The implementation is going to take some work because we will have to deal a variable number of buttons that can be supplied to it. During testing I also learned that there is an order in which you add generic buttons as well as cancel buttons and destructive buttons. So from the following implementation you can see that we first add the generic buttons followed by the cancel button and then finally add the destructive button.

-(id) initWithTitleAndButtons:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle destructiveButtonTitle:(NSString *)destructiveButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ...
{
    self = [self initWithTitle:title];
    
    if (self) {
        if (nil != otherButtonTitles) {
            [self addButtonWithTitle:otherButtonTitles];

            va_list arguments;
            id eachObject;

            va_start(arguments, otherButtonTitles);
            while ((eachObject = va_arg(arguments, id)))
            {
                [self addButtonWithTitle:eachObject];
            }
            va_end(arguments);
        }
        
        if (nil != cancelButtonTitle) {
            NSInteger index = [self addButtonWithTitle:cancelButtonTitle];
            [self setCancelButtonIndex:index];
        }
        
        if (nil != destructiveButtonTitle) {
            NSInteger index = [self addButtonWithTitle:destructiveButtonTitle];
            [self setDestructiveButtonIndex:index];
        }
    }
    
    return self;
}

Hopefully you will find this useful and if so check back on it because even as I wrote it and this blog I found some other areas for improvement. Might rethink the whole thing and give the developer a way to assign a block of code to an individual button?

For now you can find the source here and as always Happy Coding!!

Hello world!

First post and like all things developed for the first time you start with a simple Hello World. I am going to a different level though and also put out some code examples.

The following code samples can be be seen in action here.

$('#helloBtn').button().click(function() {
    alert('Hello World.');
});


Some simple Objective C debug logging. You didn’t think I was going to write a whole iOS app did you? Maybe that will be some future blog.

-(void)sayHello
{
    NSLog(@"Hello World");
}