Code Snippet: NSDate Floor and Ceiling

May 25th, 2011

Overview

In Stone Hill Time Card, you select a date range using an NSDatePicker. Unfortunately, NSDatePicker gives back the dates selected with the time component set to the time the app was launched. If you want to use the dates you get from the date picker in an NSPredicate to filter between the dates selected, you need to find out the NSDate value for a chosen date at 00:00:00 and 23:59:59.

NSDate+DateFloor+DateCeil is a category on NSDate that adds two methods to take care of this. Given an instance of NSDate someDate that contains the date 2011-05-25 11:00:07 -0700:, you can call

[someDate floor] to get an NSDate instance containing 2011-05-25 00:00:00 -0700

and

[someDate ceil] to get an NSDate instance containing 2011-05-25 23:59:59 -0700

Implementation

Date+DateFloor+DateCeil.h

#import <Foundation/Foundation.h>



@interface NSDate (DateFloorAndDateCeil)

- (NSDate *) floor;

- (NSDate *) ceil;

@end

Date+DateFloor+DateCeil.m

#import "NSDate+DateFloor+DateCeil.h"

@implementation NSDate (DateFloorAndDateCeil)

// Note:    This implementation assumes 60 minutes in an hour, 60 seconds in a minute, and 24 hours in a day

//          Not for use on Mars.

//          Assumes we’re using the gregorian calendar.



- (NSDate *) floor {

    // Returns an NSDate that is the same day as the receiver, but

    // with a time component of 00:00:00

    

    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

    NSDateComponents *overshootComponents = [gregorian components:NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit fromDate:self];

    NSTimeInterval overshotByHours = [overshootComponents hour] * 60 * 60;

    NSTimeInterval overshotByMinutes = [overshootComponents minute] * 60;

    NSTimeInterval overshotBySeconds = [overshootComponents second];

    NSTimeInterval interval = [self timeIntervalSinceReferenceDate];

    NSTimeInterval floorInterval = interval – overshotByHours – overshotByMinutes – overshotBySeconds;

    NSDate *floorDate = [[[NSDate alloc] initWithTimeIntervalSinceReferenceDate:floorInterval] autorelease];

    

    return floorDate;

}

- (NSDate *) ceil {

    // Returns an NSDate that is the same day as the receiver, but

    // with a time component of 11:59:59

    NSDate *floorDate = [self floor];

    NSTimeInterval secondsInADay = 86400;

    NSDate *ceilDate = [floorDate dateByAddingTimeInterval:secondsInADay-1];

    

    return ceilDate;

    

}

@end

You can also download a sample XCode project with this category.

Attribution

Attribution All source is released free of charge. You may incorporate it into any of your applications. I assume no liability for any action that may come from you using this source. If you do use this in your application, I’d like a mention in your about box, but I don’t require it. Don’t forget to drop me an email at warwick@codehackers.net so I know when to stroke my ego.

Locations 3

May 20th, 2011

Locations 3 is now available on the Mac App Store. Like always, I’ve gotten a few questions about what’s new. In this release, you’ll find:

  • Changing your default mail application
  • Changing your default web browser
  • Hiding applications
  • Enabling or disabling your firewall
  • Turning Time Machine on and off

While answering this question, I decided to pull a few screenshots and put together a short history of Locations. If you’re interested in seeing how it’s evolved, you should check out the History of Locations page.

Thanks to everyone who helped get this release shipped. As always, don’t hesitate to contact me if you’ve got any questions or concerns.

Stone Hill Invoicer 2

May 12th, 2010

E.B. White once said “The best writing is rewriting.” He wasn’t talking about Stone Hill Invoicer, but he could have been.

I’m pleased to announce the release of Stone Hill Invoicer 2. The entire application has been been rewritten, right down to it’s core. Of course, that’s not all. If you’ve used previous versions of Invoicer, you’ll see lots of new stuff: quotes, uninvoiced items, a decent address book, folders, more powerful smart folders, Stone Hill Time Card integration, better searching, multiple companies, and an even cleaner and simpler design.

If you’ve never used it before, here are a few key facts:

  1. If Stone Hill Invoicer had a MySpace page, it would say “Influences: iTunes, Mail, Brent Simmons and his Weblog, WWPD.”
  2. Stone Hill Invoicer uses 12 pieces of open source code, including one that was written just for it.
  3. Stone Hill Invoicer got popovers a couple weeks before the iPad was introduced, but it doesn’t expect you to believe that.
  4. The best way to get to know Stone Hill Invoicer better is to download it and give it a try.

If you’ve got any questions or comments, I’d be pleased to answer them.

In Defense of Online Help Pages

May 11th, 2010

In a couple days I’m going to be releasing version 2 of Stone Hill Invoicer. The last beta reports are trickling in, and like the superb testers they are, my beta group is working hard to find anything they can to comment on. Among other things, todays email contained a bit of discussion on the delay in the scrolling about box, a note about behaviour of the sourcelist when a certain sequence of clicks and keyboard arrows was applied, and two notes about the online help system.

I’ve done something a little different this time around. In previous versions of Stone Hill I shipped a standard Apple Help Book. This is an HTML help book with a few special tweaks that you can bundle up with your app so that it’s available through the Apple Help Viewer. In this version, the “Stone Hill 2.0 Help” menu item opens your default web browser and directs you to a blog that I set up specially for the app.

There’s only one disadvantage compared to a help book for my app: it’s not local.

So what about the guy on a fourteen hour flight to Paris? Will he be unable to work because he doesn’t have a local copy? For Google Docs, I buy that argument. For an invoicing app’s help system? Not so much. I’d like to imagine I write good enough apps that you don’t need the help system, but even if you do, a lack of access to it for a few hours is unlikely to kill your ability to be productive.

