Correct way to subclass UIToolbar - iphone

For the first time I'm subclassing UIToolbar for creating ones with custom UIBarButton.
I'm doing this:
#interface CustomToolbar : UIToolbar
#end
#implementation CustomToolbar
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// add buttons
UIBarButtonItem *myButton = [[UIBarButtonItem alloc] initWithTitle:#"Button" style:UIBarButtonItemStyleBordered target:self action:#selector(pressSignupButton:)];
// add buttons to the array
NSArray *items = [NSArray arrayWithObjects:myButton, nil];
[self setItems:items];
}
return self;
}
#end
Then in my view controller:
CustomToolbar *myToolbar = [[CustomToolbar alloc] init];
[self.navigationController.view addSubview:myToolbar];
The problem is that I can see the toolbar but there aren't buttons. Why?
NB: I prefer to have all programmatically without nib.

Does this work?
CustomToolbar *myToolbar = [[CustomToolbar alloc]
initWithFrame:CGRectMake(0,0,self.navigationController.view.frame.size.width, 44)];
[self.navigationController.view addSubview:myToolbar];

Related

Adding back button to a view controller

I have created 3 UIViewControllers for my 3 UIToolBar items but i am not able to create a button on each controller which takes me back to the home page or main view controller. Kindly help me as i am new to objective c.
Here is my code....
"ViewController.h"
#import "ViewController.h"
#import "ContactInfoViewController.h"
#import "DateViewController.h"
#import "MessageViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIToolbar *toolbar = [[UIToolbar alloc] init];
toolbar.frame = CGRectMake(0, 417, self.view.frame.size.width, 44);
UIBarButtonItem *contact = [[UIBarButtonItem alloc] initWithTitle:#"Contact" style:UIBarButtonItemStyleDone target:self action:#selector(ContactButton)];
UIBarButtonItem *flexiableItem1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
UIBarButtonItem *date = [[UIBarButtonItem alloc] initWithTitle:#"Date" style:UIBarButtonItemStyleDone target:self action:#selector(DateButton)];
UIBarButtonItem *flexiableItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
UIBarButtonItem *message = [[UIBarButtonItem alloc] initWithTitle:#"Message" style:UIBarButtonItemStyleDone target:self action:#selector(MessageButton)];
NSMutableArray *items = [[NSMutableArray alloc] initWithObjects:contact,flexiableItem1,date,flexiableItem,message, nil];
[toolbar setItems:items animated:YES];
[items release];
[self.view addSubview:toolbar];
[toolbar release];
}
-(void)ContactButton
{
ContactInfoViewController *secondViewcont = [[ContactInfoViewController alloc] init];
UINavigationController *navigCont = [[UINavigationController alloc] initWithRootViewController:secondViewcont];
[self presentViewController:navigCont animated:YES completion:nil];
}
-(void)DateButton
{
DateViewController *secondViewcont = [[DateViewController alloc] init];
UINavigationController *navigCont = [[UINavigationController alloc] initWithRootViewController:secondViewcont];
[self presentViewController:navigCont animated:YES completion:nil];
}
-(void)MessageButton
{
MessageViewController *secondViewcont = [[MessageViewController alloc] init];
UINavigationController *navigCont = [[UINavigationController alloc] initWithRootViewController:secondViewcont];
[self presentViewController:navigCont animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
Add custom button to your second view controller
In viewDidLoad you can do like this
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(doneButtonPressed:)]autorelease];
- (IBAction)doneButtonPressed:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}

left bar button items in navigationBar

In iOS5 we have leftBarButtonItems that we can set to a navigation bar, how can I do this for pre-iOS 5? I basically have an array of UIBarButtonItem that I wanted to set the bar to.
You can build own bar, and add it as left button:
UIBarButtonItem *firstButton = [[UIBarButtonItem alloc] initWithTitle:#"First" style:UIBarButtonItemStyleBordered target:self action:#selector(firstButtonAction:)];
UIBarButtonItem *secondButton = [[UIBarButtonItem alloc] initWithTitle:#"Second" style:UIBarButtonItemStyleBordered target:self action:#selector(secondButtonAction:)];
UIToolbarTransparent *toolbar = [UIToolbarTransparent new];
[toolbar setFrame:CGRectMake(0,0, 140,44)];
[toolbar setItems:[NSArray arrayWithObjects:firstButton, secondButton, nil]];
UIBarButtonItem *customBarButton = [[UIBarButtonItem alloc] initWithCustomView:toolbar];
self.navigationItem.leftBarButtonItem = customBarButton;
UIToolbarTransparent
.h
#import <Foundation/Foundation.h>
#interface UIToolbarTransparent : UIToolbar {
}
.m
#import "UIToolbarTransparent.h"
#implementation UIToolbarTransparent
- (id)init {
if (self = [super init]) {
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
self.translucent=YES;
}
return self;
}
#end
samfisher is right, but you can just use a custom UIView that contains your UIButtons, than use that UIView as a (single) leftBasButtonItem

Presenting Modalviewcontroller for Info Button when pressed on UIToolbar programmatically

I want is on main window to present the created modalViewController view when the infobutton is pressed. But when I press Info button on the main window nothing happens.
In the mainviewcontroller.h file I have following code:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface imageviewViewController : UIViewController{
AVAudioPlayer *audioPlayer;
}
#property (nonatomic, retain) UIToolbar *toolbar;
#property (nonatomic, assign) UIBarButtonSystemItem currentSystemItem;
#property (nonatomic, retain) AVAudioPlayer *audioPlayer;
#end
In the mainviewcontroller.m file have following code:
#import "imageviewViewController.h"
#import "Infoviewcontroller.h"
#implementation imageviewViewController
#synthesize toolbar;
#synthesize currentSystemItem;
#synthesize audioPlayer;
UIBarButtonItem *infoItem = [[UIBarButtonItem alloc]
initWithTitle:#"Info"
style:UIBarButtonItemStyleBordered
target:nil
action:#selector(Infobuttonpressed)];
// flex item used to put space in between buttons
UIBarButtonItem *flexItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:nil
action:nil];
//Add buttons to the array
NSArray *toolbarItems = [NSArray arrayWithObjects: settingsButton, flexItem, systemItem, flexItem, systemItem1,flexItem, systemItem2, flexItem, systemItem3, flexItem, infoItem, nil];
[toolbar setItems:toolbarItems];
[settingsButton release];
[systemItem release];
[systemItem1 release];
[systemItem2 release];
[systemItem3 release];
[infoItem release];
[flexItem release];
[super viewDidLoad];
}
- (void) Infobuttonpressed: (id) sender
{
Infoviewcontroller *myView = [[Infoviewcontroller alloc] init];
[self presentModalViewController:myView animated:YES]; // present view modally
[self.navigationController pushViewController:myView animated:YES]; // add to navigation stack
[myView release];
}
In the Infoviewcontroller.h file have following code:
#import <UIKit/UIKit.h>
#interface Infoviewcontroller : UIViewController <UITextViewDelegate>
{
UITextView *textView;
}
#property (nonatomic, retain) UITextView *textView;
#property (nonatomic, assign) UINavigationBar *navBar;
#end
Then in the infoviewcontroller.m file have the following code:
#import "Infoviewcontroller.h"
#implementation Infoviewcontroller
#synthesize textView;
#synthesize navBar;
-(void)dealloc
{
[textView release];
[navBar release];
[super dealloc];
}
-(void)setupTextView
{
self.textView = [[[UITextView alloc] initWithFrame:self.view.frame] autorelease];
self.textView.textColor = [UIColor redColor];
self.textView.font = [UIFont fontWithName:#"System Bold" size:13];
self.textView.delegate = self;
self.textView.backgroundColor = [UIColor whiteColor];
self.textView.textAlignment = UITextAlignmentCenter;
self.textView.text = #"This is UITextView\nThis is UITextView\nThis is UITextView\nThis is UITextView";
[self.view addSubview: self.textView];
}
- (void)viewDidLoad
{
[super viewDidLoad];
navBar = [[UINavigationBar alloc] init];
UINavigationItem *navItem = [[[UINavigationItem alloc] initWithTitle:#"ModalViewControllerTest"] autorelease];
UIBarButtonItem *done = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissView:)] autorelease];
navItem.rightBarButtonItem = done;
navBar.items = [NSArray arrayWithObject:navItem];
[self.view addSubview:navBar];
}
UIBarButtonItem *infoItem = [[UIBarButtonItem alloc]
initWithTitle:#"Info"
style:UIBarButtonItemStyleBordered
target:nil
action:#selector(Infobuttonpressed)];
Should be
UIBarButtonItem *infoItem = [[UIBarButtonItem alloc]
initWithTitle:#"Info"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(Infobuttonpressed:)];
Spot the difference? First, you're missing a target (should be self, not nil). Second, the colon at the end of the selector is part of the signature, you were missing it so it was not calling your method (which is InfoButtonPressed:(id)sender).
As an aside, methods should start with a lower case name.
you dont need both pushModalViewController and pushViewController
Assuming your views are already controlled within an instance of a UINavigationController pushModalViewController on its own will work.
edit - remove the reference to pushViewController. Does it now work?
-(void) Infobuttonpressed: (id) sender;
{ Infoviewcontroller *myView = [[Infoviewcontroller alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:myView];
UIViewController *pushController = [[UIViewController alloc] init];
[self addSubview:pushController.view];
[pushController presentModalViewController:navController animated:YES];
[myView release];
}
The target of your infoItem is set to nil. You should set this to an instance of the class where the Infobuttonpressed is defined. I assume that is the mainviewController?
Also, the selector to call is specified as: #selector(Infobuttonpressed), while the method is called "Infobuttonpressed:", that is, with a parameter. That will likely result in a runtime error once you fix the target.
Then, there are some peculiarities in the way you handle things. As Nik already answered, you need to choose whether you want to present the view modally (with presentModalViewController), or present it as part of the navigation stack (with pushViewController), you cannot do both.
If you present it as part of the navitation stack, then it already has a navigation bar, and so you should not add one yourself. Just set the properties of the self.navigationItem.
Have a look at the View Controller Programming Guide for more info.

Left Arrow - UINavigationItem

I've searched around and I can't seem to figure it out. I'm sure many people will have links for me and such, which I've most likely already looked at. But if someone could please just show me code to do the following:
I'd like to have a left arrow in my UINavigationBar as a "Back" UINavigationItem. How can I do this? Here is my current code in my UIViewController:
theBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 48.0f)];
UIBarButtonItem *leftButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:nil action:#selector(backButtonSelected:)];
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithTitle:#"Options" style:UIBarButtonItemStyleBordered target:nil action:#selector(optionsButtonSelected:)];
UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:#"Title"];
item.leftBarButtonItem = leftButton;
item.hidesBackButton = YES;
item.rightBarButtonItem = rightButton;
[self.view addSubview:theBar];
I think this may be what you're looking for.
// in .h file
#property (nonatomic, retain) UINavigationBar *navBar;
// in .m file just below #implementation
#synthesize navBar;
// within .m method
navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 48.0f)];
navBar.barStyle = UIBarStyleBlack;
UINavigationItem *title = [[UINavigationItem alloc] initWithTitle:#"Nav Bar Title"];
UIBarButtonItem *leftButton = [[UIBarButtonItem alloc]
initWithTitle:#"Back"
style:UIBarButtonItemStylePlain
target:nil
action:#selector(backButtonSelected:)];
title.leftBarButtonItem = leftButton;
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAction
target:nil
action:#selector(showActionSheet:)];
title.rightBarButtonItem = rightButton;
[navBar pushNavigationItem:title animated:YES];
[self.view addSubview:navBar];
You should use an UINavigationController in order to pop/push UIViewControllers on your screen. The navigation controller will add an UINavigationBar to your UIViewControllers automatically so you will not need to create them as you did.
Here is a sample. I didn't looked for memory leaks.
In the app delegate you'll find this method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
MainVC *mainVC = [[MainVC alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:mainVC];
[self.window addSubview:navController.view];
[self.window makeKeyAndVisible];
return YES;
}
MainVC is a UIViewController that represents the level 1 of the hierarchy. in it i have
- (void)loadView
{
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
self.view.backgroundColor = [UIColor redColor];
self.title = #"MainVC";
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithTitle:#"SecondLevel" style:UIBarButtonItemStyleBordered target:self action:#selector(secondLevelSelected:)];
self.navigationItem.rightBarButtonItem = rightButton;
}
- (void) secondLevelSelected:(id)sender
{
SecondVC *secondVC = [[SecondVC alloc] init];
[self.navigationController pushViewController:secondVC animated:YES];
}
SecondVC is another UIViewController that represents the second level of the hierachy. Here you will find the back button that you want.
- (void)loadView
{
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 240)];
self.view.backgroundColor = [UIColor greenColor];
self.title = #"SecondVC";
self.navigationItem.hidesBackButton = YES;
UIBarButtonItem *leftBtn = [[UIBarButtonItem alloc] initWithTitle:#"FirstLevel" style:UIBarButtonItemStyleBordered target:self action:#selector(leftBtnSelected:)];
self.navigationItem.leftBarButtonItem = leftBtn;
}
- (void) leftBtnSelected:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}

