I have UITableView and there is a picker view inside every cell. User can change value of every picker separately.
I need to know that which cell's pickerView is changed. For example, when user move a picker I will know that, this is cell 3.
Is there any way to do that? It's not necessary to get instant changed values. We can store it on array than send when Go button clicked that is not related with tableview.
I would suggest to store an array of pointers to your picker views. So index 0 of your array would point to the picker view of table row 0, etcetera. Then, make sure your view controller is the delegate of all picker views. Implement the pickerView:didSelectRow:inComponent: method declared by the UIPickerViewDelegate protocol. That method has an instance of UIPickerView as parameter: that's the one which value was changed. To obtain the row of that picker view, you can send your array the indexOfObject: method.
Create a subclass of a UITableViewCell. That subclass should get a reference to whatever you want to update, and then it can simply create (or IBOutlet link) to the picker and be a delegate of the picker.
So Cell should have:
- (void) passStuff:(NSMutableDictionary *)datamodel key:(NSString *)key values:(NSArray *)values {
_privateWeakRefDataModel = datamodel;
_privateCopyKey = key;
_privateValues = values;
[picker reloadData];
}
#pragma mark - Picker Delegate Code here
This way the cells will handle updating your data model directly. Keeps your code much cleaner.
I have set a picker view appearance on text field begin editing function and hide keyboard. Now problem is that when picker view appear then it show selection indicator on first value but that value not show in text field. I want that when user click on text filed then picker view appear and value from picker will show in text field where selection indicator place. How do that? Suppose i have four value in same order 0,1,2,3. Now when picker view appear then selection indicator is at on '0' value. But to get that value we have to scroll picker view and come back to '0' then it show that value in text field. Why it is occurs? And how get value automatically?
Thanks in advances...
Well, there is no connection between your picker view and the text field - you have to make it yourself.
There are two things to consider:
1) Your text field already contains a value, let's say "1". When you open the picker, you can set it to this value already with the function
[yourPicker selectRow:1 inComponent:0 animated:false]
, assuming your data array for the picker is [0, 1, 2, 3]. In your case, to make the text field contain the value 0, just call
[textField setText:#"0"]
2) When you are finished with the picker, it calls the delegate function
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
So you have to make your view implement the UIPickerViewDelegate protocol, implement the above delegate function in it and set the view as the picker view's delegate.
In the delegate function you can get the value the user picked (using row to access your data array values) and set this value in your textfield.
I have a UIPickerView with custom cells. For every moment, I'd like to know which cell is in the center location (behind the tinted selection indicator). The UIPickerView delegate method
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
does provide this information, but only after the picker view has stopped. What I'm looking for is a way to retrieve the currently "selected" cell while the picker view is still spinning and has not yet come to a halt.
As a work around, I've tried to go through KVO and register for each cell's view changes:
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
.
.
[cell addObserver:self forKeyPath:#"frame" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}
I do receive callbacks to observeValueForKeyPath:ofObject:change:context:, however no matter whether I scroll the picker view up or down, the received values seem to be always the same:
2011-03-09 08:00:34.429 Project[35069:207] Frame: x=2.00, y=-13.00
2011-03-09 08:00:34.430 Project[35069:207] Location: x=4.00, y=-26.00
2011-03-09 08:00:34.430 Project[35069:207] Change: {
kind = 1;
new = "NSRect: {{2, -13}, {118, 70}}";
old = "NSRect: {{2, -13}, {118, 70}}";
}
2011-03-09 08:00:34.431 Project[35069:207] Frame: x=2.00, y=-13.00
2011-03-09 08:00:34.431 Project[35069:207] Location: x=4.00, y=-26.00
2011-03-09 08:00:34.432 Project[35069:207] Change: {
kind = 1;
new = "NSRect: {{2, -13}, {118, 70}}";
old = "NSRect: {{2, -13}, {118, 70}}";
(Location coordinates are in window frame)
Any other ideas what I could try to get that value?
I think the method:
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view`
with:
[pickerView selectedRowInComponent:0]);
Is exactly what you want.
I think this is interesting. I wanted to do the similar thing with UIPicker, but haven't looked into much yet.
UIDatePicker actually does this, you can drag time around noon and it will toggle AM/PM even though the finger has not lifted from the screen (it should not have called "didSelectRow" yet).
UIDatePicker actually subclasses from UIControl, instead of UIView like UIPickerView. So, UIDatePicker is probably observing all UIControlEvents to do this.
Observing -pickerView:titleForRow:forComponent probably works for now, but if behavior changes later (ie caching behavior, ie) is changed, this will break...
One suggestion is to try KVO on center property, instead of frame.
Edit1: By the way, how does your observeValueForKeyPath method look like?
Edit2: Upon further research, it seems most of UIKit is not KVO compliant.
See this answer -
Key value Observing during UIView Animations
So, it seems like only option is to keep poling for the view's value when it's visible. How about forloop and pole frame/center property whenever it's in view?
You can use your picker delegate's -pickerView:titleForRow:forComponent: method to give you a pretty good idea of where the picker's wheel is. As you scroll in one direction or the other, the picker will ask its delegate for the title (or view) for a row. The number of rows between the "center" of the wheel and the row the picker is asking for depends on the row height, but you'll likely know what that is.
You'll have to experiment a bit to get it right. The picker seems to ask for one or two rows that aren't yet visible, perhaps to keep the scrolling smooth. In my test with a picker that displays 5 rows, there seems to be about a 3-row difference between the row the picker was asking for and the center. You'll obviously also have to keep track of the direction of rotation (up or down), and you won't find out if the first or last two or three rows are selected until your -pickerView:didSelectRow:inComponent: is called.
Just a quick thought, but you could add a UIScrollView (transparent with a contentView that is as tall as the number of components.) over the UIPickerView.
Then listen to the UIScrollViewDelegate methods to get the live scrolling data you need.
The tricky part would be passing along the touch events from the scroller to the picker so that it would act as expected.
The problem you have with UIPickerView is that the view you are setting for the cells are actually subViews.
UIPickerView is built from various imageViews, a TableView and a selection line.
The view you provide when using viewForRow are inserted as subViews of the tableView's cells and so their frame is never changed.
The only frame that changes is the frame of the actual tableView cell, to which of course you have no access.
A possible solution is to simply replace the UIPickerView's tableView with your own.
You can do this by setting the number of rows for the UIPickerView to be 0, and place a transparent tableView over the UIPickerView. Your tableView will hold the actual cells, and since a UITableView inherits from UIScrollView, you can get events about the scroll and simply check which UITableViewCell is currently under the selection line.
The frame does not change because the cell is not moving. One of its superviews is, and the cell is carried along with it. To figure out the cell's coordinate in the parent UIPickerView, try this:
CGRect theApparentRect = [theCell convertRect: theCell.bounds toView: thePicker];
The resultant rectangle will be in the coordinate system of thePicker, and you can then figure out if it's in the middle with the following simple test:
CGPoint middleOfThePicker = CGPointMake
(
CGRectGetMidX(thePicker.bounds),
CGRectGetMidY(thePicker.bounds)
);
Boolean isInMiddle = CGRectContainsPoint(theApparentRect, middleOfThePicker);
How can i disable UIPickerView column?
I want to disable only one column of picker. Lets say i have picker with 3 column and i want to disable second column.
There are a couple options here.
1) Capture interaction with the second column and override it.
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component;
This method can be implemented to simply go back to where it was before the selection. However, they will still be able to interact with the second column.
2) Simply remove the column from the picker.
I believe this is the best option. Have some boolean 'showSecondColumn'. Then, in:
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
you can simply check your little flag and return the appropriate number. UIPickerView has a method: reloadAllComponents
Good luck!
It seems there is no way to disable column. I solved(not best solution) like this:
get column as "image" (snapshot)
grayscale "image"
set "image view" user interaction enabled
add "image view" to picker as subview
If there is any better way it would be great.
Is there a way to have a UIPickerView just have escalating numbers to infinity?
Haha. Just kidding, but seriously, I want just a bunch of numbers going up, but I don't want to have to hard-code it all.
Thanks in advance!
I'm pretty sure this can be done with some cleverness (a custom integer class that supports arbitrarily large numbers, and periodically "rolling over" to a higher set of numbers once the user has scrolled through all NSIntegerMax rows (i.e. when the user scrolls to row NSIntegerMax you programmatically scroll them back to row 0 and display "row + NSIntegerMax" instead of just "row"). As they scroll through repeatedly you change it to "row + NSIntegerMax * 2", etc.
Obviously you also have to handle them changing directions are some point.
But I suspect that's what fancier than what you're looking to do. In this case you just want to add these lines of code to your View Controller:
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return NSIntegerMax;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [NSString stringWithFormat:#"%d", row];
}
Then double click on the View nib file (the *.xib file) to open it in Interface Builder, and connect the "data source" outlet to the "File Owner" (the View Controller). That will get you as many integers as I could ever imagine someone scrolling to.
Have a look at this question. It is a variation on your problem.
You could just use a UIStepper. I know you asked about UIPicker but do you really need to use that class or were you using it to explain what kind of thing you wanted