Last active
December 16, 2015 02:09
-
-
Save ifournight/5360212 to your computer and use it in GitHub Desktop.
SToolkitController and Company class.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// STabControl.h | |
// Xcode Reader | |
// | |
// Created by Song Hui 2013-4-15 | |
// Copyright(C)Song Hui. All rights reserved. | |
#import <UIKit/UIKit.h> | |
@class STabControl; | |
@protocol SStabControlDelegate<NSObject> | |
- (void)tabControl:(STabControl *)tabControl didSelectTabAtIndex:(NSUInteger *)index; | |
@end | |
@interface STabControl : UIView | |
// Tab images for normal/ unselected state. | |
@property (nonatomic, strong) NSArray *tabImages; | |
// Tab images for selected state. | |
// Selected images' size should be the same with normal/ unselected images' respectively. | |
@property (nonatomic, strong) NSArray *tabSelectedImages; | |
// All tabs STabControl owned from left to right order. | |
@property (nonatomic, strong) NSArray *tabs; | |
// Control's delegate. | |
@property (nonatomic, weak) id<STabControlDelegate> delegate; | |
// Designated Initializer | |
- (id)initWithFrame:(CGRect)frame tabImages:(NSArray *)tabImages tabSelectedImages:(NSArray *)tabSelectedImages; | |
@end | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// STabControl.m | |
// Xcode Reader | |
// | |
// Created by Song Hui 2013-4-15 | |
// Copyright(C)Song Hui. All rights reserved. | |
#import "STabControl.h" | |
const NSUInteger kSTabControlAffectiveTouchWidth = 44; | |
@implementation STabControl | |
- (id)initWithFrame:(CGRect)frame tabImages:(NSArray *)tabImages tabSelectedImages:(NSArray *)tabSelectedImages | |
{ | |
self = [super initWithFrame:frame]; | |
if (self) { | |
self.backgroundColor = [UIColor clearColor]; | |
_tabImages = tabImages; | |
_tabSelectedImages = tabSelectedImages; | |
} | |
return self; | |
} | |
- (void)willMoveToSuperview:(UIView *)superview | |
{ | |
[super willMoveToSuperview]; | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
if (self.tabImages.count == 0) { | |
NSLog(@"STabControl: Tab images empty."); | |
return; | |
} | |
if (self.tabImages.count != self.tabSelectedImages.count) { | |
NSLog(@"STabControl: Tab images' count and selected images' count not match."); | |
return; | |
} | |
NSUInteger tabWidth = kSTabControlAffectiveTouchWidth; | |
NSUInteger tabHeight = self.frame.size.height; | |
NSUInteger horizontalOffset = self.frame.size.width / self.tabImages.count; | |
NSUInteger x = (horizontalOffset - tabWidth) / 2; | |
NSMutableArray *tabs = [NSMutableArray alloc] initWithCapacity:self.tabImages.count]; | |
for (NSUInteger i = 0; i < self.tabImages.count; i++) { | |
UIButton *tab = [UIButton alloc] initWithFrame:CGRectIntegral(CGRectMake(x, 0, tabWidth, tabHeight))]; | |
[tab setImage:self.tabImages[i] forState:UIControlStateNormal]; | |
[tab setImage:self.tabSelectedImages[i] forState:UIControlStateSelected]; | |
[tab addTarget:self action:@selector(tabTouchUpInside:) forControlStates:UIControlEventTouchUpInsdie]; | |
// Calculate tab's image edge insets. | |
UIImage *tabImage = self.tabImages[i]; | |
CGSize tabImageSize = tabImage.size; | |
NSUInteger topEdgeInset = floor((tabHeight - tabImageSize.height) / 2); | |
NSUInteger leftEdgeInset = floor((tabWidth - tabImageSize.width) / 2); | |
NSUInteger bottomEdgeInset = ceil((tabHeight - tabImageSize.height) / 2); | |
NSUInteger rightEdgeInset = ceil((tabWidth - tabImageSize.width) / 2); | |
tab.imageEdgeInsets = UIEdgeInsetsMake(topEdgeInset, leftEdgeInset, bottomEdgeInset, rightEdgeInset); | |
[self addSubview:tab]; | |
[tabs addObject:tab]; | |
x += horizontalOffset; | |
} | |
self.tabs = tabs; | |
[self selectTab:self.tabs[0] animated:NO]; | |
}); | |
} | |
- (void)selectTab:(UIButton *)selectedTab animated:(BOOL)animated | |
{ | |
for (UIButton *tab in self.tabs) { | |
if (selectedTab == tab) { | |
tab.selected = YES; | |
self.selectedIndex = [self.tabs indexOfObject:tab]; | |
} else { | |
tab.selected = NO; | |
} | |
} | |
} | |
- (void)tabTouchUpInside:(UIButton *)tab | |
{ | |
[self selectTab:tab animated:NO]; | |
[self.delegate tabControl:self didSelectTabAtIndex:self.selectedIndex]; | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SToolkitBar.h | |
// Xcode Reader | |
// | |
// Created by Song Hui 2013-4-11 | |
// Copyright(C)Song Hui. All rights reserved. | |
#import <UIKit/UIKit.h> | |
@interface SToolkitBar : UIToolbar | |
// SToolkitBar can have STabControl and UIButton as its items. | |
// Before assignment, please ensure each item has correct frame and UIViewAutoResizingMask, so SToolkitBar can layout all items appropriately when device rotate and any other reason causing SToolkitBar's geometry change. | |
// FUTURE UPDATE: When UIViewAutoResizingMask cannot achieve the intended layout request. Update maybe require. | |
@property (nonatomic, strong) NSArray *barItems; | |
// Designated Initializer is still initWithFrame:. This is just a continent initial method. | |
- (id)initWithFrame:(CGRect)frame barItems:(NSArray *)barItems backgroundImage:(UIImage *)backgroundImage shadowImage:(UIImage *)shadowImage; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SToolkitBar.h | |
// Xcode Reader | |
// | |
// Created by Song Hui 2013-4-11 | |
// Copyright(C)Song Hui. All rights reserved. | |
#import "SToolkitBar.h" | |
@implementation SToolkitBar | |
- (id)initWithFrame:(CGRect)frame | |
{ | |
self = [super initWithFrame:frame]; | |
if (self) { | |
} | |
return self; | |
} | |
- (id)initWithFrame:(CGRect)frame barItems:(NSArray *)barItems backgroundImage:(UIImage *)backgroundImage shadowImage:(UIImage *)shadowImage | |
{ | |
self = [self initWithFrame:frame]; | |
if (self) { | |
_barItem = barItem; | |
[self setBackgroundImage:backgroundImage forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault]; | |
// [self setBackgroundImage:backgroundImage forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsLandscapePhone]; | |
[self setShadowImage:shadowImage forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault]; | |
// [self setShadowImage:shadowImage forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsLandscapePhone]; | |
self.autoresizingMask = UIViewAutoresizingMaskFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin; | |
} | |
return self; | |
} | |
- (void)setBarItems:(NSArray *)barItems | |
{ | |
for (id barItem in _barItems) { | |
[barItem removeFromSuperView]; | |
} | |
_barItems = barItems; | |
for (id barItem in self.barItems) { | |
if ([barItem isKindOfClass:[STabControl class]]) { | |
[self addSubview:barItem]; | |
} else ([barItem isKindOfClass:[UIButton class]]) { | |
[self addSubview:barItem]; | |
} else { | |
NSLog(@"SToolkitBar: Unacceptable Bar Item Type."); | |
} | |
} | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SToolkitcontroller.h | |
// Xcode Reader | |
// | |
// Created by Song Hui 2013-4-11 | |
// Copyright(C)Song Hui. All rights reserved. | |
#import <UIKit/UIKit.h> | |
@interface SToolkitController : UIViewController<STabControlDelegate> | |
UIKIT_EXTERN NSUInteger kSToolkitControllerWidth; | |
// Controller's root superview. | |
// Be responsible for displaying a shadow effect at controller's right border. | |
@property (nonatomic, strong) UIView *containerView; | |
// The superview for all children view controller. | |
@property (nonatomic, strong) UIView *viewControllerTransitionView; | |
// SToolkitController's children view controller. | |
@property (nonatomic, strong) NSArray *viewControllers; | |
// The current selected/ on screen child controller. | |
@property (nonatomic, strong) UIViewController *selectedViewController; | |
// Self explained. | |
@property (nonatomic, assign) NSUInteger selectedIndex; | |
// The main function bar of SToolkitController, controller use it to switch its children controllers. | |
// ToolkitBar is a hybrid of UITabbar and Standard UIToolbar. | |
// It has a STabControl to achieve the tab switch ability, along with that, it also has a setting button. | |
// Because UIToolbar already can put STabControl and UIButton on itself, toolkitBar will be UIToolbar for now. | |
// Subclass of UIToolbar may be required in later progress of Xcode Reader Project. | |
@property (nonatomic, strong) UIToolbar *toolkitBar; | |
// Tab control located on toolkitBar. Controller use it to switch its children controllers. | |
@property (nonatomic, strong) STabControl *tabControl; | |
// Toolkit Bar items will determine what's currently displayed on Toolkit Bar. | |
@property (nonatomic, strong) NSArray *barItems; | |
// The root/ default Toolkit bar items. | |
@property (nonatomic, strong) NSArray *defautBarItems; | |
- (id)initWithViewControllers:(NSArray *)viewControllers selectedIndex:(NSUInteger)selectedIndex; | |
@end | |
@interface UIViewController (SToolkitController) | |
// If view controller has a SToolkitController as its ancestor, return it. Otherwise, return nil. | |
// Child view controller of SToolkitController that want to quick access SToolkitController can write an getter method in its implementation. | |
@property (nonatomic, readonly, weak) UIViewController *toolkitController; | |
// Child view controller of SToolkitController can override this property's setter method in order to change the Bar items display on its parent' Toolkit Bar. | |
// The override of setter method only require assign the SToolkitBarItems intended to be displayed on ToolkitBar, SToolkitController will notice the change and update the bar appropriately. | |
// NOTICE: If not being a child of SToolkitController, don't implement this property. | |
@property (nonatomic, readwrite, weak) NSArray *toolkitBarItems; | |
@end | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SToolkitcontroller.m | |
// Xcode Reader | |
// | |
// Created by Song Hui 2013-4-11 | |
// Copyright(C)Song Hui. All rights reserved. | |
#import "SToolkitController.h" | |
const NSUInteger kSToolkitControllerWidth = 320; | |
const NSUInteger kSToolkitBarHeight = 44; | |
const UIEdgeInsets kSToolkitBarBackgroundImageCapInsets = UIEdgeInsetsZero; //Placeholder Value | |
const UIEdgeInsets kSToolkitBarShadowImageCapInsets = UIEdgeInsetsZero; //Placeholder Value | |
const UIEdgeInsets kSToolkitBarButtonBackgroundImageCapInsets = UIEdgeInsetsZero; //Placeholder Value | |
const CGRect kTabControlFrame = CGRectMake(40, 0, 180, 44); | |
const CGRect kSettingButtonFrame = CGRectMake(0, 0, 0, 0); //Placeholder Value | |
const NSString *kSToolkitBarBackgroundImageName = @"ToolkitBar-Background.png"; | |
const NSString *kSToolkitBarShadowImageName = @"ToolkitBar-Shadow.png"; | |
const NSString *kSTabControlSearchImageName = @"STabControl-Search.png"; | |
const NSString *kSTabControlSearchSelectedImageName = @"STabControl-Search-Selected.png"; | |
const NSString *kSTabControlExploreImageName = @"STabControl-Explore.png"; | |
const NSString *kSTabControlExploreSelectedImageName = @"STabControl-Explore-Selected.png"; | |
const NSString *kSTabControlBookmarkImageName = @"STabControl-Bookmark.png"; | |
const NSString *kSTabControlBookmarkSelectedImageName = @"STabControl-Bookmark-Selected.png"; | |
const NSString *kSToolkitBarButtonImageName = @"ToolkitBar-Button.png"; | |
const NSString *kSToolkitBarButtonBackgroundImageName = @"ToolkitBar-Button-Background.png" | |
const NSString *kSToolkitBarButtonBackgroundSelectedImageName = @"ToolkitBar-Button-BackgroundSelected.png" | |
typedef NS_ENUM(NSInteger, SToolkitTransitionAnimation) { | |
SToolkitTransitionAnimationNone, | |
SToolkitTransitionAnimationPlaceholder1, | |
SToolkitTransitionAnimationPlaceholder2 | |
}; | |
@interface SToolkitController : UIViewController() | |
// Child view controller's transition will be added to this queue. And execute FIFO. | |
@property (nonatomic, strong) NSArray *transitionQueue; | |
@end | |
@implementation SToolkitController | |
#pragma mark - Life Cycle | |
- (id)initWithViewControllers:(NSArray *)viewControllers selectedIndex:(NSUInteger)selectedIndex | |
{ | |
self = [super init]; | |
if (self) { | |
_viewControllers = viewControllers; | |
_selectedIndex = selectedIndex; | |
_transitionQueue = [[NSMutableArray alloc] init]; | |
// Default Bar Items Creation | |
UIImage *tabControlSearchImage = [UIImage imageNamed:kSTabControlSearchImageName]; | |
UIImage *tabControlExploreImage = [UIImage imageNamed:kSTabControlExploreImageName]; | |
UIImage *tabControlBookmarkImage = [UIImage imageNamed:kSTabControlBookmarkImageName]; | |
UIImage *tabControlSearchSelectedImage = [UIImage imageNamed:kSTabControlSearchSelectedImageName]; | |
UIImage *tabControlExploreSelectedImage = [UIImage imageNamed:kSTabControlExploreSelectedImageName]; | |
UIImage *tabControlBookmarkSelectedImage = [UIImage imageNamed:kSTabControlBookmarkSelectedImageName]; | |
STabControl *tabControl = [[STabControl alloc] initWithFrame:kTabControlFrame tabImages:@[tabControlSearchImage, tabControlExploreImage, tabControlBookmarkImage] tabSelectedImages:@[tabControlSearchSelectedImage, tabControlExploreSelectedImage, tabControlBookmarkSelectedImage]]; | |
tabControl.delegate = self; | |
UIButton *settingButton = [[UIButton alloc] initWithFrame:kSettingButtonFrame]; | |
UIImage *settingButtonImage = [UIImage imageNamed:kSToolkitBarButtonImageName]; | |
UIImage *settingButtonBackgroundImage = [[UIImage imageNamed:kSToolkitBarButtonBackgroundImageName] resizableImageWithCapInsets:kSToolkitBarButtonBackgroundImageCapInsets]; | |
UIImage *settingButtonBackgroundSelectedImage = [[UIImage imageNamed:kSToolkitBarButtonBackgroundSelectedImageName] resizableImageWithCapInsets:kSToolkitBarButtonBackgroundImageCapInsets]; | |
[settingButton setImage:settingButtonImage forState:UIControlStateNormal]; | |
[settingButton setBackgroundImage:settingButtonBackgroundImage forState:UIControlStateNormal]; | |
[settingButton setBackgroundImage:settingButtonBackgroundSelectedImage forState:UIControlStateHighlighted | UIControlStateSelected]; | |
[settingButton addTarget:self action:@selector(settingButtonTouchUpInside:) forControlEvent:UIControlEventTouchUpInside]; | |
NSArray *defaultBarItems = @[tabControl, settingButton]; | |
_defaultBarItems = defaultBarItems; | |
_barItems = defaultBarItems; | |
} | |
return self; | |
} | |
- (void)loadView | |
{ | |
CGSize applicationSize = [[UIScreen mainScreen] applicationFrame].size; | |
CGFloat applicationHeight = applicationSize.height; | |
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) { | |
applicationHeight = applicationSize.width; | |
} | |
// Alloc and init views | |
_containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kSToolkitControllerWidth, applicationHeight)]; | |
_viewControllerTransitionView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kSToolkitControllerWidth, applicationHeight - kSToolkitBarHeight)]; | |
UIImage *toolkitBarBackgroundImage = [[UIImage imageNamed:kSToolkitBarBackgroundImageName] resizableImageWithCapInsets:kSToolkitBarBackgroundImageCapInsets]; | |
UIImage *toolkitBarShadowImage = [[UIImage imageNamed:kSToolkitBarShadowImageName] resizableImageWithCapInsets:kSToolkitBarShadowImageCapInsets]; | |
_toolkitBar = [[SToolkitBar alloc] initWithFrame:CGRectMake(0, applicationHeight - kSToolkitBarHeight, kSToolkitControllerWidth, kSToolkitBarHeight) barItems:self.barItems backgroundImage:toolkitBarBackgroundImage shadowImage:toolkitBarShadowImage]; | |
// Views's autoresizingMask configuration. | |
_containerView.autoresizingMask = UIViewAutoresizingMaskFlexibleHeight | UIViewAutoresizingMaskFlexibleRightMargin | UIViewAutoresizingMaskFlexibleBottomMargin; | |
_viewControllerTransitionView.autoresizingMask = UIViewAutoresizingMaskFlexibleHeight | UIViewAutoresizingMaskFlexibleRightMargin | UIViewAutoresizingMaskFlexibleBottomMargin; | |
_toolkitBar.autoresizingMask = UIViewAutoresizingMaskFlexibleTopMargin | UIViewAutoresizingMaskFlexibleRightMargin; | |
// Add Subviews | |
self.view = _containerView; | |
[_containerView addSubview:_viewControllerTransitionView]; | |
[_containerView addSubView:_toolkitBar]; | |
} | |
- (void)viewDidLoad | |
{ | |
[super viewDidLoad]; | |
// Children view controllers' toolkitBatItem observing. | |
for (UIViewController *viewController in self.viewController) { | |
[viewController addObserver:self forKeyPath:@"toolkitBatItems" options:NSKeyValueObservingNew context:nil]; | |
} | |
} | |
#pragma mark - Children View Controllers Management and Transition | |
- (void)selectViewControllerAtIndex:(NSUInteger)selectedIndex animationType:(SToolkitTransitionAnimation)animationType | |
{ | |
if (!self.selectedViewController || self.selectedIndex != selectedIndex) { | |
NSArray *transition = @[@self.selectedIndex, @selectedIndex]; | |
// Add transition to transition queue. | |
if (self.transitionQueue.count == 0) { | |
[self.transitionQueue addObject:transition]; | |
// Only immediately start transition if queue is empty. | |
[self executeTransitionWithAnimationType:animationType]; | |
} else { | |
[self.transitionQueue addObject:transition]; | |
} | |
} | |
} | |
- (void)executeTransitionWithAnimationType:(SToolkitTransitionAnimation)animationType | |
{ | |
NSArray *transition = self.transitionQueue[0]; | |
NSUInteger fromIndex = [transition[0] integerValue]; | |
NSUInteger toIndex = [transition[1] integerValue]; | |
UIViewController *fromController = self.viewControllers[fromIndex]; | |
UIViewController *toController = self.viewControllers[toIndex]; | |
if (animationType == SToolkitTransitionAnimationNone) { | |
toController.view.frame = CGRectMake(0, 0, self.viewControllerTransitionView.bounds.size.width, self.viewControllerTransitionView.bounds.size.height); | |
if (self.selectedViewController && self.selectedViewController == fromController) { | |
[self.selectedViewController removeFromParentViewController]; | |
[self.selectedViewController beginAppearanceTransition:NO animated:NO]; | |
[self.selectedViewController.view removeFromSuperview]; | |
[self.selectedViewController endAppearanceTransition]; | |
} | |
[self addChildViewController:toController]; | |
[toController beginAppearanceTransition:YES animated:NO]; | |
[self.viewControllerTransitionView addSubview:toController.view]; | |
[toController endAppearanceTransition]; | |
} else if (animationType == SToolkitTransitionAnimationPlaceHolder1) { | |
} | |
self.selectedIndex = toIndex; | |
self.selectedViewController = toController; | |
// Queue out transition and execute next one if there is. | |
[self.transitionQueue removeObject:transition]; | |
if (self.transitionQueue.count != 0) { | |
[self executeTransitionWithAnimationType:SToolkitTransitionAnimationNone]; | |
} | |
} | |
- (void)tabControl:(STabControl *)tabControl didSelectTabAtIndex:(NSUInteger)index | |
{ | |
[self selectViewControllerAtIndex:index animationType:SToolkitTransitionAnimationNone]; | |
} | |
#pragma mark - Toolkit Bar Items Update | |
- (void)observeForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context | |
{ | |
if ([keyPath isEqualToString:@"toolkitBarItems"] && object == self.selectedViewController) { | |
self.barItems = self.selectedViewController.toolkitBarItems; | |
self.tookitBar.barItems = self.barItems; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment