diff --git a/Demo/SGTabs.xcodeproj/project.pbxproj b/Demo/SGTabs.xcodeproj/project.pbxproj index 40f0460..87a7d63 100644 --- a/Demo/SGTabs.xcodeproj/project.pbxproj +++ b/Demo/SGTabs.xcodeproj/project.pbxproj @@ -14,7 +14,7 @@ F470D28B1580EA5100ED3644 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F470D28A1580EA5100ED3644 /* main.m */; }; F470D28F1580EA5100ED3644 /* SGAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F470D28E1580EA5100ED3644 /* SGAppDelegate.m */; }; F470D2921580EA5100ED3644 /* SGViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F470D2911580EA5100ED3644 /* SGViewController.m */; }; - F470D2951580EA5100ED3644 /* SGViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = F470D2931580EA5100ED3644 /* SGViewController_iPhone.xib */; }; + F470D2951580EA5100ED3644 /* SGViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F470D2931580EA5100ED3644 /* SGViewController.xib */; }; F470D2A91580EAD700ED3644 /* SGTabsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F470D2A81580EAD700ED3644 /* SGTabsViewController.m */; }; F470D2AC1580ECDB00ED3644 /* SGTabsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F470D2AB1580ECDB00ED3644 /* SGTabsView.m */; }; F470D2AF1580ECE700ED3644 /* SGTabView.m in Sources */ = {isa = PBXBuildFile; fileRef = F470D2AE1580ECE700ED3644 /* SGTabView.m */; }; @@ -34,7 +34,7 @@ F470D28E1580EA5100ED3644 /* SGAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SGAppDelegate.m; sourceTree = ""; }; F470D2901580EA5100ED3644 /* SGViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SGViewController.h; sourceTree = ""; }; F470D2911580EA5100ED3644 /* SGViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SGViewController.m; sourceTree = ""; }; - F470D2941580EA5100ED3644 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/SGViewController_iPhone.xib; sourceTree = ""; }; + F470D2941580EA5100ED3644 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/SGViewController.xib; sourceTree = ""; }; F470D2A71580EAD700ED3644 /* SGTabsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SGTabsViewController.h; sourceTree = ""; }; F470D2A81580EAD700ED3644 /* SGTabsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SGTabsViewController.m; sourceTree = ""; }; F470D2AA1580ECDB00ED3644 /* SGTabsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SGTabsView.h; sourceTree = ""; }; @@ -102,7 +102,7 @@ F470D28E1580EA5100ED3644 /* SGAppDelegate.m */, F470D2901580EA5100ED3644 /* SGViewController.h */, F470D2911580EA5100ED3644 /* SGViewController.m */, - F470D2931580EA5100ED3644 /* SGViewController_iPhone.xib */, + F470D2931580EA5100ED3644 /* SGViewController.xib */, F476BFBE15816C8E00BABEEB /* Source */, F470D2851580EA5100ED3644 /* Supporting Files */, ); @@ -206,7 +206,7 @@ buildActionMask = 2147483647; files = ( F470D2891580EA5100ED3644 /* InfoPlist.strings in Resources */, - F470D2951580EA5100ED3644 /* SGViewController_iPhone.xib in Resources */, + F470D2951580EA5100ED3644 /* SGViewController.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -238,12 +238,12 @@ name = InfoPlist.strings; sourceTree = ""; }; - F470D2931580EA5100ED3644 /* SGViewController_iPhone.xib */ = { + F470D2931580EA5100ED3644 /* SGViewController.xib */ = { isa = PBXVariantGroup; children = ( F470D2941580EA5100ED3644 /* en */, ); - name = SGViewController_iPhone.xib; + name = SGViewController.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ diff --git a/Demo/SGTabs/SGAppDelegate.h b/Demo/SGTabs/SGAppDelegate.h index 2793508..5d1a06b 100644 --- a/Demo/SGTabs/SGAppDelegate.h +++ b/Demo/SGTabs/SGAppDelegate.h @@ -30,6 +30,6 @@ @property (strong, nonatomic) UIWindow *window; -@property (strong, nonatomic) SGTabsViewController *viewController; +@property (strong, nonatomic) SGTabsViewController *tabController; @end diff --git a/Demo/SGTabs/SGAppDelegate.m b/Demo/SGTabs/SGAppDelegate.m index 795e04d..20977bd 100644 --- a/Demo/SGTabs/SGAppDelegate.m +++ b/Demo/SGTabs/SGAppDelegate.m @@ -22,12 +22,13 @@ #import "SGAppDelegate.h" +#import "SGViewController.h" #import "SGTabsViewController.h" @implementation SGAppDelegate @synthesize window = _window; -@synthesize viewController = _viewController; +@synthesize tabController = _tabController; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { @@ -39,47 +40,24 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // self.viewController = [[SGViewController alloc] initWithNibName:@"SGViewController_iPad" bundle:nil]; // } - self.viewController = [[SGTabsViewController alloc] initWithNibName:nil bundle:nil]; - self.window.rootViewController = self.viewController; + self.tabController = [[SGTabsViewController alloc] initEditable:YES]; + self.window.rootViewController = self.tabController; [self.window makeKeyAndVisible]; - [self.viewController addBlankTab:@"Hello World"]; + [self openTab]; timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(openTab) userInfo:nil repeats:YES]; return YES; } - (void)openTab { - if (self.viewController.maxTabs > self.viewController.count) { - [self.viewController addBlankTab:@"Hello Tab"]; + if (3 > self.tabController.count) { // You can add up to tabController.maxCount Tabs + SGViewController *vc = [[SGViewController alloc] + initWithNibName:NSStringFromClass([SGViewController class]) + bundle:nil]; + vc.title = [NSString stringWithFormat:@"Tab %i content", self.tabController.count+1]; + [self.tabController addTab:vc]; } } -- (void)applicationWillResignActive:(UIApplication *)application -{ - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. -} - -- (void)applicationDidEnterBackground:(UIApplication *)application -{ - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. -} - -- (void)applicationWillEnterForeground:(UIApplication *)application -{ - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. -} - -- (void)applicationDidBecomeActive:(UIApplication *)application -{ - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. -} - -- (void)applicationWillTerminate:(UIApplication *)application -{ - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. -} - @end diff --git a/Demo/SGTabs/SGViewController.h b/Demo/SGTabs/SGViewController.h index 3745214..b55b64a 100644 --- a/Demo/SGTabs/SGViewController.h +++ b/Demo/SGTabs/SGViewController.h @@ -22,9 +22,8 @@ #import -@class SGTabsViewController; -@interface SGViewController : UIViewController { - SGTabsViewController *inner; -} +@interface SGViewController : UIViewController + +@property (weak, nonatomic) IBOutlet UILabel *label; @end diff --git a/Demo/SGTabs/SGViewController.m b/Demo/SGTabs/SGViewController.m index a067321..b48d9f9 100644 --- a/Demo/SGTabs/SGViewController.m +++ b/Demo/SGTabs/SGViewController.m @@ -27,15 +27,18 @@ @interface SGViewController () @end @implementation SGViewController +@synthesize label; - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. + self.label.text = self.title; } - (void)viewDidUnload { + [self setLabel:nil]; [super viewDidUnload]; // Release any retained subviews of the main view. } diff --git a/Demo/SGTabs/en.lproj/SGViewController.xib b/Demo/SGTabs/en.lproj/SGViewController.xib index b1c1fbf..78cf45e 100644 --- a/Demo/SGTabs/en.lproj/SGViewController.xib +++ b/Demo/SGTabs/en.lproj/SGViewController.xib @@ -1,18 +1,19 @@ - 1280 - 11C25 - 1919 - 1138.11 - 566.00 + 1296 + 11E53 + 2182 + 1138.47 + 569.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 916 + 1181 IBProxyObject IBUIView + IBUILabel com.apple.InterfaceBuilder.IBCocoaTouchPlugin @@ -33,15 +34,42 @@ 274 + + + + 274 + {{77, 83}, {167, 142}} + + _NS:9 + NO + YES + 7 + NO + IBCocoaTouchFramework + Label + + 1 + MCAwIDAAA + + + 0 + 10 + + 1 + 35 + + + Helvetica + 35 + 16 + + + {{0, 20}, {320, 460}} - 3 - MC43NQA - - 2 - + MQA NO @@ -58,6 +86,14 @@ 7 + + + label + + + + 10 + @@ -81,8 +117,16 @@ 6 + + + + + 9 + + + @@ -91,29 +135,23 @@ UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 7 - - - - - SGViewController - UIViewController - - IBProjectSource - ./Classes/SGViewController.h - - - + 10 + 0 IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + YES 3 - 916 + 1181 diff --git a/README.md b/README.md index c2fa2bd..a41f199 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,16 @@ I provider my code under the Apache Licence. Credits go to [Fictorial LLC][https://github.com/fictorial/BHTabBar], my tab control implementation is spired by BHTabBar - Copyright 2012 Simon Grätzer + Copyright 2012 Simon Grätzer - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Source/SGTabView.h b/Source/SGTabView.h index a535f2b..c62fa97 100644 --- a/Source/SGTabView.h +++ b/Source/SGTabView.h @@ -29,7 +29,7 @@ } @property (nonatomic, strong) UILabel *titleLabel; -@property (nonatomic, assign) NSUInteger position; +@property (nonatomic, strong) UIButton *closeButton; @property (nonatomic, assign) BOOL editable; - (id)initWithFrame:(CGRect)frame title:(NSString *)title; diff --git a/Source/SGTabView.m b/Source/SGTabView.m index e8b0ddb..a038750 100644 --- a/Source/SGTabView.m +++ b/Source/SGTabView.m @@ -24,16 +24,16 @@ #import #define kRadius 10.0 -#define kMargin 0.0 +#define kMargin 2*kRadius @implementation SGTabView -@synthesize titleLabel, position, editable; +@synthesize titleLabel, editable, closeButton; - (CGRect)tabRect { - return CGRectMake(self.bounds.origin.x + kMargin, + return CGRectMake(self.bounds.origin.x, self.bounds.origin.y, - self.bounds.size.width - kMargin, + self.bounds.size.width, self.bounds.size.height); } @@ -44,7 +44,7 @@ - (id)initWithFrame:(CGRect)frame title:(NSString *)title self.backgroundColor = [UIColor clearColor]; self.autoresizesSubviews = UIViewAutoresizingFlexibleWidth; - self.titleLabel = [[UILabel alloc] initWithFrame:[self tabRect]]; + self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; self.titleLabel.text = title; self.titleLabel.textAlignment = UITextAlignmentCenter; self.titleLabel.lineBreakMode = UILineBreakModeTailTruncation; @@ -57,13 +57,24 @@ - (id)initWithFrame:(CGRect)frame title:(NSString *)title [self addSubview:self.titleLabel]; topColor = [[UIColor alloc] initWithWhite:0.9 alpha:1]; bottomColor = [[UIColor alloc] initWithWhite:0.8 alpha:1]; + + self.closeButton = [UIButton buttonWithType:UIButtonTypeCustom]; +// [self.closeButton setImage:[UIImage imageNamed:@"button_close"] +// forState:UIControlStateNormal]; } return self; } +- (void)layoutSubviews { + CGRect inner = [self tabRect]; + self.titleLabel.frame = inner; + CGSize size = self.closeButton.frame.size; + self.closeButton.center = CGPointMake(inner.size.width - size.width, + inner.size.height/2); +} - (void)drawRect:(CGRect)rect { - CGRect tabRect = [self tabRect]; + CGRect tabRect = self.bounds; CGFloat tabLeft = tabRect.origin.x; CGFloat tabRight = tabRect.origin.x + tabRect.size.width; CGFloat tabTop = tabRect.origin.y; diff --git a/Source/SGTabsView.h b/Source/SGTabsView.h index 17d1a15..95dd620 100644 --- a/Source/SGTabsView.h +++ b/Source/SGTabsView.h @@ -24,12 +24,9 @@ @class SGTabsViewController; -@interface SGTabsView : UIView { - CGPoint _original; -} - +@interface SGTabsView : UIView @property (nonatomic, weak) SGTabsViewController *tabsController; -@property (nonatomic, strong) NSMutableArray *tabs; +@property (nonatomic, readonly) NSMutableArray *tabs; @property (nonatomic, assign) NSUInteger selected; - (void)addTab:(NSString *)title; diff --git a/Source/SGTabsView.m b/Source/SGTabsView.m index a9a8941..5ef2540 100644 --- a/Source/SGTabsView.m +++ b/Source/SGTabsView.m @@ -46,7 +46,7 @@ - (id)initWithFrame:(CGRect)frame - (NSMutableArray *)tabs { if (!_tabs) { - _tabs = [[NSMutableArray alloc] initWithCapacity:[self.tabsController maxTabs]]; + _tabs = [[NSMutableArray alloc] initWithCapacity:[self.tabsController maxCount]]; } return _tabs; } @@ -57,15 +57,18 @@ - (void)addTab:(NSString *)title { CGRect frame = CGRectMake(self.bounds.size.width, 0, width, self.bounds.size.height - kMARGIN); SGTabView *newTab = [[SGTabView alloc] initWithFrame:frame title:title]; - newTab.position = self.tabs.count; newTab.editable = self.tabsController.editable; - UITapGestureRecognizer *tapG = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; + UITapGestureRecognizer *tapG = [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(handleTap:)]; tapG.numberOfTapsRequired = 1; tapG.numberOfTouchesRequired = 1; + tapG.delegate = self; [newTab addGestureRecognizer:tapG]; - UIPanGestureRecognizer *panG = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; + UIPanGestureRecognizer *panG = [[UIPanGestureRecognizer alloc] initWithTarget:self + action:@selector(handlePan:)]; + panG.delegate = self; [newTab addGestureRecognizer:panG]; // CGFloat cap = 7.5/width; @@ -73,50 +76,45 @@ - (void)addTab:(NSString *)title { _selected = self.tabs.count; [self.tabs addObject:newTab]; - [UIView transitionWithView:self - duration:0.5 - options:UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionCurveEaseInOut - animations:^{ +// [UIView transitionWithView:self +// duration:0.5 +// options:UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionCurveEaseInOut +// animations:^{ [self addSubview:newTab]; - for (SGTabView *tab in self.tabs) { - tab.frame = CGRectMake(width*tab.position + kMARGIN, 0, width, self.bounds.size.height - kMARGIN); - if (tab.position == newTab.position) { + for (int i = 0; i < self.tabs.count; i++) { + SGTabView *tab = [self.tabs objectAtIndex:i]; + tab.frame = CGRectMake(width*i + kMARGIN, 0, width, self.bounds.size.height - kMARGIN); + if (i == self.tabs.count-1) { tab.alpha = 1.0; [self bringSubviewToFront:tab]; } else tab.alpha = 0.6; } - } - completion:NULL]; +// } +// completion:NULL]; } - (void)removeTab:(NSUInteger)index { SGTabView *oldTab = [self.tabs objectAtIndex:index]; - [self.tabs removeObject:oldTab]; - CGFloat width = [self tabWidth:self.tabs.count]; - - [UIView transitionWithView:self - duration:0.5 - options:UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionCurveEaseInOut - animations:^{ - [oldTab removeFromSuperview]; - for (SGTabView *tab in self.tabs) { - if (tab.position > index) { - tab.position--; - } - tab.frame = CGRectMake((width)*tab.position + kMARGIN, 0, width, self.bounds.size.height - kMARGIN); - } - - } - completion:NULL]; + if (oldTab) { + [self.tabs removeObjectAtIndex:index]; + [UIView transitionWithView:self + duration:0.5 + options:UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionCurveEaseInOut + animations:^{ + [oldTab removeFromSuperview]; + [self resizeTabs]; + } + completion:NULL]; + } } - (void)setSelected:(NSUInteger)selected { if (_selected != selected) { _selected = selected; - NSUInteger selectedPosition = [[self.tabs objectAtIndex:selected] position]; - for (SGTabView *tab in self.tabs) { - if (tab.position == selectedPosition) { + for (int i = 0; i < self.tabs.count; i++) { + SGTabView *tab = [self.tabs objectAtIndex:i]; + if (i == selected) { tab.alpha = 1.0; [self bringSubviewToFront:tab]; } else { @@ -128,7 +126,6 @@ - (void)setSelected:(NSUInteger)selected { } #pragma mark - Helpers - - (CGFloat)tabWidth:(NSUInteger)count { CGFloat width; if (count > 0) @@ -141,24 +138,31 @@ - (CGFloat)tabWidth:(NSUInteger)count { - (void)resizeTabs { CGFloat width = [self tabWidth:self.tabs.count]; - for (SGTabView *tab in self.tabs) - tab.frame = CGRectMake(width*tab.position + kMARGIN, 0, width, self.bounds.size.height - kMARGIN); + for (int i = 0; i < self.tabs.count; i++) { + SGTabView *tab = [self.tabs objectAtIndex:i]; + tab.frame = CGRectMake(width*i + kMARGIN, 0, width, self.bounds.size.height - kMARGIN); + } } #pragma mark - Gestures +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { + return NO; +} + - (void)handleTap:(UITapGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateEnded) { - NSUInteger index = [self.tabs indexOfObject:sender.view]; - [self.tabsController showTab:index]; + SGTabView *tab = (SGTabView *)sender.view; + [self.tabsController showIndex:[self.tabs indexOfObject:tab]]; } } - (void)handlePan:(UIPanGestureRecognizer *)sender { SGTabView *panTab = (SGTabView *)sender.view; + NSUInteger panPosition = [self.tabs indexOfObject:panTab]; if (sender.state == UIGestureRecognizerStateBegan) { - [self.tabsController showTab:[self.tabs indexOfObject:panTab]]; + [self.tabsController showIndex:panPosition]; } else if (sender.state == UIGestureRecognizerStateChanged) { CGPoint position = [sender translationInView:self]; CGPoint center = CGPointMake(sender.view.center.x + position.x, sender.view.center.y); @@ -170,23 +174,18 @@ - (void)handlePan:(UIPanGestureRecognizer *)sender { CGFloat width = [self tabWidth:self.tabs.count]; // If more than half the tab width is moved, switch the positions - if (abs(center.x - width*panTab.position - width/2) > width/2) { + if (abs(center.x - width*panPosition - width/2) > width/2) { + NSUInteger nextPos = position.x > 0 ? panPosition+1 : panPosition-1; + if (nextPos >= self.tabs.count) + return; - NSUInteger nextPos = position.x > 0 ? panTab.position+1 : panTab.position-1; - SGTabView *next; - // Search the tab on the position next to the dragged one - for (SGTabView *tab in self.tabs) { - if (tab.position == nextPos) { - next = tab; - break; - } - } + SGTabView *next = [self.tabs objectAtIndex:nextPos]; if (next) { - next.position = panTab.position; - panTab.position = nextPos; + [self.tabs exchangeObjectAtIndex:panPosition withObjectAtIndex:nextPos]; + [self.tabsController.tabContents exchangeObjectAtIndex:panPosition withObjectAtIndex:nextPos]; - [UIView animateWithDuration:0.5 animations:^{ - next.frame = CGRectMake(width*next.position + kMARGIN, 0, width, self.bounds.size.height - kMARGIN); + [UIView animateWithDuration:0.5 animations:^{// Move the item on the old position of the panTab + next.frame = CGRectMake(width*panPosition + kMARGIN, 0, width, self.bounds.size.height - kMARGIN); }]; } } diff --git a/Source/SGTabsViewController.h b/Source/SGTabsViewController.h index 332a34b..9b27145 100644 --- a/Source/SGTabsViewController.h +++ b/Source/SGTabsViewController.h @@ -25,38 +25,43 @@ @protocol SGTabsViewControllerDelegate @optional -- (void)willShowTab:(NSUInteger)index; -- (void)willCloseTab:(NSUInteger)index; -- (UIViewController*)blankTabContent; +- (void)willShowTab:(UIViewController *)viewController; +- (void)willRemoveTab:(UIViewController *)viewController; +- (BOOL)canRemoveTab:(UIViewController *)viewController;// TODO @end @class SGTabsTopView, SGTabsView; @interface SGTabsViewController : UIViewController { - SGTabsTopView *_topView; - SGTabsView *_tabsView; BOOL _editable; + CGRect _contentFrame; } +/// Is an optional delegate @property (nonatomic, weak) id delegate; @property (nonatomic, readonly) BOOL editable; -@property (nonatomic, strong) SGTabsView *tabsView; +/// Currently visible view controller +@property (nonatomic, readonly) UIViewController *currentViewController; + +@property (nonatomic, readonly) NSMutableArray *tabContents; - (id)initEditable:(BOOL)editable; -/** Adds a blank tab - * Reurns the id of the tab - */ -- (NSUInteger)addBlankTab:(NSString *)title; -- (void)addTabwithContent:(UIViewController *)viewController; +/// Adds a tab, don't add the same instance twice! +- (void)addTab:(UIViewController *)viewController; -- (void)showTab:(NSUInteger)index; +/// Bring a tab to the frontpage +- (void)showViewController:(UIViewController *)viewController; +/// Primarily intended for internal use +- (void)showIndex:(NSUInteger)index; -- (void)removeTab:(NSUInteger)index; +- (void)removeViewController:(UIViewController *)viewController; +/// Primarily intended for internal use +- (void)removeIndex:(NSUInteger)index; - (NSUInteger)count; -- (NSUInteger)maxTabs; +- (NSUInteger)maxCount; @end diff --git a/Source/SGTabsViewController.m b/Source/SGTabsViewController.m index e6eeb0a..33daaf4 100644 --- a/Source/SGTabsViewController.m +++ b/Source/SGTabsViewController.m @@ -25,33 +25,36 @@ #import "SGTabsView.h" #import "SGTabView.h" -#define kTabsTopViewHeigth 7.5 -#define kTabsHeigth 35.0 +#define kTabsTopViewHeigth 20.0 +#define kTabsHeigth 30.0 @interface SGTabsViewController () +@property (nonatomic, strong) UIView *headerView; +@property (nonatomic, strong) SGTabsView *tabsView; +@property (nonatomic, strong) SGTabsTopView *topView; + +- (void)showViewController:(UIViewController *)viewController index:(NSUInteger)index; +- (void)removeViewController:(UIViewController *)viewController index:(NSUInteger)index; @end @implementation SGTabsViewController -@synthesize delegate, editable = _editable, tabsView = _tabsView; +@synthesize delegate, editable = _editable; +@synthesize tabContents = _tabContents, currentViewController = _currentViewController; +@synthesize headerView = _headerView, tabsView = _tabsView, topView = _topView; - (id)initEditable:(BOOL)editable { - if (self = [super init]) { + if (self = [super initWithNibName:nil bundle:nil]) { _editable = editable; } return self; } -- (void)viewDidLoad -{ - [super viewDidLoad]; - // Do any additional setup after loading the view. -} - -- (void)viewDidUnload -{ - [super viewDidUnload]; - // Release any retained subviews of the main view. +- (id)initWithCoder:(NSCoder *)aDecoder { + if (self = [super initWithCoder:aDecoder]) { + _editable = NO; + } + return self; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation @@ -64,46 +67,161 @@ - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interface - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { [self.tabsView resizeTabs]; + CGRect head = self.headerView.frame; + CGRect bounds = self.view.bounds; + _contentFrame = CGRectMake(bounds.origin.x, + bounds.origin.y + head.size.height, + bounds.size.width, + bounds.size.height - head.size.height); } - (void)loadView { [super loadView]; - self.view.backgroundColor = [UIColor whiteColor]; - + self.view.backgroundColor = [UIColor scrollViewTexturedBackgroundColor]; CGRect bounds = self.view.bounds; - CGRect frame = CGRectMake(bounds.origin.x, bounds.origin.y, bounds.size.width, kTabsTopViewHeigth); + CGRect head = CGRectMake(bounds.origin.x, bounds.origin.y, bounds.size.width, kTabsTopViewHeigth + kTabsHeigth); + self.headerView = [[UIView alloc] initWithFrame:head]; + self.headerView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + + CGRect frame = CGRectMake(head.origin.x, head.origin.y, head.size.width, kTabsTopViewHeigth); _topView = [[SGTabsTopView alloc] initWithFrame:frame]; - frame = CGRectMake(bounds.origin.x, kTabsTopViewHeigth, bounds.size.width, kTabsHeigth); + frame = CGRectMake(head.origin.x, kTabsTopViewHeigth, head.size.width, kTabsHeigth); _tabsView = [[SGTabsView alloc] initWithFrame:frame]; _tabsView.tabsController = self; - [self.view addSubview:_topView]; - [self.view addSubview:_tabsView]; + [self.headerView addSubview:_topView]; + [self.headerView addSubview:_tabsView]; + [self.view addSubview:self.headerView]; + + _contentFrame = CGRectMake(bounds.origin.x, + bounds.origin.y + head.size.height, + bounds.size.width, + bounds.size.height - head.size.height); } -- (NSUInteger)addBlankTab:(NSString *)title { - [self.tabsView addTab:title]; - return [self.tabsView.tabs count]; +- (UIView *)rotatingHeaderView { + return self.headerView; } -- (void)addTabwithContent:(UIViewController *)viewController { +#pragma mark - Tab stuff + +- (void)addTab:(UIViewController *)viewController { + if ([self.delegate respondsToSelector:@selector(willShowTab:)]) { + [self.delegate willShowTab:viewController]; + } + if (![self.childViewControllers containsObject:viewController] && self.count < self.maxCount - 1) { + [self addChildViewController:viewController]; + viewController.view.frame = _contentFrame; + + // Add tab selects automatically the new tab + [UIView transitionWithView:self.view + duration:0.5 + options:UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self.tabsView addTab:viewController.title]; + + if (self.currentViewController) { + [self.currentViewController viewWillDisappear:YES]; + [self.currentViewController.view removeFromSuperview]; + [self.currentViewController viewDidDisappear:YES]; + } + + [self.view addSubview:viewController.view]; + } + completion:^(BOOL finished){ + [viewController didMoveToParentViewController:self]; + _currentViewController = viewController; + }]; + } } -- (void)showTab:(NSUInteger)index { + +- (void)showViewController:(UIViewController *)viewController index:(NSUInteger)index { + if (viewController == self.currentViewController) { + return; + } + + if ([self.delegate respondsToSelector:@selector(willShowTab:)]) { + [self.delegate willShowTab:viewController]; + } + self.tabsView.selected = index; + [self transitionFromViewController:self.currentViewController + toViewController:viewController + duration:0.3 + options:UIViewAnimationOptionTransitionCrossDissolve + animations:^{ + viewController.view.frame = _contentFrame; + } + completion:^(BOOL finished) { + _currentViewController = viewController; + }]; + _currentViewController = viewController; } -- (void)removeTab:(NSUInteger)index { +- (void)showIndex:(NSUInteger)index; { + UIViewController *viewController = [self.tabContents objectAtIndex:index]; + [self showViewController:viewController index:index]; +} + +- (void)showViewController:(UIViewController *)viewController { + NSUInteger index = [self.tabContents indexOfObject:viewController]; + [self showViewController:viewController index:index]; +} + +- (void)removeViewController:(UIViewController *)viewController index:(NSUInteger)index { + if ([self.delegate respondsToSelector:@selector(willRemoveTab:)]) { + [self.delegate willRemoveTab:viewController]; + } + + [self.tabsView removeTab:index]; + if (index < self.count - 1) + index++; + else if (index > 0) + index--; + else { + [viewController willMoveToParentViewController:nil]; + [viewController.view removeFromSuperview]; + [viewController removeFromParentViewController]; + _currentViewController = nil; + return; + } + + self.tabsView.selected = index; + UIViewController *to = [self.tabContents objectAtIndex:index]; + [viewController willMoveToParentViewController:nil]; + [self transitionFromViewController:viewController + toViewController:to + duration: viewController == self.currentViewController ? 0.5 : 0 + options:UIViewAnimationOptionTransitionCrossDissolve + animations:^{ + to.view.frame = _contentFrame; + } + completion:^(BOOL finished) { + [viewController removeFromParentViewController]; + _currentViewController = to; + }]; + +} + +- (void)removeViewController:(UIViewController *)viewController { + NSUInteger index = [self.tabContents indexOfObject:viewController]; + [self removeViewController:viewController index:index]; +} + +- (void)removeIndex:(NSUInteger)index { + UIViewController *viewController = [self.tabContents objectAtIndex:index]; + [self removeViewController:viewController index:index]; } #pragma mark - Propertys -- (NSUInteger)maxTabs { +- (NSUInteger)maxCount { return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? 6 : 3; } @@ -111,4 +229,16 @@ - (NSUInteger)count { return self.tabsView.tabs.count; } +- (NSMutableArray *)tabContents { + if (!_tabContents) { + _tabContents = [[NSMutableArray alloc] initWithCapacity:self.maxCount]; + } + return _tabContents; +} + +- (void)addChildViewController:(UIViewController *)childController { + [self.tabContents addObject:childController]; + [super addChildViewController:childController]; +} + @end