Rename standard "edit" button in tableview - iphone

I managed (with lots of trial and error) to have my tableview provide only reordering functionality, i.e. my tableview is editable but does not display "delete icons" nor indents the rows upon clicking on the edit button.
Now I would like the button to read "sort" instead of "edit".
I naively tried this:
self.navigationItem.leftBarButtonItem = self.editButtonItem;
self.navigationItem.leftBarButtonItem.title = #"Sort";
which works only once, i.e. it is correctly labeled "Sort", once clicked it renames to "Done", but then--as expected--it re-renames to "Edit".
In order to fix this, I deployed my "own" button on the navbar. This solution works--I can get the button to control the tableview editing mode, reload the data upon change, rename itself, etc--but I cannot get it to "stay highlighted", i.e. the default behaviour of the "Edit" button in a tableview.
Now my question is either:
a) Is there a way to rename (and keep it renamed, e.g. through a callback) the standard "Edit" button?
or
b) Is there a way to have a button behave "modally", i.e. stay selected, like the standard "Edit" button?
Thanks for any idea you might have.

You can put your changes in the - (void) setEditing:(BOOL)editing animated:(BOOL)animated method in your view controller.
- (void) setEditing:(BOOL)editing animated:(BOOL)animated {
//Do super before, it will change the name of the editing button
[super setEditing:editing animated:animated];
if (editing) {
self.navigationItem.leftBarButtonItem.title = #"Done";
self.navigationItem.leftBarButtonItem.style = UIBarButtonItemStyleDone;
}
else {
self.navigationItem.leftBarButtonItem.title = #"Sort";
self.navigationItem.leftBarButtonItem.style = UIBarButtonItemStyleBordered;
}
}

Related

UIToolbar items disappear - Weird bug in ios6

This is the current setup.
I have the navigationController's toolbar with 5 buttons, and tapping on them hides the toolbar for 2 seconds, and then shows the toolbar again (except the 5th button - which brings up an actionsheet with buttons (ACTION & CANCEL)).
On tapping on the 1-4 buttons, I do a self.navigationController.toolbarHidden = YES; and after exactly 2 seconds, I set the self.navigationController.toolbarHidden = NO; and this brings back the toolbar, and everything's fine.
On tapping the 5th button, which brings up action sheet.
If i tap on CANCEL actionsheet => actionSheet dismissed => Toolbar is fine.
If I tap on ACTION button I do a self.navigationController.toolbarHidden = YES; and after 2 seconds... self.navigationController.toolbarHidden = NO;
but now... The toolbar buttons are GONE.
Further investigating...
I can see the the toolbarButtons seem to have their alpha values set to 0.
I have no idea why the toolbar items' alpha are set to value = 0 after actionsheet operation.
Can anyone tell me the root cause for this?
Have you tried setting the toolbar items array to nil? I had this same problem and it turned out that putting a check around when you set the toolbar's items seemed to work:
if ([self.navigationController.toolbar.items count] > 0) {
[self.navigationController.toolbar setItems:nil];
}
[self.navigationController.toolbar setItems:toolbarItems]; //toolbarItems is your array of UIBarButtonItems.
I managed to fix the issue in a different way. I hide the toolbar when the action sheet comes up, and after the buttonAction(), I essentially show the toolbar again.
This solves the problem where the toolbarItems disappear.
But the reason as to why the toolbarItems disappear and set alpha=0 is still a mystery for me. If anyone finds out the reason, please let me know :)
I had the same issue and reproduced it in one of the samples. It appears to be a bug in iOS6 when setting up toolbar items manually in loadView / viewDidLoad, then later calling an ActionSheet.
The code below is a workaround it -
-(void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSArray* items = self.toolbarItems;
[self setToolbarItems:nil];
[self setToolbarItems:items animated:NO];
}
I solve it by moving action code to separate method and then calling it through sending message performSelector:withObject:afterDelay: with 0.25f second delay
Example:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
[self performSelector:#selector(logout) withObject:nil afterDelay:0.25f];
}
}
I don't know if it's the case, I found out that the disappeared items were actually in the toolbar, but placed over the bottom of the view. Maybe resetting them on certain circumstances may cause autolayout issues.
I fixed it by calling the setNeedLayout method on the viewcontroller's view (not the navigationControllers')
self.toolbarItems = toolButtons;
[self.view setNeedsLayout];

