Permalink
Browse files

Can now go back and forward through views, properties observed for ab…

…ility to forward and backwards.
  • Loading branch information...
1 parent 2016e14 commit f1f7a87431b55d44d57240e8a65671efbd62539d @danpalmer committed Oct 5, 2012
View
@@ -11,11 +11,10 @@
@protocol DPSetupWindowStageViewController <NSObject>
/*
- Stage view controllers must implement these as they will determine whether each of the buttons can be clicked.
+ Stage view controllers must implement these as they will be observed by the setup window to determine whether the interface buttons should be enabled.
*/
-- (BOOL)canContinue;
-- (BOOL)canGoBack;
-- (BOOL)canCancel;
+@property (readonly) BOOL canContinue;
+@property (readonly) BOOL canGoBack;
@optional
View
@@ -10,8 +10,15 @@
@interface DPSetupWindow ()
-@property (assign) IBOutlet NSImageView *backgroundImageView;
+@property (retain) NSImageView *imageView;
+@property (retain) NSBox *contentBox;
+@property (retain) NSButton *cancelButton;
+@property (retain) NSButton *backButton;
+@property (retain) NSButton *nextButton;
+
@property (copy) void(^completionHandler)(BOOL);
+@property (retain) NSArray *viewControllers;
+@property (assign) NSViewController *currentViewController;
@end
@@ -24,77 +31,201 @@ - (id)initWithViewControllers:(NSArray *)viewControllers completionHandler:(void
self = [super initWithContentRect:contentRect styleMask:(NSTitledWindowMask|NSClosableWindowMask) backing:NSBackingStoreBuffered defer:YES];
if (self == nil) return nil;
- currentStage = 0;
+ if (!viewControllers || [viewControllers count] == 0) return nil;
+ if (!completionHandler) return nil;
+
+ currentStage = -1;
+ _animates = YES;
+ [self setViewControllers:viewControllers];
[self setCompletionHandler:completionHandler];
[self setContentView:[self initialiseContentViewForRect:contentRect]];
+ [self next:nil];
return self;
}
- (NSView *)initialiseContentViewForRect:(NSRect)contentRect {
NSView *contentView = [[NSView alloc] initWithFrame:contentRect];
- NSButton *cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(145, 13, 97, 32)];
- [cancelButton setBezelStyle:NSRoundedBezelStyle];
- [cancelButton setTarget:self];
- [cancelButton setAction:@selector(cancel:)];
- [cancelButton setTitle:@"Cancel"];
- [contentView addSubview:cancelButton];
-
- NSButton *backButton = [[NSButton alloc] initWithFrame:NSMakeRect(372, 13, 97, 32)];
- [backButton setBezelStyle:NSRoundedBezelStyle];
- [backButton setTarget:self];
- [backButton setAction:@selector(back:)];
- [backButton setTitle:@"Back"];
- [contentView addSubview:backButton];
-
- NSButton *nextButton = [[NSButton alloc] initWithFrame:NSMakeRect(469, 13, 97, 32)];
- [nextButton setBezelStyle:NSRoundedBezelStyle];
- [nextButton setTarget:self];
- [nextButton setAction:@selector(next:)];
- [nextButton setTitle:@"Continue"];
- [contentView addSubview:nextButton];
-
- NSImageView *imageView = [[NSImageView alloc] initWithFrame:NSMakeRect(-40, 60, 320, 320)];
- [imageView setAlphaValue:0.3];
- [imageView setImageScaling:NSImageScaleProportionallyUpOrDown];
- [imageView setImage:[[NSApplication sharedApplication] applicationIconImage]];
- [contentView addSubview:imageView];
-
- NSBox *box = [[NSBox alloc] initWithFrame:NSMakeRect(148, 57, 415, 345)];
- [box setTitlePosition:(NSNoTitle)];
- [contentView addSubview:box];
+ _cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(145, 13, 97, 32)];
+ [_cancelButton setBezelStyle:NSRoundedBezelStyle];
+ [_cancelButton setTarget:self];
+ [_cancelButton setAction:@selector(cancel:)];
+ [_cancelButton setTitle:@"Cancel"];
+ [contentView addSubview:_cancelButton];
+
+ _backButton = [[NSButton alloc] initWithFrame:NSMakeRect(372, 13, 97, 32)];
+ [_backButton setBezelStyle:NSRoundedBezelStyle];
+ [_backButton setTarget:self];
+ [_backButton setAction:@selector(back:)];
+ [_backButton setTitle:@"Back"];
+ [contentView addSubview:_backButton];
+
+ _nextButton = [[NSButton alloc] initWithFrame:NSMakeRect(469, 13, 97, 32)];
+ [_nextButton setBezelStyle:NSRoundedBezelStyle];
+ [_nextButton setTarget:self];
+ [_nextButton setAction:@selector(next:)];
+ [_nextButton setTitle:@"Continue"];
+ [contentView addSubview:_nextButton];
+
+ _imageView = [[NSImageView alloc] initWithFrame:NSMakeRect(-40, 60, 320, 320)];
+ [_imageView setAlphaValue:0.3];
+ [_imageView setImageScaling:NSImageScaleProportionallyUpOrDown];
+ [_imageView setImage:[[NSApplication sharedApplication] applicationIconImage]];
+ [contentView addSubview:_imageView];
+
+ _contentBox = [[NSBox alloc] initWithFrame:NSMakeRect(148, 57, 415, 345)];
+ [_contentBox setTitlePosition:(NSNoTitle)];
+ [contentView addSubview:_contentBox];
return contentView;
}
+#pragma mark -
+#pragma mark Customisation
+
- (void)setBackgroundImage:(NSImage *)backgroundImage {
_backgroundImage = backgroundImage;
- [[self backgroundImageView] setImage:backgroundImage];
+ [[self imageView] setImage:backgroundImage];
}
- (NSImage *)backgroundImage {
return _backgroundImage;
}
+#pragma mark -
+#pragma mark Flow Control
+
+- (void)next:(id)sender {
+
+ if (currentStage == [[self viewControllers] count]) {
+ return; // we have already triggered the callback
+ }
+
+ NSViewController<DPSetupWindowStageViewController> *previousViewController = nil;
+ if (currentStage >= 0 && currentStage < [[self viewControllers] count]) {
+ previousViewController = [[self viewControllers] objectAtIndex:currentStage];
+ }
+
+ currentStage++;
+ if (currentStage == [[self viewControllers] count]) {
+ [self completionHandler](YES);
+ return;
+ }
+
+ NSViewController<DPSetupWindowStageViewController> *nextViewController = [[self viewControllers] objectAtIndex:currentStage];
+ NSView *view = [nextViewController view];
+
+ if ([self animates] && previousViewController) {
+ [NSAnimationContext beginGrouping];
+
+ if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) {
+ [[NSAnimationContext currentContext] setDuration:3.0];
+ }
+ [[NSAnimationContext currentContext] setCompletionHandler:^{
+ [[previousViewController view] removeFromSuperviewWithoutNeedingDisplay];
+ }];
+
+ [view setFrame:NSMakeRect(400, 0, 400, 330)];
+ [[self contentBox] addSubview:view];
+ [[view animator] setFrameOrigin:NSMakePoint(0, 0)];
+ [[[previousViewController view] animator] setFrameOrigin:NSMakePoint(-400, 0)];
+
+ [NSAnimationContext endGrouping];
+ } else {
+ [view setFrame:NSMakeRect(0, 0, 400, 330)];
+ if (previousViewController) {
+ [[previousViewController view] removeFromSuperviewWithoutNeedingDisplay];
+ }
+ [[self contentBox] addSubview:view];
+ }
+
+ [self registerObserversForPreviousViewController:previousViewController nextViewController:nextViewController];
+ [self recalculateButtonEnabledStates];
+}
+
+- (void)back:(id)sender {
+
+ if (currentStage == 0) {
+ return;
+ }
+
+ NSViewController<DPSetupWindowStageViewController> *previousViewController = nil;
+ if (currentStage >= 0 && currentStage < [[self viewControllers] count]) {
+ previousViewController = [[self viewControllers] objectAtIndex:currentStage];
+ }
+
+ currentStage--;
+ if (currentStage == [[self viewControllers] count]) {
+ [self completionHandler](YES);
+ return;
+ }
+
+ NSViewController<DPSetupWindowStageViewController> *nextViewController = [[self viewControllers] objectAtIndex:currentStage];
+ NSView *view = [nextViewController view];
+
+ if ([self animates] && previousViewController) {
+ [NSAnimationContext beginGrouping];
+
+ if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) {
+ [[NSAnimationContext currentContext] setDuration:2.0];
+ }
+ [[NSAnimationContext currentContext] setCompletionHandler:^{
+ [[previousViewController view] removeFromSuperviewWithoutNeedingDisplay];
+ }];
+
+ [view setFrame:NSMakeRect(-400, 0, 400, 330)];
+ [[self contentBox] addSubview:view];
+ [[view animator] setFrameOrigin:NSMakePoint(0, 0)];
+ [[[previousViewController view] animator] setFrameOrigin:NSMakePoint(400, 0)];
+
+ [NSAnimationContext endGrouping];
+ } else {
+ [view setFrame:NSMakeRect(0, 0, 400, 330)];
+ if (previousViewController) {
+ [[previousViewController view] removeFromSuperviewWithoutNeedingDisplay];
+ }
+ [[self contentBox] addSubview:view];
+ }
+
+ [self registerObserversForPreviousViewController:previousViewController nextViewController:nextViewController];
+ [self recalculateButtonEnabledStates];
+}
+
- (void)cancelOperation:(id)sender {
[self cancel:sender];
}
-#pragma mark -
-#pragma mark Flow Control
-
- (void)cancel:(id)sender {
[[NSApplication sharedApplication] endSheet:self returnCode:0];
[self completionHandler](NO);
}
-- (void)back:(id)sender {
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ [self recalculateButtonEnabledStates];
+}
+
+- (void)recalculateButtonEnabledStates {
+ NSViewController<DPSetupWindowStageViewController> *currentViewController = [[self viewControllers] objectAtIndex:currentStage];
+ if ([currentViewController respondsToSelector:@selector(canContinue)]) {
+ [[self nextButton] setEnabled:[currentViewController canContinue]];
+ }
+ if ([currentViewController respondsToSelector:@selector(canGoBack)]) {
+ [[self backButton] setEnabled:[currentViewController canGoBack]];
+ }
+
+ if (currentStage == 0) {
+ [[self backButton] setEnabled:NO];
+ }
}
-- (void)next:(id)sender {
+- (void)registerObserversForPreviousViewController:(NSViewController *)previousViewController nextViewController:(NSViewController *)nextViewController {
+ [previousViewController removeObserver:self forKeyPath:@"canContinue"];
+ [previousViewController removeObserver:self forKeyPath:@"canGoBack"];
+ [nextViewController addObserver:self forKeyPath:@"canContinue" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:NULL];
+ [nextViewController addObserver:self forKeyPath:@"canGoBack" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:NULL];
}
@end
@@ -14,6 +14,12 @@
379CC9D2161F416E009FEB86 /* DPAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 379CC9D1161F416E009FEB86 /* DPAppDelegate.m */; };
379CC9D5161F416E009FEB86 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 379CC9D3161F416E009FEB86 /* MainMenu.xib */; };
379CC9DE161F4F7A009FEB86 /* DPSetupWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 379CC9DC161F4F7A009FEB86 /* DPSetupWindow.m */; };
+ 379CC9E9161F7E62009FEB86 /* DPFirstViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 379CC9E7161F7E62009FEB86 /* DPFirstViewController.m */; };
+ 379CC9EA161F7E62009FEB86 /* DPFirstViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 379CC9E8161F7E62009FEB86 /* DPFirstViewController.xib */; };
+ 379CC9EF161F7E7F009FEB86 /* DPSecondViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 379CC9ED161F7E7F009FEB86 /* DPSecondViewController.m */; };
+ 379CC9F0161F7E7F009FEB86 /* DPSecondViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 379CC9EE161F7E7F009FEB86 /* DPSecondViewController.xib */; };
+ 379CC9F4161F7E8A009FEB86 /* DPThirdViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 379CC9F2161F7E8A009FEB86 /* DPThirdViewController.m */; };
+ 379CC9F5161F7E8A009FEB86 /* DPThirdViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 379CC9F3161F7E8A009FEB86 /* DPThirdViewController.xib */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -32,6 +38,15 @@
379CC9D4161F416E009FEB86 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
379CC9DB161F4F7A009FEB86 /* DPSetupWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DPSetupWindow.h; sourceTree = SOURCE_ROOT; };
379CC9DC161F4F7A009FEB86 /* DPSetupWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DPSetupWindow.m; sourceTree = SOURCE_ROOT; };
+ 379CC9E6161F7E62009FEB86 /* DPFirstViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DPFirstViewController.h; path = ViewControllers/DPFirstViewController.h; sourceTree = "<group>"; };
+ 379CC9E7161F7E62009FEB86 /* DPFirstViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DPFirstViewController.m; path = ViewControllers/DPFirstViewController.m; sourceTree = "<group>"; };
+ 379CC9E8161F7E62009FEB86 /* DPFirstViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = DPFirstViewController.xib; path = ViewControllers/DPFirstViewController.xib; sourceTree = "<group>"; };
+ 379CC9EC161F7E7F009FEB86 /* DPSecondViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DPSecondViewController.h; path = ViewControllers/DPSecondViewController.h; sourceTree = "<group>"; };
+ 379CC9ED161F7E7F009FEB86 /* DPSecondViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DPSecondViewController.m; path = ViewControllers/DPSecondViewController.m; sourceTree = "<group>"; };
+ 379CC9EE161F7E7F009FEB86 /* DPSecondViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = DPSecondViewController.xib; path = ViewControllers/DPSecondViewController.xib; sourceTree = "<group>"; };
+ 379CC9F1161F7E8A009FEB86 /* DPThirdViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DPThirdViewController.h; path = ViewControllers/DPThirdViewController.h; sourceTree = "<group>"; };
+ 379CC9F2161F7E8A009FEB86 /* DPThirdViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DPThirdViewController.m; path = ViewControllers/DPThirdViewController.m; sourceTree = "<group>"; };
+ 379CC9F3161F7E8A009FEB86 /* DPThirdViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = DPThirdViewController.xib; path = ViewControllers/DPThirdViewController.xib; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -88,6 +103,7 @@
379CC9D0161F416E009FEB86 /* DPAppDelegate.h */,
379CC9D1161F416E009FEB86 /* DPAppDelegate.m */,
379CC9E0161F4F85009FEB86 /* SetupWindow */,
+ 379CC9EB161F7E69009FEB86 /* ViewControllers */,
379CC9D3161F416E009FEB86 /* MainMenu.xib */,
379CC9C5161F416E009FEB86 /* Supporting Files */,
);
@@ -115,6 +131,22 @@
name = SetupWindow;
sourceTree = "<group>";
};
+ 379CC9EB161F7E69009FEB86 /* ViewControllers */ = {
+ isa = PBXGroup;
+ children = (
+ 379CC9E6161F7E62009FEB86 /* DPFirstViewController.h */,
+ 379CC9E7161F7E62009FEB86 /* DPFirstViewController.m */,
+ 379CC9E8161F7E62009FEB86 /* DPFirstViewController.xib */,
+ 379CC9EC161F7E7F009FEB86 /* DPSecondViewController.h */,
+ 379CC9ED161F7E7F009FEB86 /* DPSecondViewController.m */,
+ 379CC9EE161F7E7F009FEB86 /* DPSecondViewController.xib */,
+ 379CC9F1161F7E8A009FEB86 /* DPThirdViewController.h */,
+ 379CC9F2161F7E8A009FEB86 /* DPThirdViewController.m */,
+ 379CC9F3161F7E8A009FEB86 /* DPThirdViewController.xib */,
+ );
+ name = ViewControllers;
+ sourceTree = "<group>";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -170,6 +202,9 @@
379CC9C9161F416E009FEB86 /* InfoPlist.strings in Resources */,
379CC9CF161F416E009FEB86 /* Credits.rtf in Resources */,
379CC9D5161F416E009FEB86 /* MainMenu.xib in Resources */,
+ 379CC9EA161F7E62009FEB86 /* DPFirstViewController.xib in Resources */,
+ 379CC9F0161F7E7F009FEB86 /* DPSecondViewController.xib in Resources */,
+ 379CC9F5161F7E8A009FEB86 /* DPThirdViewController.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -183,6 +218,9 @@
379CC9CB161F416E009FEB86 /* main.m in Sources */,
379CC9D2161F416E009FEB86 /* DPAppDelegate.m in Sources */,
379CC9DE161F4F7A009FEB86 /* DPSetupWindow.m in Sources */,
+ 379CC9E9161F7E62009FEB86 /* DPFirstViewController.m in Sources */,
+ 379CC9EF161F7E7F009FEB86 /* DPSecondViewController.m in Sources */,
+ 379CC9F4161F7E8A009FEB86 /* DPThirdViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -10,6 +10,10 @@
#import "DPSetupWindow.h"
+#import "DPFirstViewController.h"
+#import "DPSecondViewController.h"
+#import "DPThirdViewController.h"
+
@interface DPAppDelegate ()
@property (retain) DPSetupWindow *setupFlow;
@@ -19,7 +23,16 @@ @interface DPAppDelegate ()
@implementation DPAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
- DPSetupWindow *setupFlow = [[DPSetupWindow alloc] initWithViewControllers:@[] completionHandler:^(BOOL completed) {
+
+ NSViewController *firstViewController = [[DPFirstViewController alloc] initWithNibName:@"DPFirstViewController" bundle:[NSBundle mainBundle]];
+ NSViewController *secondViewController = [[DPSecondViewController alloc] initWithNibName:@"DPSecondViewController" bundle:[NSBundle mainBundle]];
+ NSViewController *thirdViewController = [[DPThirdViewController alloc] initWithNibName:@"DPThirdViewController" bundle:[NSBundle mainBundle]];
+
+ DPSetupWindow *setupFlow = [[DPSetupWindow alloc] initWithViewControllers:@[
+ firstViewController,
+ secondViewController,
+ thirdViewController
+ ] completionHandler:^(BOOL completed) {
if (!completed) {
NSLog(@"Cancelled setup process");
} else {
@@ -0,0 +1,15 @@
+//
+// DPFirstViewController.h
+// DPSetupWindow
+//
+// Created by Dan Palmer on 05/10/2012.
+// Copyright (c) 2012 Dan Palmer. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+#import "DPSetupWindow.h"
+
+@interface DPFirstViewController : NSViewController <DPSetupWindowStageViewController>
+
+@end
Oops, something went wrong.

0 comments on commit f1f7a87

Please sign in to comment.