I've got two viewControllers who each need a login button in the top right corner of the navigation bar.
In viewController1#viewDidLoad, I set up the rightBarButtonItem like so (abbreviated):
// set up the login button on the right
UIButton *loginButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *loginImage = [UIImage imageNamed:#"btn_login.png"];
[loginButton setBackgroundImage:loginImage forState:UIControlStateNormal];
[loginButton setFrame:CGRectMake(0, 0, loginImage.size.width, loginImage.size.height)];
[loginButton setTag:1111111];
UIBarButtonItem *loginItem = [[UIBarButtonItem alloc] initWithCustomView:loginButton];
self.navigationItem.rightBarButtonItem = loginItem;
[loginItem release];
I tag it so that in viewWillAppear, I can use viewWithTag:1111111 to figure out if it needs to be hidden or visible, based on whether or not the user is logged in. Simple.
((UIButton *)[self.navigationController.view viewWithTag:LOGIN_BUTTON_TAG]).hidden = true;
When viewController2 gets pushed onto the stack, I basically run the same code to set up my rightBarButtonItem, but I give it a different tag (i.e. 222222).
In viewController2#viewWillAppear, when I look for the viewWithTag:222222, it comes back as null, and so I can't hide/show it.
I noticed though that if I use the same tag as I used in viewController1 (1111111), I can get to it.
Why is this? My tags are actually set up at the top of the file as constants, so it seems inelegant to copy the random number from vc1 into vc2 just so I can get this to work. Instead, I'd like to understand why vc2's tag isn't getting applied to the rightBarButtonItem, and why vc1's tag is still preserved even though I'm in a different viewController.
The easiest most scalable solution is to avoid using viewWithTag: like the plague.
Make the UIBarButtonItem an ivar and then you have instant access to it without any ambiguities.
Replace
[loginButton setTag:1111111];
with
self.loginButton = loginButton;
Then to retrieve do this
self.loginButton;
instead of
(UIButton *)[[[self.navigationController visibleViewController] view] viewWithTag:LOGIN_BUTTON_TAG];
I know which one looks more elegant and robust to me
If I understand correctly what you are doing, the first thing I would try is to check, before this line, in both viewcontrollers:
((UIButton *)[self.navigationController.view viewWithTag:LOGIN_BUTTON_TAG]).hidden = true;
to put a
NSLog("View is %#", self.navigationController.view);
to check if you are sending viewWithTag to the correct view.
Not sure about this, but Are you trying to access to a view, controller by an UIViewcontroller, loaded inside an UINavigationcontroller?
In this case I would use:
(UIButton *)[[[self.navigationController visibleViewController] view] viewWithTag:LOGIN_BUTTON_TAG];
Related
I added the info button to the navigation bar using below code:
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
[infoButton addTarget:self action:#selector(showImage:)
forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:infoButton];
Now i want to hide this button at some part of the code based on some conditions. But i didn't find any hide property for right bar button item in the navigation bar?
For making hidden, try with assigning nil to your rightBarButtonItem like below.
self.navigationItem.rightBarButtonItem = nil ;
Best option is to use buttonItem.enabled = NO to indicate the functionality isn't available at the moment. That should, in most cases, be the right behavior.
However if you intend to make it disappear, the best way would be to store a reference to the bar button. Set the rightBarButtonItem to nil when you want it to disappear and set it to the stored reference when you want it to be displayed.
If you have multiple UIBarButtonItems and you just want to remove one, you can do this:
NSMutableArray *barButtonItems = [self.toobbar.items mutableCopy];
[barButtonItems removeObject:self.buttonToRemove];
[self.toolbar setItems:[barButtonItems copy] animated:NO];
If you just want to 'hide' it visually:
Swift 3:
self.navigationItem.rightBarButtonItem?.tintColor = UIColor.clear
self.navigationItem.rightBarButtonItem?.isEnabled = false
I want to know if it is possible to subclass UIButton someway to add another component to it such as a preloader or progress indicator.
In pseudo code what I have in mind is something like the following.
UIButtonWithProgress *button =[[UIButtonWithProgress alloc]init]
[button.progressbar setProgress:50%]
In iOS the class for a progress bar is UIProgressView. If you really wanted to add a UIButton to a UIProgressBar (as in the question title). Then you can use the UIView method addSubView: For example:
UIButtonWithProgress* button = [[UIButtonWithProgress alloc] init];
UIProgressView* progressView = [[UIProgressView alloc] init];
[button addSubview:progressView];
progressView.progress = 0.5;
I think what you really want to do is create your own UIControl (so you should subclass that instead). You can override the method drawRect: to use quartz to draw your own progress bar animation, and the touchesBegan:withEvent: to detect the user tapping on your custom control, as well as adding any other methods you wish to control the state of the control (such as the progress level).
Note: In my code example I also give an example of correctly setting the progress of the UIProgressBar, just in case you were unclear.
I am tearing my hair out on this one. My client wants to add a button to the left of a search bar like the example below:
(source: erik.co.uk)
But I just can't figure out how to do it. Apple don't seem to provide any documented method for adding custom buttons to a UISearchBar, let alone to the left of the search bar.
I've tried hacking around in Interface Builder adding a UIToolbar with a button in it to the left but I cannot find any combination of styles where the two line up properly to give the impression that they are one. There is always what looks like one pixel difference in the vertical alignment as you can see from the picture below:
(source: erik.co.uk)
I've searched around and just can't find the answer, but as we can see from the screenshot it must be possible!
Thank you in advance for your help.
Erik
Use a navigation bar instead of a toolbar. Set the search bar to the navigation bar's title view.
In Interface Builder:
Result:
You can replace the Bookmark image instead, and adjust its offset if necessary.
For example:
[self.searchDisplayController.searchBar setImage:[UIImage imageNamed:#"plus2"] forSearchBarIcon:UISearchBarIconBookmark state:UIControlStateNormal];
[self.searchDisplayController.searchBar setPositionAdjustment:UIOffsetMake(-10, 0) forSearchBarIcon:UISearchBarIconBookmark];
Handle the button event in the delegate method:
- (void)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar
This is how it looks:
The first solution is to use UINavigationBar instead of UIToolbar, as KennyTM noticed. But you may not be satisfied with Navigation bar, like in my case, when I need to use 3 buttons (Navigation bar is allow to use only 2 buttons) - see the left picture. This is how I did it:
Use Toolbar with 3 buttons and Flexible Space Bar Button Item in the place where search bar should be placed.
Put search bar on (not in) the toolbar. To do so in Interface Builder, do not drag & drop the search bar on the toolbar. Instead, put it somewhere nearby and then move it to place using the arrow keys on the keyboard (or by changing X & Y position in Interface Builder).
Search bar left black line under it (see the right picture). To hide it I put one additional view with the height 1px and a white background over it.
It looks a bit dirty for me, so if you have a better solution, let me know.
The easiest solution is to add your SearchBar in TOP of your Toolbar, (not in), I give you the best solution I use in my company eBuildy:
UIBarButtonItem *mySettingsButton = [[UIBarButtonItem alloc] initWithTitle:#"Settings" style:UIBarButtonItemStyleBordered target:self action:#selector(refresh)];
UIBarButtonItem *mySpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *myRefreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(refresh)];
UIToolbar *myTopToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0,0,320,40)];
UISearchBar *mySearchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(70,1,220,40)];
[myTopToolbar setItems:[NSArray arrayWithObjects:mySettingsButton,mySpacer,myRefreshButton, nil] animated:NO];
[self.view addSubview:myTopToolbar];
[self.view addSubview:mySearchBar];
answering an old question here but i was struggling with this one myself recently and found some shortcomings with the other answers for the situation i was trying to address. here's what i did in a subclass of UISearchBar:
first add a UIButton property (here "selectButton"). then override the initWithFrame method and do something similar to the following:
-(id)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame])
{
self.selectButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.selectButton.contentEdgeInsets = (UIEdgeInsets){.left=4,.right=4};
[self.selectButton addTarget:self action:#selector(pressedButton:) forControlEvents:UIControlEventTouchUpInside];
self.selectButton.titleLabel.numberOfLines = 1;
self.selectButton.titleLabel.adjustsFontSizeToFitWidth = YES;
self.selectButton.titleLabel.lineBreakMode = UILineBreakModeClip;
[self addSubview:self.selectButton];
[self.selectButton setFrame:CGRectMake(5, 6, 60, 31)];
}
return self;
}
Now you want to override the layout subviews method to resize the searchbar to the appropriate width, depending on whether or not the cancel button is showing. That should look something like this:
-(void)layoutSubviews
{
[super layoutSubviews];
float cancelButtonWidth = 65.0;
UITextField *searchField = [self.subviews objectAtIndex:1];
if (self.showsCancelButton == YES)
[searchField setFrame:CGRectMake(70, 6, self.frame.size.width - 70 - cancelButtonWidth, 31)];
else
[searchField setFrame:CGRectMake(70, 6, self.frame.size.width - 70, 31)];
}
Note that in the above method I added a constant for the cancelButtonWidth. I tried adding code to get the width from [self cancelButton] but that seems only accessible at runtime and doesn't allow the project to compile. In any case this should be a good start for what you need
If you want a custom button on the right, taking place of the Cancel button, just use this code (valid for iOS 9 and up):
[self.searchBar setShowsCancelButton:YES];
[[UIBarButtonItem appearanceWhenContainedIn:[self.searchBar class], nil] setTitle:#""];
[[UIBarButtonItem appearanceWhenContainedIn:[self.searchBar class], nil] setImage:[UIImage imageNamed:#"search"]];
I have a UIBarButtonItem on a navigation bar. I'd like to make it arrow shaped. By that I mean I want it to be square except for a pointy side. Does anyone know how to do this?
Thanks!
I wrestled with several approaches on this one and finally figured it out using all the answers from several sources. There are a couple of tricks; this snippet will show it all (I was creating a custom rightNavButton, but you can adapt this easily for any UIBarButtonItem):
// Produces a nice "right arrow" style button that mirrors the back arrow
// automatically added by the navController
//
UIImage *buttonImage = [UIImage imageNamed:#"forwardButton.png"];
UIButton *forwardButton = [UIButton buttonWithType:UIButtonTypeCustom];
[forwardButton setBackgroundImage:buttonImage forState:UIControlStateNormal];
[forwardButton setTitle:#"Meter" forState:UIControlStateNormal];
forwardButton.font = [UIFont boldSystemFontOfSize:12];
forwardButton.frame = CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height);
[forwardButton addTarget:self action:#selector(showMeter)
forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithCustomView:forwardButton];
Note: it turns out that if you try to set the target and action directly on the button item after assigning a custom view using a UIButton that it won't take - you have to set the target and action on the button itself - apparently the navBar uses the button provided verbatim.
This is the image I was using on the opaque black navBar (you can use anything, obviously): http://raretiger.com/images/forwardbutton.png
Make a custom background. See Creating a left-arrow button (like UINavigationBar's "back" style) on a UIToolbar for how.
You may -pushNavigationItem:animated: to make the built-in back button appear, although you cannot assign custom actions to it.
For undocumented methods, you may use
UIButton* backButton = [UIButton buttonWithType:101];
to create a back "button".
You could create an UIImageView or an UIButton with the required image then use:
[[UIBarButtonItem alloc] initWithCustomView: arrowButton];
Hope this helps.
continuing on #Plamen Dragozov's idea: i noticed if you leave the uibutton as is, it would trigger a picture adjustment when touched and that is quite the opposite what a normal navigation back button does. So what I did was to uncheck "highlighted adjusts Image" and it works like a charm.
hope this helps newbies like me.
More elegant solution:
UIBarButtonItem * backButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Title" style:UIBarButtonItemStylePlain target:self action:#selector(back:)];
[backButtonItem setBackgroundImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
self.navigationItem.leftBarButtonItem = backButtonItem;
My problem was renaming the back button that appears on the pushed view controller. I found a dirty workaround and if you ignore the non-ideal animation problem, you'll get a back button with the title you want.
The trick is to change the title of the first VC in viewWillDisappear and re-set it of course in viewWillAppear
(Needless to say, by default, if there is no leftBarButtonItem set, UINavigationController will show a back button with the title of the VC that pushed the current VC)
In the VC where you push your current VC, do
-(void) viewWillDisappear:(BOOL) animated {
[super viewWillDisappear:animated];
self.title = #"Back";
}
-(void) viewWillAppear:(BOOL) animated {
[super viewWillAppear:animated];
self.title = #"Original Title";
}
Does anyone know how to add two system buttons to the top right side of my navigation toolbar? I know that custom buttons can be added, and I really don't understand why the system buttons can't do this too.
And I really need it. I need an add button and an edit button.
Edit to reorder and delete table rows.
Add to add a new row.
I can't use the bottom toolbar because I have a tabbar there.
Could somebody help me out?
Something like this should work (substitute your own images and action methods):
#define ACTIONEDIT 0
#define ACTIONADD 1
...
UISegmentedControl* segmentedControl = [[UISegmentedControl alloc]
initWithItems: [NSArray arrayWithObjects:
[UIImage imageNamed:#"icon-edit.png"],
[UIImage imageNamed:#"icon-add.png"],
nil]
];
[segmentedControl addTarget:self
action:#selector(segmentAction:)
forControlEvents:UIControlEventValueChanged];
segmentedControl.frame = CGRectMake(0, 0, 90, 30);
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.momentary = YES;
[segmentedControl setEnabled:YES forSegmentAtIndex:ACTIONEDIT];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithCustomView:segmentedControl];
...
- (void)segmentAction:(id)sender
{
UISegmentedControl* segCtl = sender;
int action = [segCtl selectedSegmentIndex];
switch (action) {
case ACTIONADD:
[self addToList];
break;
case ACTIONEDIT:
[self editList];
break;
}
}
With the default navigation bar, you can only have three buttons, unless I'm missing something. One on the left, one in the center, and one on the right. Even if you create a smaller button and think you have enough space, the touches will all register to the same button (whichever is linked to the right or left). If you want to get functionality like google's navbars, I would suggest implementing it yourself. It really wouldn't be that difficult, and you would get exactly the functionality that you want. If you decide to do this, I'm sure SO can guide you through difficult parts.
I wonder what would happen if you'd use a custom view for your UINavigationItem:
myViewController.navigationItem.titleView = myCustomView;
I imagine the titleView might expand all the way to the right if you don't have a button there. I'noticed that title text gets more space if there is no right button.
Then you could add a label (for the title) and your two buttons to that custom view.