Strange behaviour of simulator & device when adding rightBarButtonItems to a UINavigationItem - iphone

In my application I am added two button at right of a UINavigationItem, its working fine on simulator, but when I testing it on device its gives me error of SIGABRT, along with unrecognized selector sent to NSArray. I tried to add one button at right side, it was added successfully, and works fine on device as well. Here my question is, whats the problem?
I am adding right buttons using following code,
NSArray *buttons=[[NSArray alloc] initWithObjects:btnOne,btnTwo,nil]];
myNavItem.rightBarButtonItems=buttons; //Error on device, but works fine on simulator.
Please, point me what is I doing wrong?
Thanks!

It appears that myNavItem is not an instance of UINavigationItem, but rather an instance of NSArray (which does not support setRightBarButtonItems). Could you show us more lines concerning myNavItem?
My suspicion is that myNavItem did not correctly retain the navigation item that it was originally pointing to. And that it points to an NSArray now by coincidence. This error might not occur in a debug setting if all objects are retained indefinitely for better logging.
If this code runs from an instance of a view controller try to use this line instead:
self.navigationItem.rightBarButtonItems = buttons;
On iOS prior to version 5: if you receive unrecognized selector sent to NSArray logs there is something wrong with your memory management. The log should read unrecognized selector sent to UINavigationItem on iOS prior to iOS 5.
Once the memory issue is fixed you should use a UIBarButtonItem with a custom view containing two UIButtons.

try adding these buttons to a UIBarButtonItem and add UIBarButtonItem to myNavItem like myNavItem.rightBarButtonItem = barButtonItem;

Your first line
NSArray *buttons=[[NSArray alloc] initWithObjects:btnOne,btnTwo,nil]];
has an extra right bracket at the end. Not sure if this would cause that error but it should cause some error.

You can use the UISegmentedControl. Check the UICatalog code sample to check its usage in the navigation bar.
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:btn1,btn2,nil]];
[segmentedControl addTarget:self action:#selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
segmentedControl.frame = CGRectMake(0, 0, 90, 35);
segmentedControl.segmentedControlStyle=UISegmentedControlStyleBar;
segmentedControl.momentary = YES;
UIBarButtonItem *segmentBarItem = [[UIBarButtonItem alloc] initWithCustomView:segmentedControl];
[segmentedControl release];
self.navigationItem.rightBarButtonItem = segmentBarItem;
[segmentBarItem release];
}
This is the best way of adding as many number of buttons in your bar as you desire.Hope it gonna help u.
Thanks :)

Related

UIView frame sent to deallocated instance

I am currently working on an application that has some ViewController with a button on it that pushes to a UITableViewController with Search Bar and Search Display controller. I have some data in the cells and that gets populated. I have added the following code to hide the search bar and also make it clearcolor when you do see it:
[[self.searchBar.subviews objectAtIndex:0] removeFromSuperView];
[self.searchBar setBackgroundColor:[UIColor clearColor]];
CGRect newBounds = [[self tableView]bounds];
newBounds.origin.y = newBounds.origin.y + searchBar.bounds.size.height;
[[self tableView] setBounds:newBounds];
Now this works when I am using the iOS simulator, but when I run it on my device and begin to scroll, either up or down, it crashes and gives me the following error:
EXC_BREAKPOINT (code = EXC_ARM_BREAKPOINT, subcode = 0xdefe)
I then enabled Zombie Objects to further debug and got this:
-[UIView frame]: message sent to deallocated instance 0x156b1430
When I take off:
[[self.searchBar.subviews objectAtIndex:0] removeFromSuperView];
[self.searchBar setBackgroundColor:[UIColor clearColor]];
and run my application on my device again, it does not crash and works fine.
Anyone have any ideas whats going on and how this is happening? Thanks in advance!
[[self.searchBar.subviews objectAtIndex:0] removeFromSuperView];
//This Line remove search bar from view ,which means it is deallocated at that moment so when you again try to remove it ,,the app crash ..
// So,instead of this try to hide searchbar ,,
Self. searchBar.hidden =YES;
I think UISearchBar has unsafeunretained reference of [self.searchBar.subviews objectAtIndex:0];
And You set the Bounds of UITableView then change frame of UITableView.
It would fire auto resizing and layoutSubviews.
So, UISearchBar's layoutSubviews method access [self.searchBar.subviews objectAtIndex:0].frame
for layout or auto resizing.
It's not recommended that change view in UIControls from SDK.
There is an idea that Setting hidden=YES instead removeFromSuperview.
I was having this same problem. Got to this page, read the words 'zombie objects' in the question and remembered that I'd left zombie objects enabled. After disabling them, the problem went away. Now when I scroll my tableView, there's no crash.
This may be weird, but perhaps it will help someone...

UIBarButtonItem action selector working for iPad but not for iPhone

