Category Archives: objective-c

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!!

A UITextView with borders

Ok I very rarely ever work with the UITextView (more like ever) and this will be a quick post to go over what I am sure is probably a simple and widely known fix for some developers.

So in my current project I have a need for the user to be able to enter multiple lines of text as a way of storing notes:

screen shot

The screen looks simple and as such I assumed it would be easily laid out in the storyboard editor. Maybe that was my first mistake? The following picture gives you an idea of what it looks like in the storyboard and, without some code quick additions, what it also will look when run:

iosstoryboard

Are you thinking the same thing I was?  Where is the border that intuitively tells the users they can click and enter some text on the screen?

I don’t know the reason behind this (maybe someone can explain it) but apparently the UITextView does not come with borders that you can control or define with the storyboard editor. There is also no way to enter Placeholder text but I will leave that as a subject for a future post.

For now lets cover the solution of getting a border to surround the text area.

Make sure that you first include the QuartzCore.framwork and then for your view add the following include statement:

#include <QuartzCore/QuartzCore.h>

Then in your views viewDidLoad method add the following lines of code (make sure that you change the name to match):

- (void)viewDidLoad
{
   [super viewDidLoad];
   // Do any additional setup after loading the view.

   [[self.textViewNotes layer] setBorderColor:[[UIColor lightGrayColor] CGColor]];
   [[self.textViewNotes layer] setBorderWidth:.4];
   [[self.textViewNotes layer] setCornerRadius:8.0f];    
}

Run it and there you go.

I am not going to go into details about what each line of code is doing because in this situation I believe the code is doing a fairly good job at documenting itself.

Where did I get the numbers from you ask?? I hate to say this but I applied no science in those numbers and it was just guess work and trying to find something that matched the UITextField already on the screen. I tried using the getters on my title field (it’s a UITextField) but that didn’t work. Probably a subject for another post at some other time.

For now enjoy and as always Happy Coding!!

Date Counting

Ok so I kind of left my previous post a bit incomplete. I did so because there was that bit of date calculation that I wasn’t yet done resolving.

Remember this bit of code:

    int castedInterval = (int)interval;
    int minutes = (castedInterval / 60) % 60;

Ok so I will be honest I wasn’t that proud of the date calculations. Especially when you take into consideration that the full implementation looked something like:

-(NSString*) stringFromTimeInterval:(NSTimeInterval) interval
{
    int castedInterval = (int)interval;
    int years = (castedInterval / (((3600 * 24)*7)*52));
    int weeks = (castedInterval / ((3600 * 24)*7));
    int days = (castedInterval / (3600 * 24));
    int hours = (castedInterval / 3600);
    int minutes = (castedInterval / 60) % 60;
    int seconds = castedInterval % 60;

    if (hours >= 24){
        hours = hours - (days * 24);
    }

    if (days >= 7){
        days = days - (weeks * 7);
    }

    if(weeks >= 52) {
        weeks = weeks - (years * 52);
    }
    // removed other code
}

I shivered every time I read that code especially that god awful mess for determining the number of years. I looked at it a few times and thought to myself ok I could probably clean that up really nice with some constants or some defines.

Ok who am I kidding it still would look crap!

Regardless of it solving the problem I still hate ugly code. Mostly because I hear myself saying what I say to so many other software developers.

In five years when you and I are no longer working here we will be judged by the code we leave behind. For that future developer lets try and make his job easier.

Quick jump to my favorite research tool and five minutes later I come up with NSCalendar and NSDateComponents. You can get more information about them here and here.

The cool thing with NSCalendar is that it has a calculations method called components that allows me to pass in 2 NSDate objects to calculate the difference. It then returns back a NSDateComponents which I can then query to get the information I am looking for.

The following is the result:

-(NSString*) stringFromTimeInterval:(NSDate *) fromDate toDate:(NSDate *)toDate interval:(NSTimeInterval) interval
{
    NSCalendar *calender = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSUInteger flags =  NSYearCalendarUnit | NSMonthCalendarUnit | NSWeekCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
    
    NSDateComponents *dateComponents = [calender components:flags fromDate:[NSDate date] toDate:[self.dueDatePicker date] options:0];
    
    int years = [dateComponents year];
    int months = [dateComponents month];
    int weeks = [dateComponents week];
    int days = [dateComponents day];
    int hours = [dateComponents hour];
    int minutes = [dateComponents minute];
    int seconds = [dateComponents second];

    NSString* result = [[NSString alloc] init];
    if (years > 0) {
        result = (years > 1) ? [result stringByAppendingString:[NSString stringWithFormat:@"%d Years ", years]] : [result stringByAppendingString:[NSString stringWithFormat:@"%d Year ", years]];
    }
    
    if (months > 0) {
        result = (months > 1) ? [result stringByAppendingString:[NSString stringWithFormat:@"%d Months ", months]] : [result stringByAppendingString:[NSString stringWithFormat:@"%d Month ", months]];
    }
    
    if (weeks > 0) {
        result = (weeks > 1) ? [result stringByAppendingString:[NSString stringWithFormat:@"%d Weeks ", weeks]] : [result stringByAppendingString:[NSString stringWithFormat:@"%d Week ", weeks]];
    }
    
    if (days > 0) {
        result = (days > 1) ? [result stringByAppendingString:[NSString stringWithFormat:@"%d Days ", days]] : [result stringByAppendingString:[NSString stringWithFormat:@"%d Day ", days]];
    }
    
    if (hours > 0) {
        result = (hours > 1) ? [result stringByAppendingString:[NSString stringWithFormat:@"%d Hours ", hours]] : [result stringByAppendingString:[NSString stringWithFormat:@"%d Hour ", hours]];
    }
    
    if (minutes > 0) {
        result = (minutes > 1) ? [result stringByAppendingString:[NSString stringWithFormat:@"%d Minutes ", minutes]] : [result stringByAppendingString:[NSString stringWithFormat:@"%d Minute ", minutes]];
    }
    
    if ((years == 0) && (months == 0) && (weeks == 0)) {
        result = [result stringByAppendingString:[NSString stringWithFormat:@"%02d Seconds", seconds]];
    }
    
    return result;
}

I am still passing in NSTimeInterval because I am debating whether I want to include the calculation of milliseconds. I couldn’t find milliseconds as a component so the following bit of code should work if I want:

    NSTimeInterval actualTimer = interval;
    int castedInterval = (int)actualTimer;
    int millSeconds = ((double)actualTimer - castedInterval) / .01;

Happy coding everyone.