There are a lot of questions like this, but I believe my case is unique.
I have a pickerview that pops up when a textbox is clicked inside. The user selects their location in the pickerview, and it's put into the textbox.
When opening the pickerview, the slider is on the first element, but if i click the done button to minimize the pickerview, that no element is selected (i.e. you must scroll down and back up to select the first element)
I've tried
[pickerView selectRow:0 inComponent:0 animated:YES];
and various combinations of it, but it only puts the slider on that element, again not actually selecting it.
Some answers said to apply the above code in the viewDidLoad() method, but unfortunately I'm pulling the location array in from a web service and cannot do that.
Ideally, I'd like to work as - the user clicks in the textbox, the pickerview pops up as normal, if at that point the done button is clicked, the first item is selected.
Thankyou in advance
Swift 3+
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == yourTextField{
self.yourPickerView.selectRow(0, inComponent: 0, animated: true)
self.pickerView(yourPickerView, didSelectRow: 0, inComponent: 0)
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
//Do all the stuff to populate the TextField
}
Here is something you can try :
Set a var in your #interface, something like NSString *selectedString;.
When you init your UIPickerView (in pickerView:titleForRow:forComponent:), when you set the title for the row 0, set selectedString with the same string as the title of the row #0. Then, in pickerView:didSelectRow:inComponent:, set selectedString with the appropiate string :
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
// Some stuff
// Assuming "yourDataArray" contains your strings
selectedString = [yourDataArray objectAtIndex:row];
}
Then, when you call the method triggered by the push of the "done" button, use selectedString to set the text.
Hope this works out for you !
I had a same problem. My solution was this:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
// Only for the text field with the picker
if (textField == self.yourTextField)
{
// Select the first row programmatically
[self.categoriesPicker selectRow:0 inComponent:0 animated:YES];
// The delegate method isn't called if the row is selected programmatically
[self pickerView:self.categoriesPicker didSelectRow:0 inComponent:0];
}
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
//Do all the stuff to populate the TextField
}
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
// Only for the text field with the picker
if (textField == self.yourTextField && [textField length]!=0)
{
// Select the first row programmatically
[self.categoriesPicker selectRow:0 inComponent:0 animated:YES];
// The delegate method isn't called if the row is selected programmatically
[self pickerView:self.categoriesPicker didSelectRow:0 inComponent:0];
}
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
//Do all the stuff to populate the TextField
}
/Create a IBACTION for the textfield with event editing did begin and the function should check if field is empty like this/
-(IBAction)asignFirst:(UITextField *)sender{
if ([sender.text isEqualToString:#""])
{
[yourPicker selectRow:0 inComponent:0 animated:YES];
sender.text = self.pickerData[0];
}
}
Related
I have a customized UIPickerview and I do not want to use a datepicker. I want to implement the feature where when a user scrolls down/up the hours, the AM/PM component switches while the hour is scrolling. This means that I need to switch it before pickerView didSelectRow is called. Is there a way to do this?
Thanks
Use following method,
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
// put your logic here.
}
Above method is from UIPickerViewDelegate, If user selects any element using pickerview, this method is automatically triggered.
Hope it helps to you.
Edit :
I think you should use following method for detecting - in which direction user is scrolling ?
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
NSLog(#"scrolling to row is %#",[NSString stringWithFormat:#"index_%i",row]);
return [NSString stringWithFormat:#"index_%i",row];
}
whenever user is scrolling up/down above method is triggered automatically which is similar to UITableView. Remember UIPickerView is using UIPickerTableView which is private, so we can not detect the scrolling the way you want to have.
Let me explain the detecting the direction in pickerview.
Example. Visible rows are index_4,index_5,index_6,index_7. Now if user is scrolling down index_8 will be called. Similarly if user is scrolling to up index_3 will be called.
I hope this trick will solve your problem. Even-though let me know your feedback on this.
There is a trick to detect this but there is no delegate method/ property to detect if its scrolling or not
take a property as isScrolling
set isScrolling to true in func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? or equivalent method
set isScrolling to false in func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
hope this helps, BTW this is what is explained in all the above mentioned answers
have to use this two UIPickerView delegate method for event:
DID END SCROLL:
- (void)pickerView:(UIPickerView *)pV didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSLog(#"pickerView didSelectRow %zd",row);
// DID END SCROLL: CALL YOUR HEANDLER METHOD!!
}
WILL START SCROLLING:
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view{
// WILL START SCROLLING: CALL YOUR HEANDLER METHOD!!
UILabel* label = (UILabel*)view;
if (!label){
label = [UILabel new];
[label setFrame:frame];
[label setBackgroundColor:[UIColor clearColor]];
[label setFont:[UIFont fontWithName:FONT_QUICKSAND_BOLD size:12]];
[label setTextAlignment:NSTextAlignmentRight];
[label setLineBreakMode:NSLineBreakByTruncatingMiddle];
[label setTextColor:UIColorFromRGB(0X2692ff)];
}
//Update labeltext here
[label setText:[NSString stringWithFormat:#"row %zd",row]];
return label;
}
obviosly except for the building of UIPickerview (firstTIME!); so u have to implement a fake didendscroll event when you finish to build you piker or ignore first willstart scroll for the visible piker row
Unfortunately, the above solutions (setting a flag when data source methods such as titleForRow are called and resetting it when didSelectRow is called) are sketchy and won't work in many cases. The data source methods may be called in many cases where there is no scrolling by the user - such as UI changes resulting in changing view layouts, and also may be called immediately after didSelectRow - you have no control over when these methods are called.
In relation to this particular question, these solutions may work, since these methods are always called when scrolling - just not ONLY when scrolling. However, care needs to be taken not to assume that the user MUST be scrolling in these cases. Also, if you manage a state machine with this flag (like disabling some button when scrolling and enabling it after), you'll get in trouble.
Finally, this only allows detecting a scroll (with the above caveats), but it won't tell you the speed or current values - so it would be difficult to tell when to switch the AM/PM label.
The only way I was able to reliably detect a scroll was through a mess of flags that I set when UI changes are made and other ugly hacks (like waiting 100 ms after a didSelectRow before trying to detect scrolls again, because I noticed calls to the data source immediately after didSelectRow). Adding willScroll/didScroll methods to the delegate seems like an obvious thing Apple neglected to add.
I just found a way to achieve "pickerDidScroll",it work fine. The key point is add KVO to the row view. Here is my code:
//picker view delegate
- (UIView*)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
if (!view) {
view = [[RatchetScrollUnit alloc] initWithFrame:CGRectZero];
//
[((RatchetScrollUnit*)view) addRatchetObserver:self];
}
return view;
}
//KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (self.ratchet) {
NSInteger row = [self.ratchet selectedRowInComponent:0];
if (row != _lastRow) {
_lastRow = row;
if (self.delegate && [self.delegate respondsToSelector:#selector(ratchetScrollerDidRatchetedToTooth:)]) {
[self.delegate ratchetScrollerDidRatchetedToTooth:row];
}
}
}
}
The "RatchetScrollUnit" class :
#interface RatchetScrollUnit ()
#property (nonatomic,assign) BOOL observed;
#property (nonatomic,weak) NSObject *myObserver;
#end
#implementation RatchetScrollUnit
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_observed = NO;
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)dealloc {
if (self.observed) {
[self removeObserver:self.myObserver forKeyPath:#"frame" context:nil];
}
}
- (void)addRatchetObserver:(NSObject*)observer {
if (self.observed) {
return;
}
self.observed = YES;
self.myObserver = observer;
//
[self addObserver:observer
forKeyPath:#"frame"
options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew
context:nil];
}
Have a try
I want to use two picker views in same view where first picker view is of category and when a category is selected, its corresponding values are added to second picker view. How can I make it possible? Values are taken from JSON.
This is fairly simple. You must be populating your second picker view from an array.
Make your view controller the delegate and data source for both the picker views.
Then when the 'func pickerView(UIPickerView, didSelectRow: Int, inComponent: Int)' delegate method is called when you select an item in the first picker view, update the data array for the second picker view and call reloadAllComponents on it
Here is some code
func pickerView(UIPickerView, didSelectRow: Int, inComponent: Int)
{
if (pickerView == self.firstPicker)
{
//calculate your data array for the second picker here
self.secondPickerView.reloadAllComponents()
}
}
You can get value selected in first picker view like this
NSInteger row;
NSArray *firstPickerViewDataArray;
UIPickerView *firstPickerView;
NSString *selectedValue;
row = [firstPickerView selectedRowInComponent:0];
selectedValue = [firstPickerViewDataArray objectAtIndex:row];
Get the data from server or wherever you want and store that into another array like
NSArray * secondPickerViewDataArray;
After that you can use below method to show values
- (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
if (pickerView == secondPickerView){
// Do whatever you want based on selected value in first PickerView
return secondPickerViewDataArray[row];
}else{
//stuff for first pickerView
}
}
i have two different UIpickerView's in same storyboard, and i want to add change event on them and bind different actions with them.
i used below code, it adds event to both the UIpickerView but i want to add to different actions on both of them. But in this case, as both are triggering same action thats why doing the same work.
- (void)pickerView:_picker didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
}
You can set tag to each UIPickerView in storyboard, and now in selection method
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
if(pickerView.tag == 0) {
} else if (pickerView.tag == 1) {
}
}
based on the tag you differentiate the which UIPickerView is selected.
if (pickerView == self.picker1) {
//do action
}
else if (pickerView == self.picker2) {
//do action
}
Obviously have each picker declared separately (for example: picker1 & picker2) in your .h file and then add this to your didSelect with the appropriate action for each picker
I need the last selected row in my pickerview to be totally recalled when the view appears! I have this code in my viewDidAppear it animates to the last selected row but it doesn't really call the delegate and the NSLog in my rows won't print without touching my picker and reselect them.
How do I do that?
- (void)viewDidAppear:(BOOL)animated {
NSUserDefaults *pickerViewSelectionDefaults = [NSUserDefaults standardUserDefaults];
[tasbeehPicker selectRow:[pickerViewSelectionDefaults integerForKey:#"picker"]
inComponent:0 animated:YES];
[pickerViewSelectionDefaults synchronize];
[UIPicker reloadAllComponents];
NSLog(#"Last selcted row was %d ",[[NSUserDefaults standardUserDefaults] integerForKey:#"picker"]);
}
The delegate is not supposed to be called when you update the picker through code. The delegates are only called when the user interacts with the picker. If you need the same code called in both cases then do something like this:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[myPicker selectRow:4 inComponent:0 animated:animated];
[self handlePickerSelection:myPicker];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
[self handledPickerSelection:pickerView];
}
- (void)handlePickerSelection:(UIPickerView *)pickerView {
// process selection
}
You can get selected row from below method,
Add this code in viewDidAppear,
int index = [pickerViewObject selectedRowInComponent:0];
[pickerViewObject selectRow:index inComponent:0 animated:YES];
selectedRowInComponent will give you selected row number, and then you can use selectRow:Row_number inComponent:Component_number animated:YES to select any row, picker delegate method will not get called.
If you want to save selected row in NSUserDefaults than you can do in in viewWillDisappear,
int index = [pickerViewObject selectedRowInComponent:0];
now save this index value in NSUserDefaults.
IF I wanted a picker, for states/provinces, i haven't seen an example, but I mocked up shown above, a picker for US/Can/Mex. wondering can you dynamically switch the NSMutableArray for the UIPickerView and then have it somehow reload each time you click on US/Can/Mex buttons??? How do I go about doing this. What approach do I take. Looking for someone to point a beginner for a clue in the right direction.
You will have to implement the datasource and the delegate.
once you press a button, set an array pointer to the appropriate array.
than call [thePicker reloadAllComponents];
-(IBAction) usButtonPressed:(id)sender{
self.inputArray = self.usArray;
[self.thePicker reloadAllComponents];
}
-(IBAction) mexicoButtonPressed:(id)sender{
self.inputArray = self.mexicoArray;
[self.thePicker reloadAllComponents];
}
the datasource methods:
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [self.inputArray count];
}
the delegate methods:
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [self.inputArray objectAtIndex:row];
}
Swift: xcode 6.1
// reload the component of picker (Used this inside the trigger action or button)
[self.pickerControl reloadAllComponents];
In swift 3,4,or 5 you just write in didSelectRow function like bellow
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.view.endEditing(false)
yourPickerView.selectRow(0, inComponent: 0, animated: true)
yourPickerView.reloadAllComponents();
}
By calling
thePicker.delegate = self;
again, I was able to force refresh the pickerview with new info
Use multiple pickers, or use a switch block to determine which data should be loaded, and reload the picker.
In your button press action,
[self.pickerView selectRow:0 inComponent:0 animated:YES];