I have added the toolbar using Interface Builder, but I need to add the buttons at runtime / conditionally. I'm not getting any errors, but my dynamic buttons are not appearing on the toolbar. I have verified that arrayOfModulesScreens is loaded with the data I need. At least that works (:)). Do I need to add the buttons into a UIView, then add that view to the toolbar? Just thinking outloud. Perhaps there is a better approach to begin with? Thanks in advance for any clues leading to the resolve.
CustomFormController.h
#interface CustomFormController : UIViewController {
UIToolbar *screensBar;
}
CustomFormController.m
EPData *epData = [[EPData alloc] init];
NSArray *screens = [epData loadPlistIntoArray:#"Screens"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"process_module_id == %#", process_modulesID];
NSArray *arrayOfModulesScreens = [screens filteredArrayUsingPredicate:predicate];
for(int i=0; i < [arrayOfModulesScreens count]; i++) {
NSDictionary *dictRow = [arrayOfModulesScreens objectAtIndex:i];
UIButton *button = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
[button setTitle:[dictRow objectForKey:#"screen_title"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
[screensBar addSubview:button];
}
If you are actually wanting to add a UIBarButtonItem (not a UIButton) to the toolbar, you just create one or more UIBarButtonItems, put them in an NSArray (or NSMutableArray), and assign that array to the items property of the toolbar. See the UIBarButtonItem documentation for details. Using your code above, that might look something like this:
NSMutableArray *items = [NSMutableArray array];
for (int i = 0; i < [arrayOfModulesScreens count]; i++) {
NSDictionary *dictRow = [arrayOfModulesScreens objectAtIndex:i];
UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithTitle:[dictRow objectForKey:#"screen_title"]
style:UIBarButtonItemStyleBordered
target:self
action:#selector(buttonClick:)];
[items addObject:button];
[button release];
}
screensBar.items = items;
(you would, of course, then need to change your buttonClick: to expect a UIBarButtonItem instead of a UIButton).
If you're really wanting to put a UIButton in there, you first need to wrap the UIButton in a UIBarButtonItem, something like this:
UIBarButtonItem *item = [[[UIBarButtonItem alloc] initWithCustomView:button] autorelease];
Then add the item to the toolbar as above.
As for why your buttons aren't showing up in your posted code, the problem is that UIButton's buttonWithType: creates a button with zero width and zero height. You would need to resize the button (manually or by using sizeToFit after setting the title) to make it show. After fixing that, you would then see that the buttons are all on top of each other in the upper-left corner of the parent view; you would need to position them manually as appropriate.
I don't think you can change the navigationController directly in your code like that.
A safer way would be this:
UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithTitle:[dictRow objectForKey:#"screen_title"]
style:UIBarButtonItemStyleBordered
target:self
action:#selector(buttonClick:)];
self.navigationItem.rightBarButtonItem = button;
ToolBar Approach
One problem with adding UIButton to UIToolBar is that width of the button and positioning of the buttons is difficult to control
Simpler Approach
Remove the toolbar and place a UIView with the background image which is identical to your toolbar. Now you can add the UIButton as a subview to that view. This way you have more control over the positioning and the width of the buttons. Also you can add other views as well.
Related
I have made a standard Master-Detail application (with the Xcode template). I modified it, and now when I click one cell of the first table view the app shows another table view (which is the detail because is filtered by the clicked cell value).
In a standard table view I would have this situation on the UINavigationBar:
Edit Table title + (for adding new items)
I need the default back button of UINavigationBar too. Is it possible and allowed to do this? Graphically it would be like this:
< Back Table title Edit +
Do you know any other layout to display 3 button (back - edit - add) plus title all in the top bar?
Sorry for not posting images, Thanks in advance.
P.S. I'd like to know if it is possible to do the things I'm asking with built-in back button and edit button (I mean those that the system puts automatically to the navigation bar).
you need to setup array of right bar items to set up bar buttons right side...
Here is the code of 2 custom button to setup bar button items right side...you can use default button instead of this
UIButton *btnabout = [UIButton buttonWithType:UIButtonTypeCustom];
btnabout.frame = CGRectMake(0,8,30,30);
[btnabout setBackgroundImage:[UIImage imageNamed:#"about.png"] forState:UIControlStateNormal];
[btnabout addTarget:self action:#selector(callselector) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *about = [[UIBarButtonItem alloc] initWithCustomView:btnabout];
about.tintColor = [UIColor whiteColor];
UIButton *btnsetting = [UIButton buttonWithType:UIButtonTypeCustom];
btnsetting.frame = CGRectMake(0,8,30,30);
[btnsetting setBackgroundImage:[UIImage imageNamed:#"setting"] forState:UIControlStateNormal];
[btnsetting addTarget:self action:#selector(SettingData) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *setting = [[UIBarButtonItem alloc] initWithCustomView:btnsetting];
setting.tintColor = [UIColor whiteColor];
self.navigationItem.rightBarButtonItems =[NSArray arrayWithObjects:setting, about, nil];
// --- or if you want left side ----
// self.navigationItem.leftBarButtonItems =[NSArray arrayWithObjects:setting, about, nil];
[self.navigationItem setRightBarButtonItems:#[/*array of UIBarButtonItem*/] animated:NO]
Apple doc
to someone stuck with this problem in swift, here is the correct answer :
let barButton_array: [UIBarButtonItem] = [Button1, Button2]
navigationItem.setRightBarButtonItems(barButton_array, animated: false)
I need to show only arrow button and hide text of left navigation button. According to this link, I can do like this. But if I do like that, slide to go back feature will be destroyed.
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:nil action:nil];
So, I use like this to hide text.
self.navigationController.navigationBar.topItem.title = #"";
So far, it is okay. However, if my previous view's searchDisplayController is active and push to new view,it show left navigation button text. May I know how to do?
Copy and paste this line in every view controller:
self.navigationController.navigationItem.leftBarButtonItem.title = " "
Alternatively you can achieve this in storyboard/xib files using the following steps:-
Drag and drop a Navigation Item from object library onto your ViewController. Then select the ViewController.
Select that Navigation Item in the menu on left side(it will the one with back arrow and Title as text: "< Title").
Select Attribute Inspector on the right hand side and replace text: Title with an empty space.
Repeat these steps for all the the view controllers.
Hope it helps.
One solution you will have to add custom button for your requirement like this:
//create image instance add here back image
UIImage *imgBack = [UIImage imageNamed:#"image name here"];
//create UIButton instance for UIBarButtonItem
UIButton *btnBack = [UIButton buttonWithType:UIButtonTypeCustom];
[btnBack setImage:imgBack forState:UIControlStateNormal];
btnBack.frame = CGRectMake(0, 0, imgBack.size.width,imgBack.size.height);
[btnBack addTarget:self action:#selector(btnBackAction:) forControlEvents:UIControlEventTouchUpInside];
//create UIBarButtonItem instance
UIBarButtonItem *barBtnBackItem = [[UIBarButtonItem alloc] initWithCustomView:btnBack];
//set in UINavigationItem
self.navigationItem.leftBarButtonItem = barBtnBackItem;
Button method given below:
-(void)btnBackAction:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
EDIT : For slide swipe add in viewDidLoad method
//for enabling swipe gesture
if ([self.navigationController respondsToSelector:#selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
#Atif's answer is correct. I just want to add in that, instead of copying and pasting the code in all files, create a custom UINavigationController and implement the required code as mentioned by #Atif.(I cannot comment due to low rating.)
I want to add a thin line between items in my UIToolBar so I'm creating a UIBarButtonItem with a custom view like so:
UILabel *separatorLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 1, 44)];
separatorLabel.backgroundColor = [UIColor colorWithRGB:0xe5edec];
UIBarButtonItem *separator = [[UIBarButtonItem alloc] initWithCustomView:separatorLabel];
Then I add my separator to the items array:
[items addObjectsFromArray:[NSMutableArray arrayWithObjects:someButton1, separator, somebutton2, separator, someButton3, nil]];
I thought this would add 2 separators to my toolbar but it only adds the one at the end. Why is this?
I can fix this by creating 2 duplicate labels and UIBarButtonItem's, but is there any other better way?
Any given instance of UIView can only appear in once in the view hierarchy. If you think about the APIs defined on UIView, this is fairly obvious. For example, UIView has a frame property which defines it's location in the superview. The frame property wouldn't make sense if the viewed appeared in two places.
So you need multiple instances. You can streamline your code by defining a method that creates separators:
- (UILabel *)newSeparator
{
UILabel *separatorLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 1, 44)];
separatorLabel.backgroundColor = [UIColor colorWithRGB:0xe5edec];
UIBarButtonItem *separator = [[UIBarButtonItem alloc] initWithCustomView:separatorLabel];
return separator;
}
And then you can add your items like this:
[items addObjectsFromArray:#[button1, [self newSeparator], button2, [self newSeparator]];
Also, you don't need to use UILabel if you're only displaying a background color. You can just use UIView.
Yes,you just created only one UIBarButtonItem object,so it showed one.
I think the better way is creating a UIBarButtonItem subclass with custom label,then create two objects of the subclass.
I hope my answer can help you.
I think this is going to be a stupid question, but I can't seem to find the answer. I have a few simple lines of code to put a button in the navigation bar:
UIBarButtonItem *cancelButton=[[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:#"button-cancel.png"] style:UIBarButtonItemStylePlain target:self action:#selector(cancelPressed:)];
UINavigationItem *item = [[UINavigationItem alloc] init];
item.leftBarButtonItem = cancelButton;
item.hidesBackButton = YES;
[self.navigationBar pushNavigationItem:item animated:NO];
This button works fine, but it looks like this:
Any thoughts?
You probably want to create the bar button item using a custom view, where the custom view is a UIButton:
UIImage *cancelImage = [UIImage imageNamed:#"button-cancel"];
UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
cancelButton.frame = (CGRect){CGPointZero, cancelImage.size);
[cancelButton setImage:cancelImage forState:UIControlStateNormal];
UIBarButtonItem *cancelBarButton = [[UIBarButtonItem alloc] initWithCustomView:cancelButton];
Set your button (cancelButton) size according to the size of the button-cancel.png.
stopButton.frame = CGRectMake ();
Instead, create a custom type UIButton with your image. Set the target and selector of the UIbutton to what you wish the bar button item to do. Then initialize the bar button item as follows:
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
Where button is your UIButton using the desired image.
UIBarButtonItem/initWithImage: is typically used for making iconic buttons - not buttons that have text in them.
If you just want to change how the common textual UIBarButtonItem looks, you just need to set the background image of your bar button item. This way you don't have to have images for each button that contain your button text.
Docs: - (void)setBackgroundImage:(UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics
You can also set this app-wide by calling setBackgroundImage: on the UIBarButtonItem appearance proxy.
Lastly, note that you'll likely need to create a resizeable image to pass to setBackgroundImage. This will let your single image accomodate any button size. See UIImage/resizeableImageWithCapInsets:resizingMode: (iOS6) or UIImage/stretchableImageWithLeftCapWidth:topCapHeight: (pre iOS6)
You can certainly do what #Wain suggests but there are drawbacks. For one, your press-handler will no longer be sending a UIBarButtonItem as the 'sender'. That may not seem like much until you have a common handler that suddenly needs to determine if the sender is a UIBarButtonItem or a UIButton, or if you want to present a UIPopoverController against this BarButtonItem (but you only have the UIButton reference...)
I've read a couple similar questions regarding this topic on StackOverflow, but none of them apply to my case.
I have a UIToolbar on my application. The ToolBar has a layout like the following.
(Button #1) (Button #2) (Button #3) <--- Flex Controller ---> (Button #4)
What is the most efficient way to Show/Hide the Button #3 UIBarButtonItem since it is in the middle of the UIToolbar?
FOLLOW-UP
I am aware of the setItems:Animated method, but this seems like it could be problematic because all of my buttons are wired through IB and they have associated IBActions.
Does this mean that I, therefore, need to find a way to retain my Button #3 when it is hidden (since I am using ARC?) I'm looking for the most optimal/efficient method for adding and removing these objects. If I must recreate this list each time I have to show/hide my #3 button, that's what I will do.
It just seems so inefficient.
Looking in UIToolBar.h, there's the following method:
- (void)setItems:(NSArray *)items animated:(BOOL)animated; // will fade in or out or reorder and adjust spacing
You should just be able to do something like:
NSMutableArray *items = [toolBar.items mutableCopy];
[items removeObjectAtIndex:2];
[toolBar setItems:items animated:YES];
See if that works the way you're looking for
I would recommend building a helper function in the following manner:
- (void) setToolbarItems:(BOOL) button3Shown {
NSMutableArray *items = [NSMutableArray arrayWithCapacity:5];
[items addObject:button1];
[items addObject:button2];
if(button3Shown) {
[items addObject:button3];
}
[items addObject:[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]];
[items addObject:button4];
[self.toolbar setItems:[items copy] animated:YES];
}
This then has the benefit of being available whenever you need to update it and then you can easily add/remove the button. In order for this to work, you would need to have the buttons available in your implementation file as IBOutlets and they would be easily referenced for rebuilding the toolbar.
You need to reset the toolbar items, passing in a new array without the button you want to hide. See Hide UIToolbar UIBarButtonItems for exact details.
Creating different arrays of UIBarButtonItems and adding/removing to/from the UIToolbar at runtime may increase the complexity of execution.
I've done it using a different approach. Here are the snippets for achieving it.
Step 1 : Either create an IBOutlet or Create the UIToolbar instance.
UIToolbar *toolBar = [UIToolbar alloc] init];
Set the required styles if you want.
Step 2 : Create UIButton type instances.
UIButton *button1, *button2;
Step 3: Assign images (if required) to the buttons.
button1 = [UIButton alloc] initWithFrame:CGRectMake(0.f,0.f,30.f,30.f);
[button1 setBackgroundImage:[UIImage imageNamed:#"image1.png"] forState:UIControlStateNormal];
button2 = [UIButton alloc] initWithFrame:CGRectMake(0.f,0.f,30.f,30.f);
[button2 setBackgroundImage:[UIImage imageNamed:#"image2.png"] forState:UIControlStateNormal];
Step 4: Create UIBarButtonItem instances with above buttons as custom view.
UIBarButtonItem *toolbarbuttonItem1 = [UIBarButtonItem alloc] initWithCustomView:button1];
UIBarButtonItem *toolbarbuttonItem2 = [UIBarButtonItem alloc] initWithCustomView:button1];
Step 5: Assign the bar button items to the toolbar.
toolBar.items = [NSArray arrayWithObjects:toolbarbuttonItem1,toolbarbuttonItem2,nil];
Step 6: Add the Toolbar to the VIEW.
Step 7: Create a UIButton IBOutlet in the VIEW and assign a click event.
Invoke the following helper method int the click event to toggle hiding.
- (void) toggleToolbarButtons {
button1.hidden = !button1.hidden;
button2.hidden = !button2.hidden;
}
Hope that helps!!
With existing references to toolbar items item1, item2 and item3, the most straightforward way is to remove all items and then re-add each item in the order you want it. This code is written from the perspective of being within a UIToolbar subclass:
let flexSpace: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let fixedSpace: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
self.items?.removeAll()
self.items?.append(flexSpace)
self.items?.append(item1)
self.items?.append(fixedSpace)
self.items?.append(item3)
self.items?.append(fixedSpace)
self.items?.append(item2)
These item references can be from IBOutlets. Change these references to be strong rather than the IB-inserted-default weak references, so that the UIToolbar will maintain references to these items even when they have been removed, thus maintaining the relevant IBAction function relationships.
To remove a specific item from your UIToolbar from within a UIToolbar subclass, without having to re-set all items:
if let index = self.items?.index(of: buttonToRemove) {
self.items?.remove(at: index)
}
If not within a UIToolbar subclass, simply replace self with a reference to the UIToolbar throughout.