I have installed 4.2 iphone sdk today and ran my project (which is written in 4.0) with 4.2. I noticed that toolbar items are misplaced over toolbar, both in ipad, iphone simulators. Is there anyone else other than me who has encountered with this? Here is the code:
#define TOOLBAR_HEIGHT 34
#define TOOLBAR_ITEM_WIDTH 90
// extends UIView
#implementation MapViewControllerContainer
- (void) setFrame:(CGRect)frame
{
[super setFrame:frame];
if (self.subviews.count == 2)
{
((UIView *)[self.subviews objectAtIndex:0]).frame = CGRectMake(0, 0, frame.size.width, TOOLBAR_HEIGHT);
((UIView *)[self.subviews objectAtIndex:1]).frame = CGRectMake(0, TOOLBAR_HEIGHT, frame.size.width,
frame.size.height - TOOLBAR_HEIGHT);
}
}
#end
// extends UIViewController
#implementation MapViewController
- (void) loadView
{
self.view = [[MapViewControllerContainer alloc] init];
[self.view release];
UIToolbar * toolbar = [[UIToolbar alloc] init];
toolbar.barStyle = UIBarStyleBlack;
UIBarButtonItem * flexibleItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:nil action:nil];
detailsButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"Details",#"")
style:UIBarButtonItemStyleBordered target:self
action:#selector(detailsPressed)];
detailsButton.width = TOOLBAR_ITEM_WIDTH;
detailsButton.enabled = NO;
toolbar.items = [NSArray arrayWithObjects: flexibleItem, detailsButton, nil];
[flexibleItem release];
[detailsButton release];
[self.view addSubview:toolbar];
[toolbar release];
// More non-related code here
}
#end
Here is its output:
output http://dl.dropbox.com/u/13741164/toolbar.jpg
As you see, details button is out of frame by the right side. The smaller TOOLBAR_ITEM_WIDTH, the more it is inside the frame. So I suppose there is a bug in the calculation of toolbar items, because it works wonderfully in 4.0 sdk, right? Thanks for your help.
The following call fixed the issue for me:
[toolbar sizeToFit];
Related
I've following code running on iPad, I'm usin Zbar 1.2.2 beta version for support in iPad.
ZBarReaderViewController* mReader = [[ZBarReaderViewController alloc] init];
mReader.readerDelegate = self;
mReader.showsZBarControls = NO;
mReader.wantsFullScreenLayout = NO;
mReader.readerView.frame = CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height);
UIButton *imageView = [[UIButton alloc] init];
imageView.frame = CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height);
[self setupToolBar:imageView];
mReader.cameraOverlayView = imageView;
setupToolBar does following
- (void)setupToolBar:(UIButton*)imageView
{
[imageView retain];
UIToolbar* imagePickerToolBar = [[UIToolbar alloc] init];
UIImage *c = [UIImage imageNamed:#"close.png"];
UIBarButtonItem *closeItem = [[UIBarButtonItem alloc] initWithImage:c style:UIBarButtonItemStylePlain target:self action:#selector(closeBarItemPressed:)];
NSArray *items = [NSArray arrayWithObjects: closeItem, nil];
[imagePickerToolBar setItems:items animated:NO];
imagePickerToolBar.frame = CGRectMake(0, UIScreen.mainScreen.bounds.size.height - imagePickerToolBar.frame.size.height, imagePickerToolBar.frame.size.width, imagePickerToolBar.frame.size.height);
[imageView addSubview:imagePickerToolBar];
[imageView bringSubviewToFront:imagePickerToolBar];
[imagePickerToolBar release];
[closeItem release];
[imageView release];
}
Now my question : closeBarItemPressed: is not getting called, why and how to fix it ?
I had this problem too. It looks like touches are ignored in the bottom part of the overlay view. Try moving the button a little bit higher and it will work. (in your case if you change toolbar frame to be in the top of the screen, I bet your method will be called, at least this is what happened to me). Not sure why this is happening though.
Basically I solved my problem, by recompiling Zbar, with modified iPad settings. Now it works OK.
I have a memory leak that I do not understand. I am switching views with a segmentcontrol like this:
- (void)didChangeSegmentControl:(UISegmentedControl *)control {
if (self.activeViewController) {
[self.activeViewController viewWillDisappear:NO];
[self.activeViewController.view removeFromSuperview];
[self.activeViewController viewDidDisappear:NO];
}
self.activeViewController = [self.segmentedViewControllers objectAtIndex:control.selectedSegmentIndex];
[self.activeViewController viewWillAppear:NO];
[self.containerView addSubview:self.activeViewController.view];//Here is the memory leak
[self.activeViewController viewDidAppear:NO];
[self BuildBottomBarButtons];
}
I leak appears in Instruments and I do not have any idea why...
Thanks!!!
Edit:
The "BuildBottomBarButtons":
-(void) BuildBottomBarButtons{
//create toolbar using new
UIToolbar *toolbar = [UIToolbar new];
toolbar.barStyle = UIBarStyleBlackTranslucent;
[toolbar sizeToFit];
toolbar.frame = CGRectMake(0, 330, 320, 50);
//Add buttons
UIBarButtonItem *systemItem1 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:#selector(shareClicked)];
UIBarButtonItem *systemItem2 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"scroll left.PNG"] style:UIBarButtonItemStylePlain target:self action:#selector(upClicked)];
UIBarButtonItem *systemItem3 = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"scroll right.PNG"] style:UIBarButtonItemStylePlain target:self action:#selector(downClicked)];
//Use this to put space in between your toolbox buttons
UIBarButtonItem *fixItem50 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
UIBarButtonItem *fixItem70 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
fixItem50.width = 50;
fixItem70.width = 64;
//Add buttons to the array
NSArray *items = [NSArray arrayWithObjects: systemItem1, fixItem70, systemItem2, fixItem50,systemItem3, nil];
//release buttons
[systemItem1 release];
[systemItem2 release];
[systemItem3 release];
[fixItem70 release];
[fixItem50 release];
//add array of buttons to toolbar
[toolbar setItems:items animated:NO];
[self.containerView addSubview:toolbar];
[toolbar release];
}
And the "self.segmentedViewControllers" init method is:
- (NSMutableArray *)segmentedViewControllerContent {
JobGeneralDetailsController * controller1 = [[JobGeneralDetailsController alloc] initWithSelectedRowID:selectedRowID andWithJobBoardID:jobBoardId andWithJobDetails:jobDetails];
[controller1 setViewType:jobDetailsViewType];
//initWithParentViewController:self];
JobMapDetailsController * controller2 = [[JobMapDetailsController alloc] initWithJobDetails:jobDetails];//[[AustraliaViewController alloc] initWithParentViewController:self];
[controller1 setJobMapDetailsController: controller2];
JobReviewsController *controller3 = [[JobReviewsController alloc] initWithStyle:UITableViewStyleGrouped];
[controller3 setJobDetails:jobDetails];
[controller3 setViewType:jobDetailsViewType];
[controller1 setJobReviewsController: controller3];
NSMutableArray * controllers = [NSMutableArray arrayWithObjects:controller1, controller2, controller3,nil];
[controller1 release];
[controller2 release];
[controller3 release];
return controllers;
}
The only line I could point to is [self BuildBottomBarButtons];
Looking at your code the only problem I can see is in the [self BuildBottomBarButtons]
It looks like you are adding the toolbar to the container view everytime you switch view controllers.
What type of object does Instruments say is leaking, is it possible to see a screen shot of your instruments screen showing the leaked objects?
Trying to add a button to the right side of the toolbar of the detailviewcontroller in a split-view based app. I used flexible space to get it to the right side. In portrait it works fine, but in landscape (when the menu button disappears), the button gets moved so that half of it is off the screen.
Here's the relevant code (in DetailViewController.m):
- (void) viewDidLoad
{
// initialize toolbar
toolbar = [[UIToolbar alloc] initWithFrame: CGRectMake( 0, 0, 768, 44 )];
titleLabel = [[UILabel alloc] initWithFrame: CGRectMake( 284, 3, 200, 35 )];
titleLabel.text = #"Title & Location";
titleLabel.textAlignment = UITextAlignmentCenter;
[toolbar addSubview: titleLabel];
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemFlexibleSpace target: nil action: nil];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle: #"Add Event" style: UIBarButtonItemStyleDone target: rootController action: #selector(parseDone)];
NSArray *buttonArray = [NSArray arrayWithObjects: flexibleSpace, doneButton, nil];
[toolbar setItems: buttonArray];
[doneButton release];
[flexibleSpace release];
[self.view addSubview: toolbar];
}
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem
{
NSMutableArray *itemsArray = [toolbar.items mutableCopy];
[itemsArray insertObject: barButtonItem atIndex: 0];
[toolbar setItems:itemsArray animated:NO];
}
- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem
{
NSMutableArray *itemsArray = [toolbar.items mutableCopy];
[itemsArray removeObject:barButtonItem];
[toolbar setItems:itemsArray animated:NO];
[itemsArray release];
}
I recognize this problem from a project I worked on a while back. I think it only happened occasionally and I don't remember if we actually fixed it.
In your case it seems like it would be easier to just use the navigation bar and set rightBarButtonItem and leftBarButtonItem. That should solve your problem.
In fact, if you put your view controller in a UINavigationController you get the UINavigationBar and all the functionality of the navigation controller, if you choose to use it.
Well the problem was that I wasn't correctly resizing the toolbar when the iPad rotates to landscape mode. Fixed the problem by add this code:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if( interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight ) {
[toolbar setFrame: CGRectMake( 0, 0, 700, 44 )];
}
else {
[toolbar setFrame: CGRectMake( 0, 0, 768, 44 )];
}
return YES;
}
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];
}
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 ;-)