Proper way to add a title to a modal view controller's toolbar?

Right now I'm using this in my -viewDidLoad method:
UIToolbar *toolbar = [[UIToolbar alloc] init];
UIBarButtonItem *flexibleSpace = [UIBarButtonItem alloc];
flexibleSpace = [flexibleSpace initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:nil
action:nil];
// Add a back button to allow user to close the modal view
NSString *back = NSLocalizedString(#"Back", nil);
UIBarButtonItem *backButton = [UIBarButtonItem alloc];
backButton = [backButton initWithTitle:back
style:UIBarButtonItemStyleDone
target:self
action:#selector(dismissModalViewControllerAnimated:)];
// Add a centered title to the toolbar
// I doubt this is the "correct" way to do this, but it seems to work.
// The "width" property of a UIBarButtonItem doesn't seem to correspond to
// the actual width if the button is flexible (i.e. the width isn't explicitly
// set), so I'm using this hack instead.
// This is obviously NOT an optimal solution. For one thing, if the button padding
// ever changes, it has to be changed manually here as well. For another, it is
// a pain to do this for every button I add to the toolbar, and furthermore the title
// is centered only according to its own width, not the toolbar's.
const CGRect toolbarFrame = [toolbar frame];
const CGFloat backWidth = [back sizeWithFont:[UIFont boldSystemFontOfSize:[UIFont buttonFontSize]]
constrainedToSize:toolbarFrame.size].width;
const CGRect titleFrame = {{0.0f, 0.0f},
{toolbarFrame.size.width - (backWidth * 2.0f), 50.0f}};
UILabel *titleLabel = [[UILabel alloc] initWithFrame:titleFrame];
[titleLabel setText:[self title]];
[titleLabel setBackgroundColor:[UIColor clearColor]];
[titleLabel setTextAlignment:UITextAlignmentCenter];
[titleLabel setFont:[UIFont boldSystemFontOfSize:20.0f]];
[titleLabel setTextColor:[UIColor whiteColor]];
[titleLabel setShadowColor:[UIColor colorWithWhite:0.0f alpha:0.5f]];
[titleLabel setShadowOffset:CGSizeMake(0.0f, -1.0f)];
UIBarButtonItem *titleItem = [[UIBarButtonItem alloc] initWithCustomView:titleLabel];
[titleLabel release];
NSArray *items = [[NSArray alloc] initWithObjects:flexibleSpace, titleItem, backButton, nil];
[flexibleSpace release];
[titleItem release];
[backButton release];
[toolbar setItems:items];
[items release];
[view addSubview:toolbar];
[toolbar release];
Does anyone have a better method for doing this? What I'm using feels like a major hack :(.
Edit:
Thanks for the suggestion Darren!
Here's what I'm using now, if anyone's interested:
First, in accordance with Darren's suggestion, I'm wrapping my modal view controller in a generic UINavigationController (which contains it's own UIToolbar, UINavigationBar, that comes with a title):
MyCustomViewController *myModalViewController = [[MyModalViewController alloc] init];
[myModalViewController setTitle:#"Foo"];
UINavigationController *modalNavController = [[UINavigationController alloc] initWithRootView:myModalViewController];
[myModalViewController release];
// This is intended to be presented in another view controller class
[self presentModalViewController:modalNavController animated:YES];
[modalNavController release];
Then in my -init method for the MyModalViewController class, I have this:
- (id)init
{
if (self = [super init]) {
UIBarButtonItem *backButtonItem = [UIBarButtonItem alloc];
backButtonItem = [backButtonItem initWithTitle:back
style:UIBarButtonItemStyleDone
target:[self navigationController]
action:#selector(dismissModalViewControllerAnimated:)];
[[self navigationItem] setRightBarButtonItem:backButtonItem];
[backButtonItem release];
}
return self;
}
This is a much cleaner solution than before. Thanks.
You should wrap your view controller inside a generic UINavigationController when you present the modal view:
MyCustomController* myController = [[MyCustomController alloc] init];
editor.title = #"My Title";
UINavigationController* modalController = [[UINavigationController alloc] initWithRootViewController:myController];
[self.navigationController presentModalViewController:modalController animated:YES];
[modalController release];
[myController release];
Your custom controller can specify its toolbar buttons in its init method:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self
action:#selector(doCancel:)] autorelease];
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:self
action:#selector(doSave:)] autorelease];
}
return self;
}
You should be adding the title in the items property
i.e.
#property(nonatomic, copy) NSArray *items
where items is init with a title
initWithTitle:style:target:action:
See http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIToolbar_Class/Reference/Reference.html#//apple_ref/occ/instp/UIToolbar/items
and
http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIBarButtonItem_Class/Reference/Reference.html#//apple_ref/occ/instm/UIBarButtonItem/initWithTitle:style:target:action:
for the details.
Hope that helps
[Edit]
P.S. The UIBarButtonItem is also where you would be adding your buttons ;-)