From b61cac41a2ef0436f2935be18f5ee6c70192d8e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Gra=CC=88tzer?= Date: Tue, 26 Jun 2012 23:40:06 +0200 Subject: [PATCH] Added close button on a tab support --- Demo/SGTabs.xcodeproj/project.pbxproj | 4 +- Demo/SGTabs/SGAppDelegate.h | 3 +- Demo/SGTabs/SGAppDelegate.m | 7 +++- Demo/SGTabs/SGTabs-Info.plist | 3 +- Demo/SGTabs/SGTabs-Prefix.pch | 1 + Demo/SGTabs/SGViewController.m | 17 ++------- README.md | 4 +- Source/SGTabDefines.h | 6 +-- Source/SGTabView.h | 2 + Source/SGTabView.m | 55 ++++++++++++++++++--------- Source/SGTabsView.m | 39 +++++++++++++++++-- Source/SGTabsViewController.h | 6 +-- Source/SGTabsViewController.m | 20 +++------- 13 files changed, 100 insertions(+), 67 deletions(-) diff --git a/Demo/SGTabs.xcodeproj/project.pbxproj b/Demo/SGTabs.xcodeproj/project.pbxproj index 53ae6b8..c9e1c8f 100644 --- a/Demo/SGTabs.xcodeproj/project.pbxproj +++ b/Demo/SGTabs.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + F4068591159A5BF80001C064 /* SGTabsView.m in Sources */ = {isa = PBXBuildFile; fileRef = F470D2AB1580ECDB00ED3644 /* SGTabsView.m */; }; F450946C15856EC800B7B1C6 /* cross.png in Resources */ = {isa = PBXBuildFile; fileRef = F450946B15856EC800B7B1C6 /* cross.png */; }; F470D27F1580EA5100ED3644 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F470D27E1580EA5100ED3644 /* UIKit.framework */; }; F470D2811580EA5100ED3644 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F470D2801580EA5100ED3644 /* Foundation.framework */; }; @@ -17,7 +18,6 @@ F470D2921580EA5100ED3644 /* SGViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F470D2911580EA5100ED3644 /* SGViewController.m */; }; 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 */; }; F470D2B21580ED3000ED3644 /* SGToolbar.m in Sources */ = {isa = PBXBuildFile; fileRef = F470D2B11580ED3000ED3644 /* SGToolbar.m */; }; /* End PBXBuildFile section */ @@ -204,9 +204,9 @@ F470D28F1580EA5100ED3644 /* SGAppDelegate.m in Sources */, F470D2921580EA5100ED3644 /* SGViewController.m in Sources */, F470D2A91580EAD700ED3644 /* SGTabsViewController.m in Sources */, - F470D2AC1580ECDB00ED3644 /* SGTabsView.m in Sources */, F470D2AF1580ECE700ED3644 /* SGTabView.m in Sources */, F470D2B21580ED3000ED3644 /* SGToolbar.m in Sources */, + F4068591159A5BF80001C064 /* SGTabsView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Demo/SGTabs/SGAppDelegate.h b/Demo/SGTabs/SGAppDelegate.h index addba77..8d2ca1d 100644 --- a/Demo/SGTabs/SGAppDelegate.h +++ b/Demo/SGTabs/SGAppDelegate.h @@ -21,10 +21,11 @@ // #import +#import "SGTabsViewController.h" @class SGTabsViewController; -@interface SGAppDelegate : UIResponder { +@interface SGAppDelegate : UIResponder { } @property (strong, nonatomic) UIWindow *window; diff --git a/Demo/SGTabs/SGAppDelegate.m b/Demo/SGTabs/SGAppDelegate.m index 50307c8..9697c4d 100644 --- a/Demo/SGTabs/SGAppDelegate.m +++ b/Demo/SGTabs/SGAppDelegate.m @@ -23,7 +23,6 @@ #import "SGAppDelegate.h" #import "SGViewController.h" -#import "SGTabsViewController.h" @implementation SGAppDelegate @@ -35,7 +34,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. - self.tabController = [[SGTabsViewController alloc] initEditable:YES]; + self.tabController = [[SGTabsViewController alloc] init]; + self.tabController.delegate = self; self.window.rootViewController = self.tabController; [self.window makeKeyAndVisible]; @@ -53,4 +53,7 @@ - (void)openTab { [self.tabController addTab:vc]; } +- (BOOL)canRemoveTab:(UIViewController *)viewController { + return self.tabController.count > 1; +} @end diff --git a/Demo/SGTabs/SGTabs-Info.plist b/Demo/SGTabs/SGTabs-Info.plist index 377d7ec..745e2df 100644 --- a/Demo/SGTabs/SGTabs-Info.plist +++ b/Demo/SGTabs/SGTabs-Info.plist @@ -26,6 +26,7 @@ UIRequiredDeviceCapabilities + wifi armv7 UISupportedInterfaceOrientations @@ -36,9 +37,9 @@ UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortraitUpsideDown diff --git a/Demo/SGTabs/SGTabs-Prefix.pch b/Demo/SGTabs/SGTabs-Prefix.pch index 4a19c50..a90ff54 100644 --- a/Demo/SGTabs/SGTabs-Prefix.pch +++ b/Demo/SGTabs/SGTabs-Prefix.pch @@ -11,4 +11,5 @@ #ifdef __OBJC__ #import #import +#import "SGTabsViewController.h" #endif diff --git a/Demo/SGTabs/SGViewController.m b/Demo/SGTabs/SGViewController.m index c055612..71c77cd 100644 --- a/Demo/SGTabs/SGViewController.m +++ b/Demo/SGTabs/SGViewController.m @@ -54,14 +54,9 @@ - (void)viewDidLoad UIBarButtonItem *add = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add:)]; - UIBarButtonItem *trash = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash - target:self - action:@selector(remove:)]; - SGTabsViewController *tabs = (SGTabsViewController *) self.parentViewController; - if (tabs.count) - self.toolbarItems = [NSArray arrayWithObjects:space,urlBar,space2,trash,reload,add,nil]; - else - self.toolbarItems = [NSArray arrayWithObjects:space,urlBar,space2,reload,add,nil]; + + + self.toolbarItems = [NSArray arrayWithObjects:space,urlBar,space2,reload,add,nil]; } @@ -109,11 +104,6 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return YES; } - -- (IBAction)remove:(id)sender { - SGTabsViewController *tabs = (SGTabsViewController *) self.parentViewController; - [tabs removeViewController:self]; -} - (IBAction)reload:(id)sender { [self.webView reload]; @@ -129,5 +119,4 @@ - (IBAction)add:(id)sender { [tabs addTab:vc]; } - @end diff --git a/README.md b/README.md index 126c92f..70ee362 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ Tab component for iOS 5+. You can show your ViewControllers in tabs, it is possi # Features - Uses iOS 5 UIViewController Container API - Add and remove tabs on the fly with animations -- User can remove tabs by pressing an X on a tab. TODO +- User can remove tabs, if you allow it. - Dynamically show and hide a UIToolbar at the top - Show the UIBarButtonItems in viewController.toolbarItems in the toolbar -- Enables you to build mobile Safari style Apps. (See demo +- Enables you to build mobile Safari style Apps. (See demo) # Demo A basic web browser with tabs, in around 120 lines of code. diff --git a/Source/SGTabDefines.h b/Source/SGTabDefines.h index e704fbb..aad4c81 100644 --- a/Source/SGTabDefines.h +++ b/Source/SGTabDefines.h @@ -30,13 +30,11 @@ #define kTabsToolbarHeigthFull 44.0 #define kTabsHeigth 35.0 -#define kCornerRadius 7.5 +#define kCornerRadius 5.0 -#define kShadowRadius 6.0 +#define kShadowRadius 5.0 -//[[UIColor alloc] initWithWhite:0.85 alpha:1.0] #define kTabColor [UIColor colorWithRed:168./255. green:172./255. blue:185./255. alpha:1.0] #define kTabUnselectedColor [[UIColor alloc] initWithWhite:0.7 alpha:1.0] -//[[UIColor alloc] initWithWhite:0.6 alpha:1] #endif diff --git a/Source/SGTabView.h b/Source/SGTabView.h index 6e28d0e..0738eb6 100644 --- a/Source/SGTabView.h +++ b/Source/SGTabView.h @@ -26,10 +26,12 @@ @interface SGTabView : UIView { CGSize _tSize; + CGFloat _cap; } @property (nonatomic, strong) NSString *title; @property (nonatomic, strong) UIColor *tabColor; +@property (nonatomic, strong) UIButton *closeButton; - (id)initWithFrame:(CGRect)frame title:(NSString *)title; diff --git a/Source/SGTabView.m b/Source/SGTabView.m index 7b1cc84..f926b63 100644 --- a/Source/SGTabView.m +++ b/Source/SGTabView.m @@ -27,7 +27,7 @@ @interface SGTabView () @property (nonatomic, strong) UILabel *titleLabel; -@property (nonatomic, strong) UIButton *closeButton; + @end @implementation SGTabView @@ -37,13 +37,14 @@ @implementation SGTabView - (id)initWithFrame:(CGRect)frame title:(NSString *)title { - self = [super initWithFrame:frame]; - if (self) { + if (self = [super initWithFrame:frame]) { self.backgroundColor = [UIColor clearColor]; - self.autoresizingMask = UIViewAutoresizingFlexibleWidth; - CGFloat cap = kCornerRadius/frame.size.width; - self.contentStretch = CGRectMake(cap, 0.0, 1.0, 1-cap); self.tabColor = kTabColor; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth; + + _cap = kCornerRadius/frame.size.width; + self.contentStretch = CGRectMake(_cap, 0.0, 1.0, 1-_cap); + self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; self.titleLabel.textAlignment = UITextAlignmentCenter; @@ -55,28 +56,44 @@ - (id)initWithFrame:(CGRect)frame title:(NSString *)title self.titleLabel.textColor = [UIColor darkGrayColor]; self.titleLabel.shadowColor = [UIColor colorWithWhite:0.6 alpha:0.5]; self.titleLabel.shadowOffset = CGSizeMake(0, 0.5); - self.title = title; [self addSubview:self.titleLabel]; - -// self.closeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; -// [self.closeButton setTitle:@"Hello" forState:UIControlStateNormal]; -// [self.closeButton setImage:[UIImage imageNamed:@"cross.png"] -// forState:UIControlStateNormal]; -// [self addSubview:self.closeButton]; + self.closeButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [self.closeButton setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter]; + [self.closeButton setTitle:@"x" forState:UIControlStateNormal]; + [self.closeButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal]; + [self.closeButton setShowsTouchWhenHighlighted:YES]; + self.closeButton.titleLabel.font = [UIFont boldSystemFontOfSize:16.0]; + [self addSubview:self.closeButton]; } return self; } - (void)layoutSubviews { - CGSize t = _tSize; - if (t.width > self.bounds.size.width*0.75) - t.width = self.bounds.size.width*0.75; CGRect b = self.bounds; - self.titleLabel.frame = CGRectMake((b.size.width - t.width)/2, - (b.size.height - t.height)/2, - t.width, t.height); + CGFloat margin = _cap*b.size.width*2; + + CGSize t = _tSize; + if (t.width > b.size.width*0.75) { + t.width = b.size.width*0.75; + + if(!self.closeButton.hidden) { + self.titleLabel.frame = CGRectMake((b.size.width - t.width)/2 - margin, + (b.size.height - t.height)/2, + t.width - margin, t.height); + } else { + self.titleLabel.frame = CGRectMake((b.size.width - t.width)/2, + (b.size.height - t.height)/2, + t.width, t.height); + } + } else { + self.titleLabel.frame = CGRectMake((b.size.width - t.width)/2, + (b.size.height - t.height)/2, + t.width, t.height); + } + + self.closeButton.frame = CGRectMake(b.size.width - 2*margin - 25, 0, 25, b.size.height); } - (void)setTitle:(NSString *)title { diff --git a/Source/SGTabsView.m b/Source/SGTabsView.m index bf4ec55..fd4b00a 100644 --- a/Source/SGTabsView.m +++ b/Source/SGTabsView.m @@ -60,10 +60,11 @@ - (void)layoutSubviews{ - (void)addTab:(NSString *)title { CGFloat width = [self tabWidth:self.tabs.count+1]; + // Float the subview in from rigth CGRect frame = CGRectMake(self.bounds.size.width, 0, width, self.bounds.size.height - kMARGIN); SGTabView *newTab = [[SGTabView alloc] initWithFrame:frame title:title]; - //newTab.closeButton.hidden = !self.tabsController.editing; + // Setup gesture recognizers UITapGestureRecognizer *tapG = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; tapG.numberOfTapsRequired = 1; @@ -76,19 +77,33 @@ - (void)addTab:(NSString *)title { panG.delegate = self; [newTab addGestureRecognizer:panG]; + // Setup close button + [newTab.closeButton addTarget:self action:@selector(handleRemove:) forControlEvents:UIControlEventTouchCancel]; + + // Add the tab _selected = self.tabs.count; [self.tabs addObject:newTab]; [self addSubview:newTab]; for (int i = 0; i < self.tabs.count; i++) { SGTabView *tab = [self.tabs objectAtIndex:i]; + // By setting the real position after the view is added, we create a float from rigth transition tab.frame = CGRectMake(width*i + kMARGIN, 0, width, self.bounds.size.height - kMARGIN); - if (i == self.tabs.count-1) { + if (i == _selected) { + if ([self.tabsController.delegate respondsToSelector:@selector(canRemoveTab:)]) { + tab.closeButton.hidden = ![self.tabsController.delegate canRemoveTab:[self.tabsController.tabContents objectAtIndex:i]]; + } else { + tab.closeButton.hidden = NO; + } + tab.alpha = 1.0; [self bringSubviewToFront:tab]; } else { + tab.closeButton.hidden = YES; tab.alpha = 0.7; + [tab setNeedsLayout]; } + [tab setNeedsDisplay]; } } @@ -107,10 +122,19 @@ - (void)setSelected:(NSUInteger)selected { for (int i = 0; i < self.tabs.count; i++) { SGTabView *tab = [self.tabs objectAtIndex:i]; if (i == selected) { + if ([self.tabsController.delegate respondsToSelector:@selector(canRemoveTab:)]) { + tab.closeButton.hidden = ![self.tabsController.delegate canRemoveTab:[self.tabsController.tabContents objectAtIndex:i]]; + } else { + tab.closeButton.hidden = NO; + } + tab.alpha = 1.0; + [tab setNeedsLayout]; [self bringSubviewToFront:tab]; } else { + tab.closeButton.hidden = YES; tab.alpha = 0.7; + [tab setNeedsLayout]; } [tab setNeedsDisplay]; } @@ -133,7 +157,16 @@ - (void)resizeTabs { } } -#pragma mark - Gestures +#pragma mark - Actions + +- (IBAction)handleRemove:(id)sender { + NSLog(@"%s", __PRETTY_FUNCTION__); + UIView *v = sender; + NSUInteger index = [self.tabs indexOfObject:v.superview]; + if (index != NSNotFound) { + [self.tabsController removeIndex:index]; + } +} - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return NO; diff --git a/Source/SGTabsViewController.h b/Source/SGTabsViewController.h index 54f5ff2..0d7a384 100644 --- a/Source/SGTabsViewController.h +++ b/Source/SGTabsViewController.h @@ -27,29 +27,25 @@ @optional - (void)willShowTab:(UIViewController *)viewController; - (void)willRemoveTab:(UIViewController *)viewController; -- (BOOL)canRemoveTab:(UIViewController *)viewController;// TODO +- (BOOL)canRemoveTab:(UIViewController *)viewController; @end @class SGToolbar, SGTabsView; @interface SGTabsViewController : UIViewController { - BOOL _editable; CGRect _contentFrame; BOOL _toobarVisible; } /// Is an optional delegate @property (nonatomic, weak) id delegate; -@property (nonatomic, readonly) BOOL editable; /// Currently visible view controller @property (nonatomic, readonly, weak) UIViewController *currentViewController; @property (nonatomic, readonly, strong) NSMutableArray *tabContents; -- (id)initEditable:(BOOL)editable; - /// Adds a tab, don't add the same instance twice! - (void)addTab:(UIViewController *)viewController; diff --git a/Source/SGTabsViewController.m b/Source/SGTabsViewController.m index d3a3842..3fe1ee9 100644 --- a/Source/SGTabsViewController.m +++ b/Source/SGTabsViewController.m @@ -38,20 +38,12 @@ - (void)removeViewController:(UIViewController *)viewController index:(NSUIntege @end @implementation SGTabsViewController -@synthesize delegate, editable = _editable; +@synthesize delegate; @synthesize tabContents = _tabContents, currentViewController = _currentViewController; @synthesize headerView = _headerView, tabsView = _tabsView, toolbar = _toolbar; -- (id)initEditable:(BOOL)editable { +- (id)init { if (self = [super initWithNibName:nil bundle:nil]) { - _editable = editable; - } - return self; -} - -- (id)initWithCoder:(NSCoder *)aDecoder { - if (self = [super initWithCoder:aDecoder]) { - _editable = NO; } return self; } @@ -172,13 +164,13 @@ - (void)showViewController:(UIViewController *)viewController index:(NSUInteger) if (_toobarVisible) [self.toolbar setItems:viewController.toolbarItems animated:YES]; + viewController.view.frame = _contentFrame; [self transitionFromViewController:self.currentViewController toViewController:viewController duration:0 - options:UIViewAnimationOptionAllowAnimatedContent + options:0 animations:^{ self.tabsView.selected = index; - viewController.view.frame = _contentFrame; } completion:^(BOOL finished) { _currentViewController = viewController; @@ -208,7 +200,7 @@ - (void)removeViewController:(UIViewController *)viewController index:(NSUIntege _currentViewController = nil; [UIView transitionWithView:self.tabsView duration:kRemoveTabDuration - options:0 + options:UIViewAnimationOptionAllowAnimatedContent animations:^{ [viewController viewWillDisappear:NO]; [viewController.view removeFromSuperview]; @@ -299,7 +291,7 @@ - (BOOL)toolbarHidden { #pragma mark - Propertys - (NSUInteger)maxCount { - return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? 8 : 4; + return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? 7 : 4; } - (NSUInteger)count {