Permalink
Browse files

Tidying up for publication - renamed example project, improved commen…

…ts and updated copyright headers
  • Loading branch information...
1 parent 16ca185 commit 6eba0c2bc7c4729b5f0a1a97798027cac9b81b15 @followben committed Oct 17, 2011
View
@@ -0,0 +1,41 @@
+//
+// TPScrollBarController.h
+// TPScrollBarController
+//
+// Copyright 2011 Ben Stovold. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "TPScrollBarDelegate.h"
+
+// A root container view controller in the vein of UITabBarController.
+// Has a scrolling menubar at the bottom and allows:
+// * multiple bar buttons per controller
+// * bar buttons to be enabled or disabled
+@interface TPScrollBarController : UIViewController {
+
+@private
+ NSObject<TPScrollBarDelegate> *delegate_;
+
+ UIScrollView *scrollBar_;
+ UIView *contentView_;
+ UIViewController *selectedViewController_;
+ NSArray *scrollBarPageArray_;
+ NSOrderedSet *scrollBarPageSet_;
+ NSSet *viewControllers_;
+ NSArray *barButtons_;
+ NSUInteger selectedScrollBarPage_;
+ NSArray *registry_;
+}
+
+@property(nonatomic, strong) NSObject<TPScrollBarDelegate> *delegate;
+@property(nonatomic, readonly) UIViewController *selectedViewController;
+@property(nonatomic, readonly) NSUInteger selectedScrollBarPage;
+
+- (void)setViewControllers:(NSSet *)viewControllers
+ WithBarButtons:(NSArray *)barButtons
+ onScrollBarPages:(NSArray *)pageNumbers;
+
+- (void)selectScrollBarPage:(NSUInteger)pageNumber animated:(BOOL)animated;
+
+@end
View
@@ -0,0 +1,290 @@
+//
+// TPScrollBarController.m
+// TPScrollBarController
+//
+// Copyright 2011 Ben Stovold. All rights reserved.
+//
+
+#import "TPScrollBarController.h"
+#import <objc/message.h>
+
+#pragma mark Constants and enums
+CGFloat const kScrollBarHeight = 88.f;
+
+#pragma mark -
+@interface TPScrollBarController ()
+
+@property(nonatomic) UIViewController *selectedViewController;
+@property(nonatomic) NSUInteger selectedScrollBarPage;
+
+@property(nonatomic, strong) UIScrollView *scrollBar;
+@property(nonatomic, strong) UIView *contentView;
+@property(nonatomic, strong) NSOrderedSet *scrollBarPageSet;
+@property(nonatomic, strong) NSSet *viewControllers;
+@property(nonatomic, strong) NSArray *barButtons;
+@property(nonatomic, strong) NSArray *scrollBarPageArray;
+@property(nonatomic, strong) NSArray *registry;
+
+- (void)performSelectorOnDelegate:(SEL)aSelector withObject:(id)param1 andObject:(id)param2;
+- (void)initaliseContainerViews;
+- (void)resizeScrollBarForNumberOfPages;
+- (void)layoutBarButtons;
+- (void)registerBarButtonTargetsAsChildViewControllers;
+- (void)barButtonReceivedTouchDown:(UIButton *)sender;
+- (void)barButtonReceivedTouchUpInside:(UIButton *)sender;
+
+@end
+
+
+#pragma mark -
+@implementation TPScrollBarController
+
+#pragma mark Object lifecycle
+
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
+{
+ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
+ if (self) {
+ registry_ = [NSArray array];
+ }
+ return self;
+}
+
+- (void)didReceiveMemoryWarning
+{
+ [super didReceiveMemoryWarning];
+}
+
+#pragma mark Accessors and mutators
+
+@synthesize delegate = delegate_;
+@synthesize scrollBar = scrollBar_;
+@synthesize contentView = contentView_;
+@synthesize selectedViewController = selectedViewController_;
+@synthesize scrollBarPageSet = scrollBarPageSet_;
+@synthesize selectedScrollBarPage = selectedScrollBarPage_;
+@synthesize viewControllers = viewControllers_;
+@synthesize barButtons = barButtons_;
+
+@synthesize scrollBarPageArray = scrollBarPageArray_;
+@synthesize registry = registry_;
+
+
+#pragma mark - View lifecycle
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+
+ [self initaliseContainerViews];
+ [self resizeScrollBarForNumberOfPages];
+ [self layoutBarButtons];
+ [self registerBarButtonTargetsAsChildViewControllers];
+ self.selectedScrollBarPage = 1;
+ [self.contentView addSubview:self.selectedViewController.view];
+}
+
+- (void)viewDidUnload
+{
+ self.scrollBar = nil;
+ self.contentView = nil;
+ [super viewDidUnload];
+}
+
+
+#pragma mark - UIInterfaceOrientation methods
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+ return (interfaceOrientation == UIInterfaceOrientationPortrait);
+}
+
+
+#pragma mark - Public methods
+
+- (void)setViewControllers:(NSSet *)viewControllers
+ WithBarButtons:(NSArray *)barButtons
+ onScrollBarPages:(NSArray *)pageNumbers
+{
+ NSAssert([barButtons count] == [pageNumbers count], @"barButton and pageNumber arrays must contain the same number of objects");
+ for (UIButton *barButton in barButtons) {
+ NSAssert([[barButton allTargets] count] == 1, @"barButtons can only have one target");
+ NSAssert([[[[barButton allTargets] allObjects] objectAtIndex:0] isKindOfClass:[UIViewController class]], @"barButton target must be a UIViewController");
+ }
+ for (NSNumber *pageNumber in pageNumbers) {
+ NSAssert([pageNumber integerValue] > 0, @"pageNumbers must be greater than zero");
+ }
+
+ NSSortDescriptor *ascending = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES];
+ NSMutableArray *numbers = [NSMutableArray arrayWithArray:pageNumbers];
+ [numbers sortUsingDescriptors:[NSArray arrayWithObject:ascending]];
+ self.scrollBarPageSet = [NSOrderedSet orderedSetWithArray:numbers];
+ self.viewControllers = viewControllers;
+ self.barButtons = barButtons;
+ self.scrollBarPageArray = pageNumbers;
+}
+
+- (void)selectScrollBarPage:(NSUInteger)pageNumber animated:(BOOL)animated
+{
+ NSUInteger pageWidth = [[UIScreen mainScreen] applicationFrame].size.width;
+ CGRect frame = self.scrollBar.bounds;
+ frame.origin.x = pageWidth * (pageNumber - 1);
+ [self.scrollBar scrollRectToVisible:frame animated:animated];
+ self.selectedScrollBarPage = pageNumber;
+}
+
+
+#pragma mark - Private methods
+
+- (void)performSelectorOnDelegate:(SEL)aSelector withObject:(id)param1 andObject:(id)param2;
+{
+ if ([delegate_ respondsToSelector:aSelector])
+ objc_msgSend(delegate_, aSelector, param1, param2); // performSelector: generates compiler warnings under ARC
+}
+
+// Initialises an empty scrollBar and contentView and adds them as subviews.
+- (void)initaliseContainerViews
+{
+ CGFloat xpos = 0.f;
+ CGFloat width = [[UIScreen mainScreen] applicationFrame].size.width;
+ CGFloat ypos = [[UIScreen mainScreen] applicationFrame].size.height - kScrollBarHeight;
+ self.scrollBar = [[UIScrollView alloc] initWithFrame:CGRectMake(xpos, ypos, width, kScrollBarHeight)];
+ [self.scrollBar setBackgroundColor:[UIColor redColor]];
+ self.scrollBar.clipsToBounds = YES;
+ self.scrollBar.scrollEnabled = YES;
+ self.scrollBar.pagingEnabled = YES;
+ self.scrollBar.showsHorizontalScrollIndicator = YES;
+ self.scrollBar.showsVerticalScrollIndicator = YES;
+ [self.view addSubview:self.scrollBar];
+
+
+ ypos = [[UIScreen mainScreen] bounds].origin.y;
+ CGFloat height = [[UIScreen mainScreen] applicationFrame].size.height - kScrollBarHeight;
+ self.contentView = [[UIView alloc] initWithFrame:CGRectMake(xpos, ypos, width, height)];
+ [self.contentView setBackgroundColor:[UIColor greenColor]];
+ self.contentView.clipsToBounds = YES;
+ [self.view addSubview:self.contentView];
+}
+
+- (void)resizeScrollBarForNumberOfPages
+{
+ CGSize size = self.scrollBar.frame.size;
+ size.width = [[UIScreen mainScreen] applicationFrame].size.width * ((NSNumber *)[self.scrollBarPageSet lastObject]).integerValue;
+ self.scrollBar.contentSize = size;
+}
+
+- (void)layoutBarButtons
+{
+ // For each page (in order)
+ for (NSNumber *page in self.scrollBarPageSet) {
+
+ NSUInteger pageWidth = [[UIScreen mainScreen] applicationFrame].size.width;
+
+ // Get indicies of buttons for this page
+ NSIndexSet *buttonIndexSet = [self.scrollBarPageArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
+ return obj == page;
+ }];
+
+ // Calculate the total width of buttons on this page.
+ __block NSUInteger widthOfButtonsOnThisPage = 0;
+ [buttonIndexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
+ UIButton *barButton = [self.barButtons objectAtIndex:idx];
+ widthOfButtonsOnThisPage = widthOfButtonsOnThisPage + barButton.frame.size.width;
+ }];
+
+ NSAssert(widthOfButtonsOnThisPage < pageWidth, @"Can't fit barButtons on page %f", page);
+
+ NSUInteger numberOfButtonsOnThisPage = [buttonIndexSet count];
+
+ __block NSUInteger offset;
+
+ // Calculate how far to offset each button from the left of the last one.
+ offset = (pageWidth - widthOfButtonsOnThisPage) / (numberOfButtonsOnThisPage + 1);
+
+ // Set the x position for the first button.
+ __block NSUInteger xpos = offset + (pageWidth * (page.integerValue - 1));
+
+ // For each button on the page (in order)
+ [buttonIndexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
+
+ UIButton *barButton = [self.barButtons objectAtIndex:idx];
+
+ // place it on the scrollBar according to the page offset.
+ CGRect newFrame = barButton.frame;
+ newFrame.origin.x = xpos;
+ newFrame.origin.y = barButton.frame.size.height / 2;
+ barButton.frame = newFrame;
+ [self.scrollBar addSubview:barButton];
+
+ // Calculate the x origin for the next pass through
+ xpos = xpos + barButton.frame.size.width + offset;
+
+ }];
+ }
+}
+
+- (void)registerBarButtonTargetsAsChildViewControllers
+{
+ // For each bar button
+ for (UIButton *barButton in self.barButtons) {
+
+ if (self.viewControllers) {
+
+ __block NSMutableSet *controllers = [NSMutableSet setWithArray:self.childViewControllers];
+ [[barButton allTargets] enumerateObjectsUsingBlock:^(id target, BOOL *stop) {
+
+ // View Controller setter asserts that there is only one target.
+ // That is, there should only be one target to 'enumerate'.
+ UIViewController *controller = (UIViewController *)target;
+
+ // If the target isn't already a child viewController, add it.
+ if (![controllers containsObject:target]) {
+ [self addChildViewController:controller];
+ [controller didMoveToParentViewController:self];
+ if (!self.selectedViewController) self.selectedViewController = controller;
+ [controllers addObject:target];
+ }
+
+ // Register the button/ target/ action combination
+ NSDictionary *entry = [NSDictionary dictionaryWithObjectsAndKeys:barButton, @"barButton", target, @"target", nil];
+ self.registry = [self.registry arrayByAddingObject:entry];
+
+ }];
+
+ // Add self as a target for touch events
+ [barButton addTarget:self action:@selector(barButtonReceivedTouchDown:) forControlEvents:UIControlEventTouchDown];
+ [barButton addTarget:self action:@selector(barButtonReceivedTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
+
+ }
+ }
+}
+
+- (void)barButtonReceivedTouchDown:(UIButton *)sender
+{
+ // Get the rejistry entry for the target
+ NSUInteger idx = [self.registry indexOfObjectPassingTest: ^(id dictionary, NSUInteger idx, BOOL *stop) {
+ return [[dictionary objectForKey: @"barButton"] isEqual:sender];
+ }];
+ UIViewController *targetViewController = (UIViewController *)[[self.registry objectAtIndex:idx] objectForKey:@"target"];
+
+ // If the target isn't the current viewController, transition to the new view
+ if (![self.selectedViewController isEqual:targetViewController]) {
+ [self transitionFromViewController:self.selectedViewController toViewController:targetViewController duration:0 options:UIViewAnimationTransitionNone animations:^{} completion:^(BOOL finished) {
+ self.selectedViewController = targetViewController;
+ }];
+ }
+}
+
+- (void)barButtonReceivedTouchUpInside:(UIButton *)sender
+{
+ // Inform delegate of button selection.
+ [self performSelectorOnDelegate:@selector(scrollBar:DidTouchUpInsideBarButton:) withObject:self andObject:sender];
+}
+
+@end
@@ -1,11 +1,9 @@
//
// AppDelegate.h
-// TPScrollBarController
+// TPScrollBarControllerExample
//
-// Created by Ben Stovold on 16/10/2011.
-// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+// Copyright 2011 Ben Stovold. All rights reserved.
//
-
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
Oops, something went wrong.

0 comments on commit 6eba0c2

Please sign in to comment.