akivaleffert.com
swipe this!

Lion introduces a new gesture behavior that can be seen in Safari and XCode 4.2 and probably elsewhere. In those apps you can use a sideways swipe to go forward and back between pages such that the page moving in or out tracks your swipe. There's also a lovely shadow. I wanted to use this behavior in an app I'm writing so I wrote some code to implement what I've been calling view piles. AppKit provides a method if you want to do this that handles some of the details

-[NSEvent trackSwipeEventWithOptions:
          dampenAmountThresholdMin:
          max:
          usingHandler:]
I found it irritatingly low level and finicky so I wrapped it up in a nice high level interace for your convenience.

Download it here!. There's a header, a xib, and an implementation file. This code is provided with no guarantees etc., but if you use it for something (or find the inevitable bugs) please let me know. If you're having performance issues make sure your views are layer backed. Share and enjoy!


The code is kind of grody. I can't help but feel there's probably a much easier way to do this, but well, this worked. Where XCode and Safari diverged, I tended to do what XCode did, since the event API seemed to point more at doing things that way. I had to tweak some things and guess at some more things, so umm, AppKit guys, it'd be great if you canned up all the details and I could junk this class.

The code only has one visible class, ADLPileViewController. Create it like you would any other NSViewController. IMO, the interface is clear and should tell you everything you need to know:

@interface ADLPileViewController : NSViewController

@property id <ADLPileViewControllerDelegate> delegate;

@property NSViewController* currentViewController;
@property NSViewController* nextViewController;
@property NSViewController* prevViewController;

@property NSEventSwipeTrackingOptions swipeGestureOptions;

@end
    
But in case it doesn't, let's talk about it a little. You pass in current, next and previous view controllers. The current view controller is the one on screen right now. Previous is to the left and next is to the right. Previous and next can be nil if there's nothing before or after the current page. To use it you set the initial value of those, you set up a delegate, which we'll talk about in a minute, and you set up swipe options. The options are as described in the NSEvent documentation since they're just passed straight into the event tracker.

To communicate back have one of your classes implement <ADLPileViewControllerDelegate>.

@protocol ADLPileViewControllerDelegate <NSObject>

- (NSViewController*)prevViewControllerAfterActivating:
    (NSViewController*)newCurrentViewController
    inPile:(ADLPileViewController*)pileViewController;

- (NSViewController*)nextViewControllerAfterActivating:
    (NSViewController*) newCurrentViewController
    inPile:(ADLPileViewController*)pileViewController;

@optional

// Called only when the pile view loads
- (NSView*)backgroundViewForPile:(ADLPileViewController*)pileController;

@end

When the current view changes due to swipes your delegate will receive the relevant messages and the pile view controller will update the view controller's current, next, and previous view controllers automatically. Once again, if there's no previous or next page, just return nil. You can also specify a background for the pile view controller. It'll just use an empty view if you don't and what's behind it should still show through. That's all.