Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Appearance customization #6

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
0B0F9FFB161DAF34000B7AD7 /* MHTabBarController-Protected.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MHTabBarController-Protected.h"; sourceTree = "<group>"; };
7BCE96EA1469751600135C63 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
7BCE96EE1469751600135C63 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
7BCE96F01469751600135C63 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -90,6 +91,7 @@
7BCE970B146975EA00135C63 /* ListViewController.h */,
7BCE970C146975EA00135C63 /* ListViewController.m */,
7BCE97001469751600135C63 /* MHTabBarController.h */,
0B0F9FFB161DAF34000B7AD7 /* MHTabBarController-Protected.h */,
7BCE97011469751600135C63 /* MHTabBarController.m */,
7BCE96F51469751600135C63 /* Supporting Files */,
);
Expand Down
47 changes: 47 additions & 0 deletions Demo/MHTabBarController-Protected.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*!
* \file MHTabBarController-Protected.h
*
* Copyright (c) 2011 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "MHTabBarController.h"

@interface MHTabBarController ()

@property(nonatomic, strong) IBOutlet UIView *tabButtonsContainerView;
@property(nonatomic, strong) IBOutlet UIView *contentContainerView;
@property(nonatomic, strong) IBOutlet UIImageView *indicatorImageView;

//Tab Customization
@property(nonatomic, strong) UIFont *tabTitleFont;

@property(nonatomic, assign) CGSize tabShadowOffset;

@property(nonatomic, strong) UIImage *tabInactiveBackgroundImage;
@property(nonatomic, strong) UIImage *tabActiveBackgroundImage;

@property(nonatomic, strong) UIColor *tabInactiveTitleColor;
@property(nonatomic, strong) UIColor *tabActiveTitleColor;

@property(nonatomic, strong) UIColor *tabInactiveShadowColor;
@property(nonatomic, strong) UIColor *tabActiveShadowColor;


@end
189 changes: 114 additions & 75 deletions Demo/MHTabBarController.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,54 +23,46 @@
*/

#import "MHTabBarController.h"
#import "MHTabBarController-Protected.h"

static const float TAB_BAR_HEIGHT = 44.0f;
static const NSInteger TAG_OFFSET = 1000;

@implementation MHTabBarController
{
UIView *tabButtonsContainerView;
UIView *contentContainerView;
UIImageView *indicatorImageView;
}

@synthesize viewControllers = _viewControllers;
@synthesize selectedIndex = _selectedIndex;
@synthesize delegate = _delegate;
@synthesize viewControllers;
@synthesize selectedIndex;
@synthesize delegate;
@synthesize tabButtonsContainerView;
@synthesize contentContainerView;
@synthesize indicatorImageView;

@synthesize tabTitleFont;
@synthesize tabShadowOffset;
@synthesize tabInactiveBackgroundImage;
@synthesize tabActiveBackgroundImage;
@synthesize tabInactiveTitleColor;
@synthesize tabActiveTitleColor;
@synthesize tabInactiveShadowColor;
@synthesize tabActiveShadowColor;

- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
tabShadowOffset = CGSizeMake(0, 1);
}
return self;
}

- (void)centerIndicatorOnButton:(UIButton *)button
{
CGRect rect = indicatorImageView.frame;
rect.origin.x = button.center.x - floorf(indicatorImageView.frame.size.width/2.0f);
rect.origin.y = TAB_BAR_HEIGHT - indicatorImageView.frame.size.height;
CGPoint buttonCenter = [[indicatorImageView superview] convertPoint:button.center fromView:button.superview];
rect.origin.x = buttonCenter.x - floorf(indicatorImageView.frame.size.width/2.0f);
indicatorImageView.frame = rect;
indicatorImageView.hidden = NO;
}

- (void)selectTabButton:(UIButton *)button
{
[button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];

UIImage *image = [[UIImage imageNamed:@"MHTabBarActiveTab"] stretchableImageWithLeftCapWidth:0 topCapHeight:0];
[button setBackgroundImage:image forState:UIControlStateNormal];
[button setBackgroundImage:image forState:UIControlStateHighlighted];

[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button setTitleShadowColor:[UIColor colorWithWhite:0.0f alpha:0.5f] forState:UIControlStateNormal];
}

- (void)deselectTabButton:(UIButton *)button
{
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

UIImage *image = [[UIImage imageNamed:@"MHTabBarInactiveTab"] stretchableImageWithLeftCapWidth:1 topCapHeight:0];
[button setBackgroundImage:image forState:UIControlStateNormal];
[button setBackgroundImage:image forState:UIControlStateHighlighted];

[button setTitleColor:[UIColor colorWithRed:175/255.0f green:85/255.0f blue:58/255.0f alpha:1.0f] forState:UIControlStateNormal];
[button setTitleShadowColor:[UIColor whiteColor] forState:UIControlStateNormal];
}