Replace or extend UIBarButtonItem selector

I'm trying to make a custom UIToolbar which handles rotation and custom arrangement. When it's in portrait mode some of the barbuttonitems will not be visible, so I add a "more" button from which pops up a little view with these items. My problem is that when my current orientation is portrait and when I select a visible barbuttonitem ( which is not in popup ) I want to close the popup if it's open. I want the smae behavior for the uibarbuttons in the popupview to close the popup after tap.
So I'm trying to replace somehow the UIBarButtonItem's selector with my own, in which I call the already defined actions, like this :
-(SEL)extendOriginal:(UIBarButtonItem *) uibb
{
if (popup) popup.hidden = YES;
[uibb.target performSelector:uibb.action];
// return ?? do what ??
}
But how do I replace the original selector to call this custom method with my custom UIToolbar as its target ? Or how could I "extend" the original selector with this call ? Sorry if question is lame :)
Edit: In other words, I want 2 actions with 2 separate targets to be executed when UIBarButtonItem is tapped.
Thanks!
This -(SEL)extendOriginal:(UIBarButtonItem *) uibb doesn't make any sense.
I assume your are setting the target and the action of the bar button item somewhere. There you can set any method with one argument id or UIBarButtonItem* as the selector.
Therefore try to change your code to
- (void)myMethod:(UIBarButtonItem *) uibb
{
if (popup) popup.hidden = YES;
// do cool stuff here
}
and set the target as
[[UIBarButtonItem alloc] initWithTitle: #"Blabla" style: UIBarButtonItemStylePlain target: self action: #selector(myMethod:)];
Finally, I found a way to do it, not the prettiest, but it works.
In the custom toolbar class I created in its layout method a UITapGestureRecognizer to handle taps. I've set the cancelsTouchesInView to false and in the
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
method I'm returning true only for my toolbar's subviews.
This way the original target and action of each UIBarButtonItem remains the same and the supplementary code to handle my popup is the action of the UIGestureRecognizer.
Another problem was to distinguish between the tapped items on the toolbar (the tapped view in touch.view is some undocumented view, yay), eventually I did it with some BOOL iVars.
I hope this will help somebody with the same problem.

KVO with two UIButtons

I have a custom UITableViewCell subclass that gets presented in a UITableView in a UIPopoverController. The UIPopoverController is presented from a UIBarButtonItem.
When selections in the UITableViewCell are made, I send a NSNotification to the UIViewController class that is presenting the UIPopoverController. The selections in the table update my view in my viewController.
Now, there is a requirement to have another UIBarButtonItem that does EXACTLY the same thing as one of the buttons in the UITableViewCell in the popover. Basically the use case is that this feature seems to be the most commonly used feature in our popover and they want an easy way to just turn it on and off from another button.
So what I did was create a new UIBarButtonItem, and have a target attached to it:
- (void)FilterOn:(id)sender {
isFilterOn = !isFilterOn;
if ([sender isKindOfClass:[UIBarButtonItem class]]) {
UIBarButtonItem *filter = (UIBarButtonItem *)sender;
if (isFilterOn) {
[filter setTitle:#"Filter On"];
[self DoFilter];
}
else {
[aboutMeBBI setTitle:#"Filter Off"];
[self ClearFilter];
}
}
}
So this part works. It updates the model, the title of the button changes. The problem is, let's say I turn filter on, then in the popover, I turn the filter off. I pass the notification to my viewController class to its normal update method that handles filtering and a bunch of other stuff. I added this simple snippet to when the filter button is pressed from the popover:
- (void)FilterSortOptionDidSelect:(NSNotification *)notification {
NSDictionary *userDict = [notification userInfo];
NSInteger theTag = [[userDict objectForKey:#"CellTag"] integerValue];
switch (theTag) {
case FILTER: {
// update Model
[self.FilterBarButtonItem setTitle:#"Filter off"];
}
break;
default:
break;
}
}
This isn't quite what I want though since if I click back on the filter button since for starters, if I click back on the Filter button, it'll still say Filter Off, and do the action for Filter Off, and then if I click it again, it'll do Filter on actions. I try to fake it in the switch statement by changing the title, but that isn't really what I want to do. I read a little about key value observing and I wasn't sure if I could use something like that here, to register my buttons state with that of a button in a UITableViewCell subclass in a UIPopoverController. If anyone has any ideas that would be great. Thanks.

iPhone iOS 4 UIButton toggle highlighted state on and off

I got a UIButton with style "Info Dark" set up in interface builder in my iPhone 4 app. One of the properties of the button is "Highlighted", which displays a white highlight around the button.
I would like to toggle this white highlight on and off, indicating if the button function is active or not.
The button is linked for "Touch up inside" event in the interface builder with this callback:
infoButton.highlighted = !infoButton.highlighted;
After the first touch, the highlight disappears and does not toggle as I expect it to. What else do I need to do to make the highlight toggle and display the state of the button?
Thank you!
Update:
When loaded from the interface builder, the button stays highlighted, even as the view appears/disappears. What causes this to happen is the "shows touch on highlight" interface builder property. If I assign the code above to another button, the info button highlights on and off as expected. However, the touches of the info button itself interfere with the above code, causing the button to lose the "touch" highlight
Update 2: I added another info button, directly below the first info button, in the interface builder and made it glow permanently. To create the appearance of the toggle, I hide and unhide the glowInfoButton below the real one. This works as expected:
infoButton.highlighted = NO;
glowInfoButton.highlighted = YES;
glowInfoButton.enabled = NO;
glowInfoButton.hidden = YES;
- (IBAction)toggleInfoMode:(id)sender {
// infoButton.selected = !infoButton.selected;
glowInfoButton.hidden = !glowInfoButton.hidden;
}
The Highlighted image is what displays when the UIButton is being pressed, and is controlled within the UIButton itself.
You're looking for the Selected property. You can set a Selected Image in IB, and then put a infoButton.selected = !infoButton.isSelected; in your TouchUpInside callback.
The highlighted property doesn't work like that, buttons aren't toggles.
It's just to know if the button is being pressed, if I'm correct.
If you want to implement that functionality, I recommend you subclass UIButton or UIControl.
Now that I see what you really were after I would advise subclass UIButton and check for a call to an event then toggle highlight state accordingly. You can do this without adding the dummy button.
in a custom button class implementation file place the following code, or similar:
#import "HighlightedButton.h"
#implementation HighlightedButton
BOOL currentHighlightState;
-(void)toggleHighlight:(id)sender {
self.highlighted = currentHighlightState;
}
-(void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
//get the string indicating the action called
NSString *actionString = NSStringFromSelector(action);
//get the string for the action that you want to check for
NSString *touchUpInsideMethodName = [[self actionsForTarget:target forControlEvent:UIControlEventTouchUpInside] lastObject];
if ([touchUpInsideMethodName isEqualToString:actionString]){
//toggle variable
currentHighlightState = !currentHighlightState;
//allow the call to pass through
[super sendAction:action to:target forEvent:event];
//toggle the property after a delay (to make sure the event has processed)
[self performSelector:#selector(toggleHighlight:) withObject:nil afterDelay:.2];
} else {
//not an event we are interested in, allow it pass through with no additional action
[super sendAction:action to:target forEvent:event];
}
}
#end
That was a quick run at a proper solution, there is a flicker on toggle that you may not like. I am sure if you play around with some changes that can be corrected. I tried it and actually like it for your stated case.
The highlighted state of a UIButton is simply setting the button's alpha to 0.5f. So if you set the button to not change on highlight, then just toggle the alpha between 0.1 and 0.5.
For example:
- (void)buttonPressed:(id)sender {
if((((UIButton*)sender).alpha) != 1.0f){
[((UIButton*)sender) setAlpha:1.0f];
} else {
[((UIButton*)sender) setAlpha:0.5f];
}
}
Perhaps what you really want is
infoButton.enabled = NO;
This will dim the button and disable touches when set to no, allow normal operation when set to YES.
or in your case:
infoButton.enabled = !infoButton.isEnabled;
to toggle the availability of same.
If you put this in your touchupinside event, of course it will work only the first time. After that is disabled and does not receive touch events. You would put it in another method that decides whether or not the button should be enabled.
If you truly want it to change each time it is pressed then you probably should use a switch or you may look at the -imageForState, -setTitle:forState and/or -setTitleColor:forState methods. If you want to toggle the appearance each time it is touched, you could change these.

Act on click of a button on the Nav Bar for moreNavigationController -- Can't pushviewcontroller

Okay, here is my issue: My app has a display of categories in the tab bar at the bottom of the iPhoneOS screen. This only allows 5 categories before it presents the MORE button. I have over 25 (please do not answer this by saying: "Rethink your application...etc" -- that was rudely said before. They are food, drink, etc categories and cannot be changed). I want to allow the user to put their favorites on the home page. The Apple moreNavigationController editing system only allows 20 tab bar items to be rearranged due to space constraints on the editing page. This is not enough so i need to implement my own Editing screen. I set the rightBarButtonItem to nil and created my own. Using NSLog, i can see the "click" happens when clicking the EDIT button, but I cannot push using pushViewController. Nothing happens. I think it has something to do with the navigationController I am addressing...but i am not sure. ps: This all happens in my App Delegate which DOES act as both UITabBarControllerDelegate & UINavigationControllerDelegate.
I tried to do the following:
- ( void )navigationController:( UINavigationController * )navigationController_local willShowViewController:( UIViewController * )viewController_local animated:( BOOL )animated
{
UIViewController * currentController = navigationController_local.visibleViewController;
UIViewController * nextController = viewController_local;
// Do whatever here.
NSLog(#"Nav contoller willShowViewController fired\n'%#'\n'%#'\nThere are currently: %d views on the stack\n",currentController,nextController,[self.navigationController.viewControllers count]);
if ( [nextController isKindOfClass:NSClassFromString(#"UIMoreListController")])
{
UINavigationBar *morenavbar = navigationController_local.navigationBar;
UINavigationItem *morenavitem = morenavbar.topItem;
morenavitem.rightBarButtonItem = nil;
NSLog(#"Is a UIMoreListController\n");
UIBarButtonItem *editTabBarButton = [[UIBarButtonItem alloc]
initWithTitle:#"Edit"
style:UIBarButtonItemStylePlain
target:self
action:#selector(editTabBar:)];
morenavitem.rightBarButtonItem = editTabBarButton;
[editTabBarButton release];
}
}
This works to place an EDIT button at the top right of the screen -- mimicking Apple's look and feel... but when that button is clicked, you cannot exit the darn moreNavigationController.
I have tried many things. UIAlerts work, etc...but pushing (or popping -- even popping to root view) a view controller on the stack does not.
- (void) editTabBar:(id)sender {
NSLog(#"clicked edit tabbar\n");
NSLog(#"Total count of controllers: %d\n",[self.navigationController.viewControllers count]);
TabBarViewController *tabBarViewController2 = [[TabBarViewController alloc] initWithNibName:#"TabBarView" bundle:nil];
tabBarViewController2.navigationItem.title=#"Edit Tab Bar";
[self.navigationController pushViewController:tabBarViewController2 animated:YES];
[tabBarViewController2 release];
NSLog(#"finished edit tabbar\n");
}
If you click the edit button on the moreNavigationController's display page, you get the log entries like expected AND (this is strange) the views on the stack climbs -- but no page change occurs. I marked it down to not using the correct navigation controller...but I am lost on how to find which one TO use.
this is a weird one too. In the edit function if i just do this:
- (void) editTabBar:(id)sender {
self.tabBarController.selectedIndex = 0;
}
It DOES take me home (to tabbarcontroller 0)
BUT doing this:
- (void) editTabBar:(id)sender {
[self.navigationController popToRootViewControllerAnimated:YES];
}
does not work.
Does the moreNavigationController have some special quality that screws with the rest of the system?
I would try reimplementing the whole "More" functionality from scratch. In other words, store the four home tabs in your user defaults and add a dummy fifth tab that switches to your own complete reimplementation of the more view controller stack.
You could even write a lightweight subclass of UITabBarController that handled this for you.
UITabBarController is evil, so I wouldn't be at all surprised if MoreController had some special properties, too.
I have had success intercepting the More Controller in shouldSelectViewController to change the data source; you may be able to find some workaround there.
PS I am inclined to agree that you could consider redesigning your app so that you didn't need an unlimited number of viewControllers attached to the tab bar just to select categories; you might have better luck using a tool bar with a single, scrollable, custom view in it. If that's really the best way of picking categories for your app, of course.