I have a very simple application that use a 2 component UIPickerView that causes me a crash every time I click over it. I dragged it into my view by IB, then hooked up dataSource and delegate to File's Owner. In the .h file:
#interface SettingsViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate> {
While in .m
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {
return 2;
}
- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component {
NSInteger value;
if (component == 0) {
value = [tipiDado count];
} else {
value = [numeroDadi count];
}
return value;
}
- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
if (component == 0) {
return [tipiDado objectAtIndex:row];
} else {
return [numeroDadi objectAtIndex:row];
}
}
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
NSLog(#"Selected Dice: %#. Number of Dice: %#", [tipiDado objectAtIndex:row], [numeroDadi objectAtIndex:row]);
}
I dunno why it continues to give me SIGBART or EXC_BAD_ACCESS... I don't know where I'm doing wrong.
Suggestions?
Thanks folks.
It's difficult to answer properly without seeing some more code. When it crashes, you should be able to see exactly what line is causing the crash (look at the call stack on the left). My guess is that either one of your arrays (tipiDado or numeroDadi) are not retained properly or else that the objects stored in them are not of type NSString.
If you update the question with the code you use to initialize them, it will be easier to point out the exact problem.
It's probably because your arrays (tipiDado and numeroDadi) are no longer valid. Maybe they are set up as autoreleased objects?
What you can do, is start in debug mode (debug configuration and with debugger attached) and it should stop right at the line where it crashes.
Try using Allocations with zombo detections / reference counter to see which object is the problem.
In didSelectRow, you are using the same row value to access both arrays. If the arrays are not the same size, you could be accessing an out-of-range item.
You should either check one array only based on the component parameter or to show both selections you can do this instead:
NSInteger tipiDadoRow = [thePickerView selectedRowInComponent:0];
NSInteger numeroDadiRow = [thePickerView selectedRowInComponent:1];
NSLog(#"Selected Dice: %#. Number of Dice: %#",
[tipiDado objectAtIndex:tipiDadoRow], [numeroDadi objectAtIndex:numeroDadiRow]);
I used property/synthesize to initialize them, so when I filled them up with [NSArray arrayWithObjects:...] I haven't added retain but I forget to use self. notation!!
Writing down:
self.tipiDado = [NSArray arrayWithObjects:#"D4",...];
fixed up he problem.
Related
I have a UIPicker with a UILabel above it that updates displaying the current selection.
When I go to this view the 2nd time, the UIPicker's selection is on the last cell where I left off, but the label is on the default string. How can I make it so the label is also on where I last left off?
This is the code I am using:
- (void)displayPicker
{
self.numberLabel.text = #"1 number";
}
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
self.numberButtonText = [NSString stringWithFormat:#"%# number", [self.pickerArray objectAtIndex:row]];
self.numberLabel.text = self.numberButtonText;
}
If i assume correct, you are calling
-(void)displayPicker
to set the default numberLabel text value (as in self.numberLabel.text = #"1 number";).
Possible cause would be,
that - (void)displayPicker is called every time, the view is presented to the user, but
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
is not.
You might want to check for such behavior, by inserting some NSLog() statements and perhaps correct it by using something like this:
- (void)displayPicker
{
self.numberLabel.text = [NSString stringWithFormat: #"%i number",
[self.numberPicker selectedRowInComponent: 0 ]];
}
i am working on a project in which i have to perform following two work:
1.) Fetch value from CoreData and store it in an NSMutableArray.
2.) Take a UIPickerView and fill it with an Array value.
Problem is that size of array is dynamic and i canto fill an array value in UIPickerView. can someone help me.
in order for the UIPickerView to work correctly, you must supply the amount of components (columns) and the number of rows for each component whenever it reloads:
as mentioned, use [myPickerView reloadAllComponents]; to reload the view once the array is populated, but you MUST implement also these things after you declare the containing view controller class as <UIPickerViewDelegate> link the picker to the file owner as a delegate, and then:
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;// or the number of vertical "columns" the picker will show...
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
if (myLoadedArray!=nil) {
return [myLoadedArray count];//this will tell the picker how many rows it has - in this case, the size of your loaded array...
}
return 0;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
//you can also write code here to descide what data to return depending on the component ("column")
if (myLoadedArray!=nil) {
return [myLoadedArray objectAtIndex:row];//assuming the array contains strings..
}
return #"";//or nil, depending how protective you are
}
After updating the value (Inserting or modifying existing) in your array.
Call reloadAllComponents on your UIPickerView instance.
- (void)reloadAllComponents
Use as below
[myPickerView reloadAllComponents];
Hai,In my app i want to place the 4 pickerviews.And i place the one segment control with 3 segments.If we click on first segment then firstpicker will be display.Likewise remaining pickers are display for remaining segments.But,only first picker will be displayed and remaining pickers are not show components and rows.Only display black screen.Please provide any help.Please urgent.
To again call the delegates, call the following line
[pickerview reloadComponent];
create outlet and make use of hidden property...means if you tap on first segment make first picker as highlighted and similarly for other segments with respective pickers...
do you want to resize ??
Edit: added code from below for formatting purposes
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return ( pickerView == picker1 ? 2 : 3 );
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { NSArray *values = ( pickerView == picker1 ? values1 : values2 );
return [values count];
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
NSArray *values = ( pickerView == picker1 ? values1 : values2 );
return [values objectAtIndex: row];
}
check like this..
I'm developing an application with a picker view that slide from the bottom (like an action sheet). I need to convert 3 units (litres, us gallons and imperial gallons). The value is taken from a text field but I don't know how to tell the compiler which was the unit to start from.
(ie...the user insert the value in the the text field but if he changes the units (usg or ig), the value in the text field changes accordingly.
this is the array:
NSMutableArray *weightArray = [[NSMutableArray alloc] initWithObjects:#"", #"Ltr",#"Usg",#"Ig",
nil];
self.weightPickerViewArray = weightArray;
[weightArray release];`
this is the picker:
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
if (row == 1) {
labelVolume.text = [weightPickerViewArray objectAtIndex:row];
//more code here....
}else if (row == 2){
labelVolume.text = [weightPickerViewArray objectAtIndex:row];
//more code here....
}else if (row == 3){
labelVolume.text = [weightPickerViewArray objectAtIndex:row];
//more code here....
}
how can I tell the compiler which was the value before the selection?
Add a text indicating the units at the end of the string.
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
if (row == 1) {
NSString *initalValue = labelVolume.text;
// Do your calculations here based on unit
NSString *updatedValue = urNewValue;
labelVolume.text = updatedValue;
//more code here....
}else if (row == 2){
NSString *initalValue = labelVolume.text;
// Do your calculations here based on unit
NSString *updatedValue = urNewValue;
labelVolume.text = updatedValue;
//more code here....
}else if (row == 3){
NSString *initalValue = labelVolume.text;
// Do your calculations here based on unit
NSString *updatedValue = urNewValue;
labelVolume.text = updatedValue;
//more code here....
}
When you select a row in picker, it call's its delegate method pickerView:didSelectRow:inComponent:
Here, you can can change the value of your text field depending on which row (liters, gallons, etc) user selected.
Please refer the apple's class reference for UIPickerView delegate
If I understand your problem. You want catch event when user change units in your pickerView, pass the information to the view that contains text field and execute conversion.
So you can use NSNotificationCenter (documentation here). In your pickerView when value change put this code :
[[NSNotificationCenter defaultCenter] postNotificationName:#"unitValueChange" object:#"litres"];
In the view that contains UITextField put this code :
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myMethodToExecute:) name:#"unitValueChange" object:self];
Now when your PickerView launch notification your view launch this method :
-(void)myMethodToExecute:(NSNotification *)notif{
NSString *myUnitSelected = notif.object
}
If I have a UIPickerView with three components (dials). How does the picker handle two dials spinning simultaneously? For example, the user might flick the first dial, which spins freely and immediately slowly click to a selection on the second dial.
I'm doing the following in the picker. If one dial is spinning, I don't capture its value. I only capture values when the dials spin one at a time.
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
if(component == 0){
dateEndCenturyText = (NSString *)[dateCentury objectAtIndex:row];
}
else if(component == 1){
dateEndDecadeText = (NSString *)[dateYear objectAtIndex:row];
}
else if(component == 2){
dateEndYearText = (NSString *)[dateYear objectAtIndex:row];
}
Is there a better way to ensure capture of values even if two dials are spinning?
The most robust solution to this may be to update all three values each time your event fires. You can retrieve the selected row for any component in the UIPickerView using the following method:
UIPickerView selectedRowInComponent:
Returns the index of the selected row in a given component
- (NSInteger)selectedRowInComponent:(NSInteger)component
Hope this helps!