- (void)removeTabButtons
{
NSArray *buttons = [tabButtonsContainerView subviews];
Expand All @@ -86,10 +78,27 @@ - (void)addTabButtons
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = TAG_OFFSET + index;
[button setTitle:viewController.title forState:UIControlStateNormal];
[button addTarget:self action:@selector(tabButtonPressed:) forControlEvents:UIControlEventTouchDown];
button.titleLabel.font = [UIFont boldSystemFontOfSize:18];
button.titleLabel.shadowOffset = CGSizeMake(0, 1);
[self deselectTabButton:button];
[button addTarget:self action:@selector(tabButtonPressed:) forControlEvents:UIControlEventTouchUpInside];

button.titleLabel.font = tabTitleFont;
button.titleLabel.shadowOffset = tabShadowOffset;

[button setBackgroundImage:tabInactiveBackgroundImage forState:UIControlStateNormal];
[button setBackgroundImage:tabActiveBackgroundImage forState:UIControlStateHighlighted];
[button setBackgroundImage:tabActiveBackgroundImage forState:UIControlStateSelected];
[button setBackgroundImage:tabActiveBackgroundImage forState:UIControlStateHighlighted | UIControlStateSelected];

[button setTitleColor:tabInactiveTitleColor forState:UIControlStateNormal];
[button setTitleColor:tabActiveTitleColor forState:UIControlStateHighlighted];
[button setTitleColor:tabActiveTitleColor forState:UIControlStateSelected];
[button setTitleColor:tabActiveTitleColor forState:UIControlStateHighlighted | UIControlStateSelected];

[button setTitleShadowColor:tabInactiveShadowColor forState:UIControlStateNormal];
[button setTitleShadowColor:tabActiveShadowColor forState:UIControlStateHighlighted];
[button setTitleShadowColor:tabActiveShadowColor forState:UIControlStateSelected];
[button setTitleShadowColor:tabActiveShadowColor forState:UIControlStateHighlighted | UIControlStateSelected];

[button setSelected:NO];
[tabButtonsContainerView addSubview:button];

++index;
Expand All @@ -102,8 +111,8 @@ - (void)reloadTabButtons
[self addTabButtons];

// Force redraw of the previously active tab.
NSUInteger lastIndex = _selectedIndex;
_selectedIndex = NSNotFound;
NSUInteger lastIndex = selectedIndex;
selectedIndex = NSNotFound;
self.selectedIndex = lastIndex;
}

Expand All @@ -112,15 +121,15 @@ - (void)layoutTabButtons
NSUInteger index = 0;
NSUInteger count = [self.viewControllers count];

CGRect rect = CGRectMake(0, 0, floorf(self.view.bounds.size.width / count), TAB_BAR_HEIGHT);
CGRect rect = CGRectMake(0, 0, floorf(tabButtonsContainerView.bounds.size.width / count), tabButtonsContainerView.bounds.size.height);

indicatorImageView.hidden = YES;

NSArray *buttons = [tabButtonsContainerView subviews];
for (UIButton *button in buttons)
{
if (index == count - 1)
rect.size.width = self.view.bounds.size.width - rect.origin.x;
rect.size.width = tabButtonsContainerView.bounds.size.width - rect.origin.x;

button.frame = rect;
rect.origin.x += rect.size.width;
Expand All @@ -137,21 +146,51 @@ - (void)viewDidLoad
[super viewDidLoad];

self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

CGRect rect = CGRectMake(0, 0, self.view.bounds.size.width, TAB_BAR_HEIGHT);
tabButtonsContainerView = [[UIView alloc] initWithFrame:rect];
tabButtonsContainerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.view addSubview:tabButtonsContainerView];

rect.origin.y = TAB_BAR_HEIGHT;
rect.size.height = self.view.bounds.size.height - TAB_BAR_HEIGHT;
contentContainerView = [[UIView alloc] initWithFrame:rect];
contentContainerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:contentContainerView];

indicatorImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MHTabBarIndicator"]];
[self.view addSubview:indicatorImageView];


if (tabButtonsContainerView == nil) {
CGRect rect = CGRectMake(0, 0, self.view.bounds.size.width, TAB_BAR_HEIGHT);
tabButtonsContainerView = [[UIView alloc] initWithFrame:rect];
tabButtonsContainerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self.view addSubview:tabButtonsContainerView];
}

if (contentContainerView == nil) {
CGRect rect = CGRectMake(0, tabButtonsContainerView.frame.size.height, self.view.bounds.size.width, self.view.bounds.size.height - tabButtonsContainerView.frame.size.height);
contentContainerView = [[UIView alloc] initWithFrame:rect];
contentContainerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:contentContainerView];
}

if (indicatorImageView == nil) {
indicatorImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MHTabBarIndicator"]];
CGRect rect = indicatorImageView.frame;
rect.origin.y = CGRectGetMaxY(tabButtonsContainerView.frame) - rect.size.height;
indicatorImageView.frame = rect;
[self.view addSubview:indicatorImageView];
}