Following code adds a UIBarButtonItem to my navigation controller. When I click the bar button on the iPad Simulator the selector buyApp does get called however the same selector buyApp does not get called on the iPhone simulator.
UIBarButtonItem *buyButton = [[UIBarButtonItem alloc] init];
buyButton.title = NSLocalizedString(#"BUY", nil);
[buyButton setTarget:self];
[buyButton setAction:#selector(buyApp:)];
self.navigationItem.rightBarButtonItem = buyButton;
UPDATE: I am using xib for UI and am pushing it's viewcontroller on the the navigation controller.
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"BUY" style:UIBarButtonItemStyleBordered target:self action:#selector(buyApp)] autorelease];
Better to try this for decrease the code lines
Ok, I found what the problem was. There is nothing wrong with the code.
The problem was with testing. The way I was trying to verify the functionality was by printing a string using NSLog. However while testing using the simulator, I switched between the simulators using Hardware -> Device -> iPhone and I could not see the NSLog output.
Once I explicitly stopped the iPad simulator and restarted the app in the iPhone simulator I could get the NSLog output. Weird simulator behavior.

iPhone trash can suck animation

I am trying to use the trash can animation in an iPhone application I am building. I know the feature I need help with is a private API but the app will be in-house.
According to the iPhoneDevWiki at the toolbar page you can activate the trash can opening animation using [UIToolbar animateToolbarItemIndex:duration:target:didFinishSelector:];.
After countless hours trying to use this method I could not get it to work. I have changed it so far to the following: [toolbar animateToolbarItemIndex:1 duration:1.0 target:self didFinishSelector:#selector(done:)];.
toolbar is the name of the UIToolbar I created programically using CGRectMake.
My button image for the trash can is 1, since it is the second button.
I have tried putting self and nil in target but it doesn't work.
didFinishSelector just links to -(void)done:(id)sender;.
If I change the animateToolbarItemIndex to something that does not exist, the console says that it does not exist. Any ideas to what I have wrong?
The trash can animation works with an array of images, each with the lid closing/opening a little more. So you'd do something like this:
UIImageView* trashCan = [[UIImageView alloc] initWithFrame:self.view.frame];
trashCan.animationImages = [NSArray arrayWithObjects:UIIMAGES, nil];
trashCan.animationDuration = 1.00;
trashCan.animationRepeatCount = 1;
[trashCan startAnimating];
[self.view addSubview:trashCan];
If you have a google I'm sure you'll be able to find the trash can images to use.

Using UISegmentedControl as button

In my code I am using a UISegmentedControl as a "button" with only ONE segment and the momentary property set to YES. In versions of the SDK prior to iOS 4, this was not a problem, but it appears that now iOS 4 requires that there be at least 2 segments. The following code throws an exception:
NSArray *titles = [NSArray arrayWithObject:#"Button Title"];
myButton = [[UISegmentedControl alloc] initWithItems:titles];
and now in Interface Builder you cannot even create a UISegmentedControl with less than 2 segments. It logs the following error when building:
"The number of segments property of a segmented control must be greater than or equal to 2."
I'm kinda stumped. Any work arounds for this? I tried to create a UISegmentedControl with two buttons and then remove one programmatically and that "works" as it doesn't cause the app to crash. I get a button in iOS 3 and nothing in iOS 4. Any ideas?
Have you tried this:
[myButton removeAllSegments];
[myButton insertSegmentWithTitle:#"Press this" atIndex:0 animated:NO];
Really strange. It still works fine for me both in iOS4 simulator and device (this is a real working snippet from my code):
NSArray *laterContent = [NSArray arrayWithObjects: #"Maybe later", nil];
UISegmentedControl *later = [[UISegmentedControl alloc] initWithItems:laterContent];
CGRect frame = CGRectMake( 20,
98,
self.alert.bounds.size.width/2 - 30,
30);
later.frame = frame;
later.selectedSegmentIndex = -1;
[later addTarget:self action:#selector(laterAction:) forControlEvents:UIControlEventValueChanged];
later.segmentedControlStyle = UISegmentedControlStyleBar;
later.tintColor = [UIColor colorWithRed:130.0f/255.0f green:74.0f/255.0f blue:54.0f/255.0f alpha:0.8f];
later.momentary = YES;
later.alpha = 0.9;
It's not exactly a code-related solution but: I hit a similar issue and ended up drawing my own similar looking resources in Photoshop. It was not terribly difficult to do and removed a particular bad "code smell", IMO.
I found if you have a previous project with a single-segment UISegmentedControl, you can open both that project and your new one in Interface Builder and drag (or copy/paste) the single-segment UISegmentedControl to your new view controller. It will work fine in both your app and Interface Builder, just don't change the number of segments from 1 to anything else as it won't let you go back. I'm using Xcode 4.6.2 and iOS 6.
The editor in Interface Builder won't let you change the number of segments to be less than 1, but you can make a segmented control in IB by editing the .xib xml manually.
Right click on the .xib containing the segmented control
Choose Open As -> Source Code from the popup menu.
Find "<segments> which is the beginning of the xml array of segments. The whole thing should look like:
<segments>
<segment title="Segment 1 Title"/>
<segment title="Segment 2 Title"/>
</segments>
Just delete <segment title="Segment 2 Title"/> so there is only one segment element.
Right click the .xib again and choose Open As -> Interface Builder - iOS to go back to interface builder.
You should also probably set the segmented control to "momentary" mode.
I don't get any errors compiling or running this. Of course, this is a hack, and may break things in some circumstances or in a future iOS release.
Well two possibilities:
1) Create a button and the set background image as the single dot of the UISegmentedControl
If your SegmentedControl is a class variable just replace the #property
#property (nonatomic, retain) IBOutlet UIButton *button;
In the viewDidLoad-function add the following
-(void) viewDidLoad
{
[super viewDidLoad];
...
self.button = [UIButton alloc] init];
[self.button setBackgroundImage:[UIImage imageNamed:#"segmentedDot.png"] forState:(UIControlState)UIControlStateNormal];
}
2) Set the amount of segments to three of your UISegmentedControl and afterwards set the width to 20 - now only the dot in the middle will be shown.
Dont forget, if the user interacts with the UISegmentedControl, set the currentElement again to the second segment, else the dot will be in light grey instead of white state.
3) Place a button or a small view over the unwanted second dot of the UISegmentedControl in InterfaceBuilder. Make sure the backgroundcolor is even.
When you are using a button set the state for "user interaction" in attribute inspector to disabled. As type I would chose "custom" since you won't have some borders in your button ;)
Now male again sure, that always the first dot is the active Element.
However I think solution one should be the way you should go, since Apple thought something about it, when they disabled the 1-dot-SegmentedControl. Since you are using the Control as a button the Element you are looking fpr should be a button. ;)
There's no workaround in iOS 4. If you need this functionality, file a bug (enhancement request) at bugreport.apple.com.
You can also use removeSegmentAtIndex:animated:. If you create a segmented control in a storyboard or xib with two segments, you can remove one like this:
[self.sortButton removeSegmentAtIndex:1 animated:NO];

Objective C memory leak issue

The Leaks instrument tells me that I have a leak in this code fragment. Why is this so?
This code fragment is in viewDidLoad().
UINavigationItem *navItem=[self navigationItem];
UIBarButtonItem *addFeed = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addFeed)];
[navItem setRightBarButtonItem:addFeed]; // leaks says that 128 bytes leaked
[addFeed release];
UIBarButtonItem *reload = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(reload)];
[navItem setLeftBarButtonItem:reload]; // leaks says that 128 bytes leaked here too !
[reload release];
[navItem release];
You should not be releasing navItem. You did not alloc/retain/new/create it, so you do not release it.
Other than that, your code looks fine. Is that everything in the method?
The leaks instrument only tells you where the leaked memory was allocated; it can't tell you where the memory should have been released but wasn't since there's no possible way for it to know that. Your leak is occurring elsewhere.
This code is mostly fine, except you should not be releasing navItem at the end. You are not an owner of it, since you didn't create it with a method named alloc, new, or copy in its name, so you should not release it.
if you're still getting the leak message and can't track down the bug, you can try using the static analyzer included in the latest and greatest Xcode (version 3.2)
Build > Build and Analyze
it'll use LLVM-Clang to statically analyze your code in a pretty way.
http://developer.apple.com/mac/library/featuredarticles/StaticAnalysis/index.html
UPDATE:
in your code snippet:
UINavigationItem *navItem=[self navigationItem];
UIBarButtonItem *addFeed = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addFeed)];
[navItem setRightBarButtonItem:addFeed]; // leaks says that 128 bytes leaked
[addFeed release];
your leak is probably coming from setting the new rightBarButtonItem without releasing the old one.
this is what i think is happening:
1) get a handle to the navigationItem (has right bar button A)
2) create a new UIBarButton Item (making right bar button B)
3) setRightBarButtonItem to button B
now where's Button A? it should have been released by navItem when you set the new button. so you could have forgotten to release the button when you set it the first time, or you've got it retained somewhere else.
Do you have NSZombieEnabled? This causes objects not to be retained by the NSZombie instances and you'll see "leaks" when running the Leaks tool.
It seems that you're not releasing the view controller with custom viewDidLoad method.
[navItem setRightBarButtonItem:addFeed];
[navItem setLeftBarButtonItem:reload];
You are creating copies of the objects in these accessors. These accessors are incrementing the retainCount by 1. Your accessors should release each object and then immediately retain them.
Example:
- (void) setTitle: (NSString*) newTitle {
if (title != newTitle) {
[title release];
title = [newTitle retain]; // Or copy, depending on your needs.
}
Take a look at the techniques here:
Memory Management Programming
I believe that's what's up. So take a hard look at those two accessors.