Custom TabBar in universal iOS App - ios

I am looking to recreate the raised tab bar as seen in many apps but am looking to do this in a universal app where the raised position would need to change.
At the moment I am not having much luck creating this so have come here for enlightenment, failing this I may just create a custom tab bar background.
If this is the case, would this work in the app delegate to differentiate the two devices?
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UITarBar *tabBar = [UITabBar appearance];
[tabBar setBackgroundImage: [UIImage imageNamed:#"iPadtabBar.png"]];
}
else {
UITarBar *tabBar = [UITabBar appearance];
[tabBar setBackgroundImage: [UIImage imageNamed:#"iPhonetabBar.png"]];
}
You may be thinking, why doesn't he just test it.. well that would be easier and if I could I would rather than ask first but I don't get my Mac back until the start of next week!

You will have to subclass UITabBarController and customize that center tab button. There is a nicely done example here: https://github.com/boctor/idev-recipes/tree/master/RaisedCenterTabBar/Classes
Header file:
#interface CustomTabBarViewController : UITabBarController{
}
// Create a view controller and setup it's tab bar item with a title and image
-(UIViewController*) viewControllerWithTabTitle:(NSString*)title image:(UIImage*)image;
// Create a custom UIButton and add it to the center of our tab bar
-(void) addCenterButtonWithImage:(UIImage*)buttonImage highlightImage:(UIImage*)highlightImage;
#end
Implementation file:
#implementation CustomTabBarViewController
// Create a view controller and setup it's tab bar item with a title and image
-(UIViewController*) viewControllerWithTabTitle:(NSString*) title image:(UIImage*)image
{
UIViewController* viewController = [[UIViewController alloc] init];
viewController.tabBarItem = [[UITabBarItem alloc] initWithTitle:title image:image tag:0];
return viewController;
}
// Create a custom UIButton and add it to the center of our tab bar
-(void) addCenterButtonWithImage:(UIImage*)buttonImage highlightImage:(UIImage*)highlightImage
{
UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
button.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;
button.frame = CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
[button setBackgroundImage:buttonImage forState:UIControlStateNormal];
[button setBackgroundImage:highlightImage forState:UIControlStateHighlighted];
CGFloat heightDifference = buttonImage.size.height - self.tabBar.frame.size.height;
if (heightDifference < 0)
button.center = self.tabBar.center;
else
{
CGPoint center = self.tabBar.center;
center.y = center.y - heightDifference/2.0;
button.center = center;
}
[self.view addSubview:button];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end

Related

Hide Custom ToolBar Button

I have created a custom tabbar and added a custom image at the center position. The following code works as expected.It adds my custom button at the center of the tabbar bar.
CustomTabBarViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage* buttonImage = [UIImage imageNamed:#"icon_floating.png"];
button.frame = CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
[button setBackgroundImage:buttonImage forState:UIControlStateNormal];
CGFloat heightDifference = buttonImage.size.height - self.tabBar.frame.size.height;
if (heightDifference < 0)
button.center = self.tabBar.center;
else
{
CGPoint center = self.tabBar.center;
center.y = center.y - heightDifference/2.0;
button.center = center;
}
[button addTarget:self
action:#selector(myAction)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
-(void) myAction
{
self.selectedIndex = 2;
}
The issue that I have faced as follows. There is a viewcontroller - ChatViewContoller where I hide tabbar. But since the custom button is added on the tabbar, I could not able to hide the custom button with the following code.
ChatViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
CustomTabBarViewController* tab = [[CustomTabBarViewController alloc] init];
tab.button.hidden = YES;
}
Hotspring,please reference the original tabbar object instead of creating a new one while hiding center button i.e
CustomTabBarViewController* tab = //reference to existing tabbar
instead of
CustomTabBarViewController* tab = [[CustomTabBarViewController alloc] init];
and then try hiding button

Wrong barButton item tint for play/pause buttons built programatically

I have a UIToolbar with audio controls. Sometime around iOS 7 upgrade the color of the bar changed and iOS started shading my buttons differently. That introduced a bug where the play and pause buttons which I change programatically don't match the new look of the toolbar:
Toolbar starts out looking fine:
Now I pressed play so code inserted pause button but wrong color:
Now I pressed pause and code inserted play button, again with wrong color:
I want the play and pause buttons to have that same dark look as the other buttons. I assume I have to do something differently when building the replacement buttons. The raw icon images are all that lighter color, but iOS toolbar seems to be automatically recoloring to darker color from the storyboard, as seen in first screenshot.
Here's the code I use to build the buttons:
- (UIBarButtonItem *) makeBarButton: (NSString *) imageName action:(SEL)targetAction
{
UIImage* image = [UIImage imageNamed:imageName];
CGRect frame = CGRectMake(0 - 20, 0 - 20, image.size.width + 20, image.size.height + 20);
UIButton *button = [[UIButton alloc] initWithFrame:frame];
[button addTarget:self action:targetAction forControlEvents:UIControlEventTouchUpInside];
[button setImage:image forState:UIControlStateNormal];
[button setShowsTouchWhenHighlighted:YES];
return [[UIBarButtonItem alloc] initWithCustomView:button];
}
- (void) setAsPlaying:(BOOL)isPlaying
{
self.rootViewController.playing = isPlaying;
// we need to change which of play/pause buttons are showing, if the one to
// reverse current action isn't showing
if ((isPlaying && !self.pauseButton) || (!isPlaying && !self.playButton))
{
UIBarButtonItem *buttonToRemove = nil;
UIBarButtonItem *buttonToAdd = nil;
if (isPlaying)
{
buttonToRemove = self.playButton;
self.playButton = nil;
self.pauseButton = [self makeBarButton:#"icon_pause.png" action:#selector(pauseAudio:)];
buttonToAdd = self.pauseButton;
}
else
{
buttonToRemove = self.pauseButton;
self.pauseButton = nil;
self.playButton = [self makeBarButton:#"icon_play.png" action:#selector(playAudio:)];
buttonToAdd = self.playButton;
}
// Get the reference to the current toolbar buttons
NSMutableArray *toolbarButtons = [[self.toolbar items] mutableCopy];
// Remove a button from the toolbar and add the other one
if (buttonToRemove)
[toolbarButtons removeObject:buttonToRemove];
if (![toolbarButtons containsObject:buttonToAdd])
[toolbarButtons insertObject:buttonToAdd atIndex:4];
[self.toolbar setItems:toolbarButtons];
}
}
Appreciate your help with this.
Check the following:
If the images are stored inside your image assets, make sure they are template images.
Then you can either A) set their tint colour in Interface Builder to the grey colour you prefer or B) do it programmatically in your makeBarButton method as such;
button.tintColor = [UIColor grayColor];

The navigation bar and bottom toolbar is not working(not respond to tap/click)

I have a problem in ios7(ipad), that is, the navigation bar and the bottom toolbar can not catch the tap event. The same code works fine in the ios 8 (iphone/ipad). Here is my code, I have a slideView which is a UIView, and I push another UIView singleCamView into it. The first time I load into the singleCamView, the navigation and the toolbar isn't working, but when I go back to slideView and get into the singleCamView again, everything is fine.
In slideView:
(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self changeUILayout];
[serverMgr disableTalkFunc];
if (eLayoutType == eLayout_1X1 && !backFrom1x1) {
[self changeLayout_1x1];
}
}
(void)changeLayout_1x1 {
if ([self.navigationController.visibleViewController isKindOfClass:[SlideView class]]) {
int camInArray = 0;//iPageIdx * [self getPreviewNum] + singleCamPos;
SinglePageViewController_Base *firstPage = [viewControllers objectAtIndex:0];
SingleCamViewController * mCam = [[SingleCamViewController alloc] initWithServerManager:serverMgr CurrentCam:camInArray];
[mCam setParent:firstPage];
[mCam setTitle:[serverMgr getDeviceNameByChIndex:0]];
mCam.changeLayoutDelegate = self;
int ch_index = 0;//iPageIdx * [self getPreviewNum] + pos;
[serverMgr updateAudioAndPtzDeviceByChIndex:ch_index];
[mCam setPtzCap:[serverMgr isSupportPTZ:ch_index]];
[mCam setAudioCap:[serverMgr isSupportAudio:ch_index]];
[mCam setTalkCap:[serverMgr isSupportTalk:ch_index]];
[firstPage setSingleCam:mCam];
[serverMgr setPlaybackDelegate:mCam];
[self.navigationController pushViewController:mCam animated:YES];
//clear to default picture
[firstPage setDefaultPic];
[serverMgr disconnectAllCam];
[serverMgr connectToCamWithHighResolution:ch_index];
[mCam release];
eLayoutType = eLayout_1X1;
[serverMgr setLayoutType:eLayout_1X1];
[parent save];
}
else {
return;
}
}
In singleCamView:
....
UIImage *backImage;
UIButton *bButton = [UIButton buttonWithType:UIButtonTypeCustom];
if (IS_IPAD)
backImage = [UIImage imageNamed:#"Back_48x48_nor.png"];
else
backImage = [UIImage imageNamed:#"Back_32x32_nor.png"];
bButton.bounds = CGRectMake(0, 0, backImage.size.width, backImage.size.height);
[bButton setImage:backImage forState:UIControlStateNormal];
[bButton addTarget:self action:#selector(onBackBtnTap) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem * backButton = [[UIBarButtonItem alloc] initWithCustomView:bButton];
UIBarButtonItem * backButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"Back", nil) style:UIBarButtonItemStylePlain target:self action:#selector(onBackBtnTap)];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
[backButton setTintColor:COLOR_NAVBUTTONTINT];
}
else {
[backButton setTintColor:COLOR_NAVBUTTONTINTIOS6];
}
self.navigationItem.leftBarButtonItem = backButton;
[backButton release];
The important thing is, it works fine in ios8, I can't figure out the reason. Thank you.
Finally I solve my own problem, here is the solution, the key point is that the second view is pushed right after the main view is created, the controller (maybe) is not fully initialized yet. So I add a delay between main view and second, everything works fine.
This post gives me the solution. Adding delay between execution of two following lines

Setting the background color of a specific tab bar Item and making it a seperate item

Like the Instagram app, i would like to have my center tab bar item a specific color.
I understand this is how I would set the selected image:
[[UITabBar appearance] setSelectionIndicatorImage:[AppDelegate imageFromColor:[UIColor colorWithRed:0.484 green:0.403 blue:0.884 alpha:1] forSize:CGSizeMake(64, 49) withCornerRadius:0]];
But how would I keep the 3rd tab's background to always stay that color?
Also, how do I make the middle tab separate from the rest, as in, when I press the center tab, there is modal presentation of a view controller (like instagram), and the previous tab stays selected for when dismissing the modal view controller.
#interface BaseViewController : UITabBarController
// Create a view controller and setup it's tab bar item with a title and image
-(UIViewController*) viewControllerWithTabTitle:(NSString*)title image:(UIImage*)image;
// Create a custom UIButton and add it to the center of our tab bar
-(void) addCenterButtonWithImage:(UIImage*)buttonImage highlightImage: (UIImage*)highlightImage;
#implementation BaseViewController
-(UIViewController*) viewControllerWithTabTitle:(NSString*) title image:(UIImage*)image
{
UIViewController* viewController = [[UIViewController alloc] init] ;
viewController.tabBarItem = [[UITabBarItem alloc] initWithTitle:title image:image tag:0];
return viewController;
}
// Create a custom UIButton and add it to the center of our tab bar
-(void) addCenterButtonWithImage:(UIImage*)buttonImage highlightImage:(UIImage*)highlightImage
{
UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
button.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;
button.frame = CGRectMake(0.0, 0.0, buttonImage.size.width, buttonImage.size.height);
[button setBackgroundImage:buttonImage forState:UIControlStateNormal];
[button setBackgroundImage:highlightImage forState:UIControlStateHighlighted];
CGFloat heightDifference = buttonImage.size.height - self.tabBar.frame.size.height;
if (heightDifference < 0)
button.center = self.tabBar.center;
else
{
center = self.tabBar.center;
center.y = center.y - heightDifference/2.0;
button.center = center;
}
[self.view addSubview:button];
}
Subclass this Tab controller and call the above function to set image o tab bar items and center button like Instagram
#interface InstagramViewController : BaseViewController
#end
#implementation InstagramViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.viewControllers = [NSArray arrayWithObjects:
[self viewControllerWithTabTitle:#"Feed" image:[UIImage imageNamed:#"112-group.png"]],
[self viewControllerWithTabTitle:#"Popular" image:[UIImage imageNamed:#"29-heart.png"]],
[self viewControllerWithTabTitle:#"Share" image:nil],
[self viewControllerWithTabTitle:#"News" image:[UIImage imageNamed:#"news.png"]],
[self viewControllerWithTabTitle:#"#user" image:[UIImage imageNamed:#"123-id-card.png"]], nil];
}
-(void)willAppearIn:(UINavigationController *)navigationController
{
[self addCenterButtonWithImage:[UIImage imageNamed:#"cameraTabBarItem.png"] highlightImage:nil];
}
#end

Create a custom UIButton class with delete function

I have a grid of UIButtons. When I hit an 'edit' button, I want a delete button to appear over each of these buttons, which when pressed, deletes the button (and associated data). A bit like apple's home screen, when you hold down a button and it starts to wiggle with an X in the corner.
According to this post: Subclass UIButton to add a property I can use Associative References to add a property to each of my buttons. I've tried to add a UIButton as a property of my custom UIButton but I can't seem to get it to appear and have the feeling this isn't the right way to go. Here's my custom button main:
#import "UIButton+Property.h"
#import <objc/runtime.h>
#implementation UIButton(Property)
static char UIB_DELETEBUTTON_KEY;
#dynamic deleteButton;
- (void)setDeleteButton:(UIButton *)deleteButton {
deleteButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
deleteButton.frame = CGRectMake(100, 100, 50, 50);
objc_setAssociatedObject(self, &UIB_DELETEBUTTON_KEY, deleteButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIButton *)deleteButton {
return (UIButton *)objc_getAssociatedObject(self, &UIB_DELETEBUTTON_KEY);
}
#end
And here's where I add the buttons programmatically:
//Create a custom button for each custom book doc
for (int i = 0; i < [customBookDocs count]; ++i) {
BookDoc *customBookDoc = [customBookDocs objectAtIndex:i];
NSString *bookTitle = customBookDoc.book.title;
//create a button for each book
CGRect frame = CGRectMake(xCoord, yCoord, 200, 200);
UIButton *bookButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
bookButton.bookDoc = customBookDoc;
[bookButton setFrame:frame];
[bookButton setTitle:bookTitle forState:UIControlStateNormal];
[bookButton addTarget:self action:#selector(bookButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
xCoord += 250;
[self.view addSubview:bookButton];
[self.view addSubview:bookButton.deleteButton];
}
Is there an easier more sensible way to do this? Or am I on the right track?
ORIGINAL RESPONSE BEGAN:
... Someone else may have more to say about that, but I'm not sure why you'd need to use object association here. You can certainly add another button to your button as a property using regular subclassing, which is the route that I would take. ...
EDITS BELOW:
I thought that I had subclassed a UI control directly, but I realized that I was mistaken when I went to look for the code. #Joe rightly pointed out in the comments that there are issues with directly subclassing UI controls.
I was able to implement something like the functionality you described without using Associated Objects, by creating a wrapper class to hold the button and its related delete button. It works, but it's not very flexible, so I would generally recommend #Joe's method as a better solution.
Here's the relevant code:
I threw all of the code into the appDelegate to keep it simple. I don't recommend that in real life.
AppDelegate.m:
#implementation AppDelegate
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
UIButton *toggleDeleteButtons = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[toggleDeleteButtons setFrame:CGRectMake(20, 45, 280, 45)];
[toggleDeleteButtons setTitle:#"Toggle Delete" forState:UIControlStateNormal];
[toggleDeleteButtons addTarget:self action:#selector(toggleDeleteButtonAction) forControlEvents:UIControlEventTouchUpInside];
[[self window] addSubview:toggleDeleteButtons];
ButtonWrapper *myButtonWrapper = [[ButtonWrapper alloc] init];
[[myButtonWrapper button] setFrame:CGRectMake(20, 100, 200, 45)];
[[myButtonWrapper button] setTitle:#"This is my button" forState:UIControlStateNormal];
[[myButtonWrapper deleteButton] addTarget:self action:#selector(buttonDeleteRequested:) forControlEvents:UIControlEventTouchUpInside];
[[myButtonWrapper deleteButton] setTag:0];
[[self window] addSubview:[myButtonWrapper button]];
buttonWrapper1 = myButtonWrapper;
// Added instance called anotherButtonWrapper with tag 1, as above
// Added instance called stillAnotherButtonWrapper with tag 2, as above
[self.window makeKeyAndVisible];
return YES;
}
- (void)toggleDeleteButtonAction {
static BOOL deleteButtonsShown;
[buttonWrapper1 showDeleteButton:!deleteButtonsShown];
[buttonWrapper2 showDeleteButton:!deleteButtonsShown];
[buttonWrapper3 showDeleteButton:!deleteButtonsShown];
deleteButtonsShown = !deleteButtonsShown;
}
- (void)buttonDeleteRequested:(UIButton *)deleteButton {
// delete the specified button here
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Delete" message:[NSString stringWithFormat:#"Delete was pressed on button %i",[deleteButton tag]]delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
ButtonWrapper.m:
#implementation ButtonWrapper
#synthesize button;
#synthesize deleteButton;
- (ButtonWrapper *)init {
ButtonWrapper *newWrapper = [ButtonWrapper alloc];
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myButton setFrame:CGRectZero];
UIButton *myDeleteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myDeleteButton setFrame:CGRectMake(0, 0, 100, 40)];
[myDeleteButton setTitle:#"Delete" forState:UIControlStateNormal];
[myDeleteButton setHidden:TRUE];
[myButton addSubview:myDeleteButton];
[newWrapper setButton:myButton];
[newWrapper setDeleteButton:myDeleteButton];
return newWrapper;
}
- (void)showDeleteButton:(BOOL)showButton {
if (showButton) {
[[self deleteButton] setHidden:FALSE];
[[self deleteButton] setEnabled:TRUE]; }
else {
[[self deleteButton] setHidden:TRUE];
[[self deleteButton] setEnabled:FALSE];
}
}
#end
This solution did not require me to implement all of the UI properties, but it did require extra work to hook up the embedded delegates, which is cumbersome. There may be a way to pass the delegates into the wrapper at initialization, but I couldn't make it work.

Resources