views:

14713

answers:

10

I have a NavigationBar app with two views: a parent and a sub view. In the sub view I'm adding a button to the right corner as follows:

- (void)viewDidLoad {
    UIBarButtonItem *tempButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"lock-unlocked.png"] style:UIBarButtonItemStylePlain target:self action:@selector(lockScreen)];
    self.navigationItem.rightBarButtonItem = tempButton;
    [tempButton release];
}

When that button is clicked I want to change the image of this rightBarButtonItem and disable the leftBarButtonItem (which was added automatically by the controller). Basically have two states of a button, locked and unlocked.

Question 1: The only way I can find how to change the image is to create a new UIButtonItem with a new image and replace rightBarButtonItem with that new one. But I'm wondering if there's a way to just change the image without creating a new UIBarButtonItem. Am I creating a memory leak if I keep creating new UIBarButtonItem?

Question 2: How can I get a hold of self.navigationItem.leftBarButtonItem and disable/enable it? I don't create that one manually, it's created automatically for me by the controller. I don't see any method/property on UIBarButtonItem to enable/disable user interaction with it.

+8  A: 

Question 1: Declare UIBarButtonItem *tempButton in the interface

@interface MyAppDelegate : NSObject <UIApplicationDelegate> {
    UIBarButtonItem *tempButton;
}

@property (nonatomic, retain) UIBarButtonItem *tempButton;

and synthesize it in the implementation.

@synthesize tempButton;

Create the object in viewDidLoad similiar to how you are now.

- (void)viewDidLoad {
  tempButtom = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"lock-unlocked.png"] style:UIBarButtonItemStylePlain target:self action:@selector(lockScreen)];
  self.navigationItem.rightBarButtonItem = tempButton;
}

But don't release it here, release it in the dealloc method normally found at the bottom.

Then when lockScreen is called do

tempButton.image = [UIImage imageNamed:@"myImage.png"]

I don't have an answer for question 2, im afraid!

Gcoop
That approach looks much better. Thanks!
subjective-c
A tiny issue for the 3rd piece of code: you CAN release tempButton after alloc because it's already been retained when you declare it as a property.
iPhoney
+2  A: 

In regard to question 2, use the 'enabled' property:

self.navigationItem.leftBarButtonItem.enabled = NO;

eelco
+3  A: 

I can't understand if you have a navigationController, but in this case to disable the back button you need to call:

self.navigationItem.hidesBackButton = YES;
Dzamir
A: 

I was not able to disable/grey out a NavBar button with:

self.navigationItem.leftBarButtonItem.enabled = NO;

...but hiding the back button works well!

self.navigationItem.hidesBackButton = YES;

Thanks Dzamir!

Rei
A: 

A comment on the statement that you can release the button (not in the dealloc) because it's been retained...

I got errors doing that because I wanted to address the button (cause it to appear) in another method. Errors were resolved simply by removing the release statement from the viewDidLoad method.

So, it sounds like you have to leave out the release if you want to affect the button anywhere else in code... (release in dealloc of course...)

A: 

The self.navigationItem.leftBarButtonItem is nil, this is why setting enabled=NO does not work.

The hidesBackButton=YES works, but is less elegant, as the button disappears. Disabled would be more nice.

Anyone know solution how to disable the Back-button?

I have found a solution where you create a new button, but these are less than elegant.Such as solutions mentioned here: http://stackoverflow.com/questions/227078/creating-a-left-arrow-button-like-uinavigationbars-back-style-on-a-uitoolbar

Simo Salminen
A: 

The following works fine, if as mentioned the leftBarButtonItem is pointing to an object

self.navigationItem.leftBarButtonItem.enabled = NO;

Thanks eelco, I just used this

Steffan Karagianis
A: 

Another comment on the release thing, my understanding and someone please correct me if I am wrong, is that because in the code snippet the button was assigned through direct access then you should not release it there because it only has a retain count of 1 from the 'alloc'. But if it was assigned using a 'self.' notation or the setter [self setTempButtom:] it will have a retain count of 2 because the property declaration in the header indicated a retain so the synthesized mutator method will issue a retain message. In that case you can release in the viewDidLoad method to get the retain count down to one so that in the dealloc method the release will take the retain count to 0.

This is just how I rationalize the thing as I am pretty new to this whole obj-c thing and manual memory management.

singe
A: 

Using "hidesBackButton=YES" is really not an elegant solution, cause it HIDES the button which is not what we want. An acceptable work-around would be adding a UILabel to the window just over the back button at least disabling the touches on the button.

Add this method to your AppDelegate class:

- (void) disableLeftBarButtonItemOnNavbar:(BOOL)disable
{
    static UILabel *l = nil;

    if (disable) {
        if (l != nil)
            return;
        l = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, 160, 44)];
        l.backgroundColor = [UIColor clearColor];
        l.userInteractionEnabled = YES;
        [self.window addSubview:l];
    }
    else {
        if (l == nil)
            return;
        [l removeFromSuperview];
        [l release];
        l = nil;
    }
}

You can call it like this from any view controller to disable:

MyAppDelegate *appDeleg = (MyAppDelegate *) [[UIApplication sharedApplication] delegate];
[appDeleg disableLeftBarButtonItemOnNavbar:YES];

To enable:

MyAppDelegate *appDeleg = (MyAppDelegate *) [[UIApplication sharedApplication] delegate];
[appDeleg disableLeftBarButtonItemOnNavbar:NO];
ardalahmet
A: 

Shouldn't the above have release the UILabel *l after the [self.window addSubView:l] call? That way it gets retained +1 when added to the Subview, but released -1 in the same branch. Otherwise, you must call disableLeftBarButtonItemOnNavbar:NO to release it. And while, you'll end up in the same place in the end, you aren't leaking, I think the static analysis tools they've built into XCode wouldn't like that being in a separate branch. Small detail :-)

  • (void) disableLeftBarButtonItemOnNavbar:(BOOL)disable { static UILabel *l = nil;

    if (disable) { if (l != nil) return; l = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, 160, 44)]; l.backgroundColor = [UIColor clearColor]; l.userInteractionEnabled = YES; [self.window addSubview:l]; [l release]; } else { if (l == nil) return; [l removeFromSuperview]; l = nil; } }

Hayden