if (tabTitleFont == nil) {
tabTitleFont = [UIFont boldSystemFontOfSize:18];
}
if (tabActiveBackgroundImage == nil) {
tabActiveBackgroundImage = [[UIImage imageNamed:@"MHTabBarActiveTab"] stretchableImageWithLeftCapWidth:0 topCapHeight:0];
}
if (tabInactiveBackgroundImage == nil) {
tabInactiveBackgroundImage = [[UIImage imageNamed:@"MHTabBarInactiveTab"] stretchableImageWithLeftCapWidth:1 topCapHeight:0];
}
if (tabInactiveTitleColor == nil) {
tabInactiveTitleColor = [UIColor colorWithRed:175/255.0f green:85/255.0f blue:58/255.0f alpha:1.0f];
}
if (tabActiveTitleColor == nil) {
tabActiveTitleColor = [UIColor whiteColor];
}
if (tabInactiveShadowColor == nil) {
tabInactiveShadowColor = [UIColor whiteColor];
}
if (tabActiveShadowColor == nil) {
tabActiveShadowColor = [UIColor colorWithWhite:0.0f alpha:0.5f];
}

[self reloadTabButtons];
}

Expand Down Expand Up @@ -187,26 +226,26 @@ - (void)setViewControllers:(NSArray *)newViewControllers
UIViewController *oldSelectedViewController = self.selectedViewController;

// Remove the old child view controllers.
for (UIViewController *viewController in _viewControllers)
for (UIViewController *viewController in viewControllers)
{
[viewController willMoveToParentViewController:nil];
[viewController removeFromParentViewController];
}

_viewControllers = [newViewControllers copy];
viewControllers = [newViewControllers copy];

// This follows the same rules as UITabBarController for trying to
// re-select the previously selected view controller.
NSUInteger newIndex = [_viewControllers indexOfObject:oldSelectedViewController];
NSUInteger newIndex = [viewControllers indexOfObject:oldSelectedViewController];
if (newIndex != NSNotFound)
_selectedIndex = newIndex;
else if (newIndex < [_viewControllers count])
_selectedIndex = newIndex;
selectedIndex = newIndex;
else if (newIndex < [viewControllers count])
selectedIndex = newIndex;
else
_selectedIndex = 0;
selectedIndex = 0;

// Add the new child view controllers.
for (UIViewController *viewController in _viewControllers)
for (UIViewController *viewController in viewControllers)
{
[self addChildViewController:viewController];
[viewController didMoveToParentViewController:self];
Expand Down Expand Up @@ -234,28 +273,28 @@ - (void)setSelectedIndex:(NSUInteger)newSelectedIndex animated:(BOOL)animated

if (![self isViewLoaded])
{
_selectedIndex = newSelectedIndex;
selectedIndex = newSelectedIndex;
}
else if (_selectedIndex != newSelectedIndex)
else if (selectedIndex != newSelectedIndex)
{
UIViewController *fromViewController;
UIViewController *toViewController;

if (_selectedIndex != NSNotFound)
if (selectedIndex != NSNotFound)
{
UIButton *fromButton = (UIButton *)[tabButtonsContainerView viewWithTag:TAG_OFFSET + _selectedIndex];
[self deselectTabButton:fromButton];
UIButton *fromButton = (UIButton *)[tabButtonsContainerView viewWithTag:TAG_OFFSET + selectedIndex];
fromButton.selected = NO;
fromViewController = self.selectedViewController;
}

NSUInteger oldSelectedIndex = _selectedIndex;
_selectedIndex = newSelectedIndex;
NSUInteger oldSelectedIndex = selectedIndex;
selectedIndex = newSelectedIndex;

UIButton *toButton;
if (_selectedIndex != NSNotFound)
if (selectedIndex != NSNotFound)
{
toButton = (UIButton *)[tabButtonsContainerView viewWithTag:TAG_OFFSET + _selectedIndex];
[self selectTabButton:toButton];
toButton = (UIButton *)[tabButtonsContainerView viewWithTag:TAG_OFFSET + selectedIndex];
toButton.selected = YES;
toViewController = self.selectedViewController;
}

Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ This is a custom container view controller for iOS 5 that works just like a regu

![Screenshot](https://github.com/hollance/MHTabBarController/raw/master/Screenshot.png)

To customize the tab bar's appearance you currently have to mess around in the code a bit.
You can customize the tab bar's appearance by setting the view positions with Interface Builder or setting the properties declared in the `MHTabBarController-Protected.h` file.

The MHTabBarController source code is copyright 2011 Matthijs Hollemans and is licensed under the terms of the MIT license.

Collaborators:

[@angelolloqui](http://twitter.com/angelolloqui) [angelolloqui.com](http://angelolloqui.com)