But what advantages does an online help system bring? Even if the man on the airplane is mythical, there have to be a few advantages to make it a good idea.

1. It’s a lot easier on me

There are no good tools to generate help books. They’re a pain to update and a pain to work with. Writing entries in MarsEdit is a thousand times better than building a static site with mildly specialized HTML.

2. I really like the idea of being able to leave comments on documentation

I get the occasional email that mentions some tip or way of doing something that a customer came up with, asking if I want to put it in the help documentation. Assuming it’s a great tip, and I have the time to dust off the help docs and repackage them and reupload the app, that’s still a huge turnaround time for something that’s as simple as a comment on a blog post.

With the online help system, I’ve left comments enabled. Sure, I’ll have to put in a minute or two a day to deal with spam, but a few good tips and tricks from users will more than make up for that.

3. Updating the help without updating the app

I can’t justify making the user download a new copy of the app just to get a new revision of the help book. Especially if I want to make lots of little revisions as I see the support mail come in. In previous versions I’ve had to separate help and the FAQ, when really they’re the same thing, I just feel able to update one and not the other.

4. The Leopard and Snow Leopard help viewer isn’t very good

I’m sure there’s a very good reason for it somewhere, but a help viewer that insists on sitting on top of all other windows means that I can’t keep it open and use the app I need help with. On my Powerbook this was a huge issue. On my development machine (20″ + 23″ screens) it’s less of a problem, but still annoys me.

Your browser is an excellent document viewer. We should use it for that.

5. Now I can link users directly to a piece of documentation

In the past, I’ve actually copied and pasted entire help book entries, because the help entry describes exactly what’s being asked, they just didn’t check for it. With the help entries online, I can now include a link in the email, saving me from having to reformat any pictures and text in the file.

Quirky Things in the Stone Hill Invoicer 2 Interface

May 9th, 2010

When it came time to update Stone Hill Invoicer’s design to be a little more modern, I couldn’t resist playing around a bit.   Some of this experimentation didn’t make it into the final app, but I think it’s fun to show it off anyhow.

Popovers

When the iPad announcement came out, I’ll admit I was thrilled about popovers. Those are those little bubbles that act like temporary panels when you press some button to summon them up.

A couple weeks earlier I’d used MAAttachedWindow to implement popovers in my app. In several places, the previous version of the app needed to use a sheet to present a more detailed view. That’s a lousy thing to do unless it’s really required. The user has to shift their focus, deal with a modal dialog, and then remember what they were doing.

Old Modal.png

An old modal sheet used to edit the billing address on an invoice

With a popover the user sees details immediately related to what they’re working on without changing their focus. Clicking away from the dialog immediately dismisses it. When their attention shifts to another part of the document, they don’t need to perform the initial manual step of dismissing what they were previously working on.

popover.png

A new popover, displaying the same information, but in a way that doesn’t break the users workflow.

All in all, these work great on the desktop. They let me get rid of at least a half dozen modal sheets.

That being said, during initial testing, I found that users were confused by the lack of ‘Ok’ and ‘Cancel’ buttons on the popovers. Clicking elsewhere to dismiss it wasn’t an intuitive action, though they didn’t expect to have to dismiss the popovers when switching focus to another task. I added ‘Done’ and ‘Cancel’ buttons to my popovers. This increased the size of the popover, but provided the user with an immediate cue for how to dismiss them.


Modal Popovers with Lightboxing

Flush with popover success, I decided to try applying the same style to modal sheets.  I’ve seen this in a few web apps, and on the iPad too if I’m not mistaken (I might be, we don’t get them in Canada for a while yet).

When I was experimenting, one of the big problems was that they weren’t very visible against the app.  There was no real way of indicating that they were modal.

Lightboxes are something you see a lot in the web world.  In order to highlight the modal popover, I dimmed the window they were modal to.  Though it helped draw attention to them, in my implementation it disabled interaction with the window below it.

Screen shot 2010-05-09 at 5.18.26 PM.png
A modal popover on a lightboxed background.

Feedback during beta testing was that they were just weird.  I got rid of these and replaced them with sheets.

That being said, I’m convinced that these help an application look more visually balanced, and it’s just a matter of getting the implementation right.  I plan on revisiting this at some point.


Nag Screens in the Sidebar

There’s a fine line between annoying your users with nag screens and not getting paid for your software.  In Stone Hill, I decided to try a bit of a different way of walking that line.

The nagsceen is unobtrusive.  In unregistered copies, there’s a couple of items in the source list: “Buy Stone Hill Invoicer” and “Register Stone Hill Invoicer”.  Clicking on the “Buy Stone Hill Invoicer” item shows a decent sized sales pitch.

Purchase Screen

The ‘upgrade nag screen’.  This only shows when you click the “Buy Stone Hill Invoicer” item in the sourcelist.

During beta testing, one of my users asked where the purchase button was.  He’d checked under the application menu and couldn’t find it.  Fair enough.  I ended up adding a menu item that shows the same screen under the application menu.

This strikes me as being in the same vein as Panic’s top–of-the-window nag counters.


 

Sidebar Search via the Global Search Box

One of the things that always drove me nuts about Stone Hill was that the search function only worked on some screens.  In version two, using the search from one of the document screens filters the customer and inventory lists.  Though I considered creating a special iPhone style search box at the top of each table that would initially be scrolled out of view, it seems like a better idea to keep the global search box as the only place to search from.

Feedback has been positive about this so far, but this method may have discoverability issues.

Search

A customer list filtered by search text