How to Distinguish Between Multiple Uipickerviews on One Page

How to distinguish between multiple uipickerviews on one page

Here is the way you can do it:

import UIKit

class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {

@IBOutlet weak var dropdown_1: UIPickerView!
@IBOutlet weak var dropdown_2: UIPickerView!

var arraySizes5 = ["sverysmall", "ssmall", "snormal", "slarge", "sverylarge"]
var arraySizes3 = ["ssmall", "snormal", "slarge"]

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}

func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {

if pickerView == dropdown_1 {
return arraySizes5.count
} else if pickerView == dropdown_2 {
return arraySizes3.count
}
return 0
}

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {

if pickerView == dropdown_1 {
return arraySizes5[row]
} else if pickerView == dropdown_2 {
return arraySizes3[row]
}
return ""
}
}

How to use two UIPickerViews in one view controller?

Based on the information I have in the question, I'd say that you need to set up the data source & delegate methods to handle the ability to distinguish between which picker instance is calling them.

Using the tag property on the picker view is one strategy.

There should be some if/else or switch statements in the methods that have varying logic depending on whether it's the location or the position picker that's being referenced.

How do I use multiple picker views with different data sources in the same view?

When dealing with multiple controls with delegates and data sources, you should consider avoiding view controller bloat (i.e., in the spirit of the single responsibility principle) by creating separate objects for the delegates of the multiple pickers. This keeps this logic out of the view controller, itself, and avoids single cumbersome UIPickerViewDataSource and UIPickerViewDelegate methods trying to service multiple pickers with hairy if-else or switch statements.

For example, here is a view controller that has outlets for two pickers, but rather than encumbering the view controller with code to manage the dataSource and delegate for these pickers, you can have separate objects for each picker, and all the view controller has to do is to say which delegate object will handle which picker:

class ViewController: UIViewController {

@IBOutlet weak var namePicker: UIPickerView!
@IBOutlet weak var numberPicker: UIPickerView!

let namePickerDelegate = NamePickerDelegate()
let numberPickerDelegate = NumberPickerDelegate()

override func viewDidLoad() {
super.viewDidLoad()

namePicker.delegate = namePickerDelegate
namePicker.dataSource = namePickerDelegate

numberPicker.delegate = numberPickerDelegate
numberPicker.dataSource = numberPickerDelegate
}

@IBAction func didTapButton(_ sender: Any) {
let nameValue = namePicker.selectedRow(inComponent: 0)
let numberValue = numberPicker.selectedRow(inComponent: 0)

print("\(nameValue); \(numberValue)")
}

}

The only trick is to make sure to keep a strong reference to those delegate objects, as shown above, because the picker, itself, only has weak references to its delegate, as is best practice.

And the implementation of the picker delegate methods is much cleaner:

class NamePickerDelegate: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
let names = ["Mo", "Larry", "Curley"]

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return names.count
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return names[row]
}
}

class NumberPickerDelegate: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
let numbers: [String] = {
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
return (0 ..< 100).compactMap { formatter.string(for: $0) } // use `flatMap` in Xcode versions prior to 9.3
}()

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return numbers.count
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return numbers[row]
}
}

Now, this is still, obviously, a simplified example, but the beauty is that as the code gets more complicated, the details are encapsulated within separate objects, rather than encumbering a single view controller with all the code.


If you want, you can have the view controller provide the list of strings to the delegate/data source object. In fact, that simplifies it because you need only one class for the picker delegate, and you just instantiate a different one for each picker:

class ViewController: UIViewController {

let names = ["Mo", "Larry", "Curley"]

let numbers: [String] = {
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
return (0 ..< 100).compactMap { formatter.string(for: $0) } // use `flatMap` in Xcode versions prior to 9.3
}()

@IBOutlet weak var numberPickerOne: UIPickerView!
@IBOutlet weak var numberPickerTwo: UIPickerView!
@IBOutlet weak var namePicker: UIPickerView!

lazy var numberPickerOneDelegate: PickerDelegate = PickerDelegate(strings: self.numbers)
lazy var numberPickerTwoDelegate: PickerDelegate = PickerDelegate(strings: self.numbers)
lazy var namePickerDelegate:PickerDelegate = PickerDelegate(strings: self.names)

override func viewDidLoad() {
super.viewDidLoad()

numberPickerOne.delegate = numberPickerOneDelegate
numberPickerOne.dataSource = numberPickerOneDelegate

numberPickerTwo.delegate = numberPickerTwoDelegate
numberPickerTwo.dataSource = numberPickerTwoDelegate

namePicker.delegate = namePickerDelegate
namePicker.dataSource = namePickerDelegate
}

}

class PickerDelegate: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
let strings: [String]

init(strings: [String]) {
self.strings = strings
super.init()
}

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return strings.count
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return strings[row]
}
}

iOS Multiple UIPickerView Only Showing 1 view

You can do something like this. This is just an example, you have to make changes to make it work for your app

#import "ViewController.h"
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>

#define GeoLocation TRUE // FALSE for no latitude/longitude information
#define kPICKERCOLUMN 1
typedef NS_ENUM(NSInteger, PickerType) {
CATEGORY_PICKER,
LOCATION_PICKER,
ORIGINATOR_PICKER,
DESTINATION_PICKER,
STATUS_PICKER
};

#define kPICKERCOLUMN 1
#define kPICKER_TAG 101

@interface ViewController ()

@end

@implementation ViewController
{
UIPickerView *picker;
PickerType pickerType;
}
@synthesize nameTextField, emailTextField, dateTextField, timeTextField, blankTextField, blankbTextField, mlabelcategory, messageTextView;
@synthesize name, emailaddress, date, time, blank, blankb, category, message, email;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
categoryTypes = [[NSArray alloc] initWithObjects:@"Appetizers",@"Breakfast",@"Dessert",@"Drinks",
@"Main Dish/Entree", @"Salad", @"Side Dish", @"Soup", @"Snack",
@"Baby Food", @"Pet Food",nil];

locationTypes = [[NSArray alloc] initWithObjects:@"African",@"American",@"Armenian",@"Barbecue"
,nil];

originatorTypes = [[NSArray alloc] initWithObjects:@"African",@"American",@"Armenian",@"Barbecue",
nil];


destinationTypes = [[NSArray alloc] initWithObjects:@"African",@"American",@"Armenian",@"Barbecue",
nil];


statusTypes = [[NSArray alloc] initWithObjects:@"African",@"American",@"Armenian",@"Barbecue",
nil];
}
return self;
}

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.



nameTextField.text = nil;
emailTextField.text = nil;
dateTextField.text = nil;
timeTextField.text = nil;
blankTextField.text = nil;
blankbTextField.text = nil;
mlabelcategory.text = nil;
messageTextView.text = nil;

picker = [[UIPickerView alloc] initWithFrame:CGRectMake(100,100,400,160)];
picker.showsSelectionIndicator = TRUE;
picker.dataSource = self;
picker.delegate = self;
picker.hidden = YES;
[self.view addSubview:picker];
}


/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)eve
{
picker.hidden = YES;
}

#pragma mark -
#pragma mark picker methods
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return kPICKERCOLUMN;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{

switch (pickerType) {
case CATEGORY_PICKER:
return [categoryTypes count];;
break;
case LOCATION_PICKER:
return [locationTypes count];
break;
case ORIGINATOR_PICKER:
return [originatorTypes count];
break;
case DESTINATION_PICKER:
return [destinationTypes count];
break;
case STATUS_PICKER:
return [statusTypes count];
break;
default: return -1;
break;
}

}



- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
switch (pickerType) {
case CATEGORY_PICKER:
return [categoryTypes objectAtIndex:row];
break;
case LOCATION_PICKER:
return [locationTypes objectAtIndex:row];
break;
case ORIGINATOR_PICKER:
return [originatorTypes objectAtIndex:row];
break;
case DESTINATION_PICKER:
return [destinationTypes objectAtIndex:row];
break;
case STATUS_PICKER:
return [statusTypes objectAtIndex:row];
break;
default: return nil;
break;

}
}




- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
switch (pickerType) {
case CATEGORY_PICKER: {
NSString *categoryType = [categoryTypes objectAtIndex:[pickerView selectedRowInComponent:0]];
[categoryTypeBtn setTitle:categoryType forState:UIControlStateNormal];
break;
}
case LOCATION_PICKER: {

NSString *locationType = [locationTypes objectAtIndex:[pickerView selectedRowInComponent:0]];
[locationTypeBtn setTitle:locationType forState:UIControlStateNormal];
break;

}
case ORIGINATOR_PICKER: {
NSString *originatorType = [originatorTypes objectAtIndex:[pickerView selectedRowInComponent:0]];
[originatorTypeBtn setTitle:originatorType forState:UIControlStateNormal];
break;
}
case DESTINATION_PICKER: {
NSString *destinationType = [destinationTypes objectAtIndex:[pickerView selectedRowInComponent:0]];
[destinationTypeBtn setTitle:destinationType forState:UIControlStateNormal];
break;
}
case STATUS_PICKER:{
NSString *statusType = [statusTypes objectAtIndex:[pickerView selectedRowInComponent:0]];
[statusTypeBtn setTitle:statusType forState:UIControlStateNormal];

break;
}
default:
break;
}
}
-(IBAction) showLocationTypePicker{

pickerType = LOCATION_PICKER;
picker.hidden = NO;
[picker reloadAllComponents];

}
-(IBAction) showCategoryTypePicker{
pickerType = CATEGORY_PICKER;
picker.hidden = NO;
[picker reloadAllComponents];
}

-(IBAction) showOriginatorTypePicker{
pickerType = ORIGINATOR_PICKER;
picker.hidden = NO;
[picker reloadAllComponents];
}

-(IBAction) showDestinationTypePicker{
pickerType = DESTINATION_PICKER;
picker.hidden = NO;
[picker reloadAllComponents];
}

-(IBAction) showStatusTypePicker{
pickerType = STATUS_PICKER;
picker.hidden = NO;
[picker reloadAllComponents];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}

#pragma - getting info from the UI

//NSString *test = nil;


- (IBAction)checkData:(id)sender
{
/*
name = nameTextField.text;
surname = surnameTextField.text;
bornDate = bornDateTextField.text;
address = addressTextField.text;
zipCode = zipTextField.text;
email = emailTextField.text;
*/

//NSLog(@" Name: %@ \n Surname: %@ \n Date of Birth: %@ \n Address: %@ \n Post Code: %@ \n email: %@ \n", name, surname, bornDate, address, zipCode, email);

unsigned int x,a = 0;
NSMutableString *emailmessage; //stringa variabile
emailmessage = [NSMutableString stringWithFormat: @""]; //le stringhe mutabili vanno inizializzate in questo modo!


for (x=0; x<7; x++)
{
switch (x) {
case 0:
if (nameTextField.text == nil) {
[emailmessage appendString:@"Name, "];
a=1;

}
break;

case 1:
if (emailTextField.text == nil)
{
[emailmessage appendString:@"Email Address, "];
a=1;
}
break;

case 2:
if (dateTextField.text == nil)
{
[emailmessage appendString:@"Date of Near Miss, "];
a=1;
}
break;

case 3:
if (timeTextField.text == nil)
{
[emailmessage appendString:@"Time of Near Miss, "];
a=1;
}
break;

case 4:
if (blankTextField.text == nil)
{
[emailmessage appendString:@"Post Code, "];
a=1;
}
break;

case 5:
if (blankbTextField.text == nil)
{
[emailmessage appendString:@"Email, "];
a=1;
}
break;

case 6:
if (mlabelcategory.text == nil)
{
[emailmessage appendString:@"Category, "];
a=1;
}
break;
case 7:
if (messageTextView.text == nil)
{
[emailmessage appendString:@"Observation Description, "];
a=1;
}
break;

default:
break;
}

}

NSLog (@"Email Message: %@", emailmessage);

if (a == 1) {

NSMutableString *popupError;
popupError = [NSMutableString stringWithFormat: @"Per inviare compilare i seguenti campi: "];

[popupError appendString:emailmessage]; //aggiungo i miei errori
[popupError appendString: @" grazie della comprensione."]; //

NSLog(@"%@", popupError);

UIAlertView *chiamataEffettuata = [[UIAlertView alloc]
initWithTitle:@"ATTENTION" //titolo del mio foglio
message:popupError
delegate:self
cancelButtonTitle:@"Ok, correggo" //bottone con cui si chiude il messaggio
otherButtonTitles:nil, nil];
[chiamataEffettuata show]; //istanza per mostrare effettivamente il messaggio
}

else
{
name = nameTextField.text;
emailaddress = emailTextField.text;
date = dateTextField.text;
time = timeTextField.text;
blank = blankTextField.text;
blankb = blankbTextField.text;
category = mlabelcategory.text;
message = messageTextView.text;

NSMutableString *nearmissreport;
nearmissreport = [NSMutableString stringWithFormat: @"<br><br> <b>Name:</b> %@ <br> <b>Email Address:</b> %@ <br> <b>Date of Near Miss:</b> %@ <br> <b>Time of Near Miss:</b> %@ <br> <b>Post Code:</b> %@ <br> <b>Email Address:</b> %@ <br> <b>Category:</b> %@ <br><b>Observation Description:</b> %@ <br>", name, emailaddress, date, time, blank, blankb, category, message];

NSLog(@"Near Miss Report: %@", nearmissreport);

NSMutableString *testoMail;
testoMail = [NSMutableString stringWithFormat: nearmissreport];

NSLog(@"%@", testoMail);


//MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;

[picker setSubject: name];

// Set up the recipients.
NSArray *toRecipients = [NSArray arrayWithObjects:@"paul.haddell@bbmmjv.com",nil];

//NSArray *ccRecipients = [NSArray arrayWithObjects:@"second@example.com",@"third@example.com", nil];
//NSArray *bccRecipients = [NSArray arrayWithObjects:@"four@example.com",nil];

[picker setToRecipients:toRecipients];
//[picker setCcRecipients:ccRecipients];
//[picker setBccRecipients:bccRecipients];

// Attach an image to the email.
//NSString *path = [[NSBundle mainBundle] pathForResource:@"ipodnano" ofType:@"png"];
//NSData *myData = [NSData dataWithContentsOfFile:path];
//[picker addAttachmentData:myData mimeType:@"image/png" fileName:@"ipodnano"];

// Fill out the email body text.
//NSMutableString *emailBody;
testoMail = [NSMutableString stringWithFormat: @"%@", testoMail];

[picker setMessageBody:testoMail isHTML:YES]; //HTML!!!!!!

// Present the mail composition interface.
[self presentViewController:picker animated:YES completion:nil];
}
}


// The mail compose view controller delegate method
- (void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError *)error
{
[self dismissModalViewControllerAnimated:YES];
}

#pragma mark - Mandare email
/*
- (void)sendMail:(NSMutableString*)testoMail{

MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;

[picker setSubject:@"Reclutamento pompieri"];

// Set up the recipients.
NSArray *toRecipients = [NSArray arrayWithObjects:@"reda.bousbah@gmail.com",nil];
//NSArray *ccRecipients = [NSArray arrayWithObjects:@"second@example.com",@"third@example.com", nil];
//NSArray *bccRecipients = [NSArray arrayWithObjects:@"four@example.com",nil];

[picker setToRecipients:toRecipients];
//[picker setCcRecipients:ccRecipients];
//[picker setBccRecipients:bccRecipients];

// Attach an image to the email.
//NSString *path = [[NSBundle mainBundle] pathForResource:@"ipodnano" ofType:@"png"];
//NSData *myData = [NSData dataWithContentsOfFile:path];
//[picker addAttachmentData:myData mimeType:@"image/png" fileName:@"ipodnano"];

// Fill out the email body text.
NSString *emailBody = @"It is raining in sunny California!";
[picker setMessageBody:emailBody isHTML:NO];

// Present the mail composition interface.
[self presentViewController:picker animated:YES completion:nil];


}
*/



#pragma mark - methods to control the keyboard

- (IBAction)backgroundTap:(id)sender //method for resign the keyboard when the background is tapped
{
[nameTextField resignFirstResponder];
[emailTextField resignFirstResponder];
[dateTextField resignFirstResponder];
[timeTextField resignFirstResponder];
[blankTextField resignFirstResponder];
[blankbTextField resignFirstResponder];
[mlabelcategory resignFirstResponder];
[messageTextView resignFirstResponder];

}

- (IBAction)doneButtonPressed:(id)sender
{
NSLog( @"done button pressed");
[sender resignFirstResponder];
}
@end

How to make two UIPickerView's with Done buttons

First, add the parameter to the delegate method declarations:

protocol ToolbarPickerViewDelegate: class {
func didTapDone(pickerView: ToolbarPickerView)
func didTapCancel(pickerView: ToolbarPickerView)
}

Then, when you call the delegate methods in doneTapped and cancelTapped, you also pass in self:

@objc func doneTapped() {
self.toolbarDelegate?.didTapDone(pickerView: self)
}

@objc func cancelTapped() {
self.toolbarDelegate?.didTapCancel(pickerView: self)
}

Now you can check the tag in your VC:

extension MyViewController: ToolbarPickerViewDelegate {

func didTapDone(pickerView: ToolbarPickerView) {
switch pickerView.tag {
case 1:
let row = self.pickerView1.selectedRow(inComponent: 0)
self.pickerView1.selectRow(row, inComponent: 0, animated: false)
self.textField1.text = self.titles1[row]
self.textField1.resignFirstResponder()
case 2:
let row = self.pickerView2.selectedRow(inComponent: 0)
self.pickerView2.selectRow(row, inComponent: 0, animated: false)
self.textField2.text = self.titles2[row]
self.textField2.resignFirstResponder()
default:
break
}

}

func didTapCancel(pickerView: ToolbarPickerView) {
switch pickerView.tag {
case 1:
self.textField1.text = nil
self.textField1.resignFirstResponder()
case 2:
self.textField2.text = nil
self.textField2.resignFirstResponder()
default:
break
}
}
}

How to use multiple UIPickerViews in one view controller

You are referencing a non-existent variable named pickerView. You should update the parameter name to pickerView to avoid issues.

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == foodTypeUIPickerView {
return self.foodTypePickerData.count
} else {
return self. nutritionPickerData.count
}
}

Make a similar change to the other delegate/data source methods.

Appropriate way to add multiple UIPicker controls on page

If only one pickerView will be visible at once, then you should consider using only one pickerView and configure it's delegate/datasource so that you can give it a different datasource for each field.

Unless more than one is visible at once, there really isn't any reason to use more than one in your nib file. And even if you used more than one, you would still have to configure you delegate/datasource methods to handle each individual picker.

Hope that helps.

EDIT: It would be a little bit of work, but if you wanted the pickerView to animate in and out of the view whenever you need and if you wanted to clean your Xib up even more you could do the following:

  • Create a subview containing your pickerView
  • Set up a protocol on the subview to allow you to pass the selected value back to the view controller.
  • Set up your viewController to conform to the protocol on your picker subview.
  • Set the pickerView to be each textField's inputView.
  • Set the textField's delegate methods to configure the dataSource of your subview when editing begins.

By doing this, you have set your textField so that when it receives firstResponder that it will display the pickerView instead of a keyboard.

How to have two UIPickerViews together in one ViewController?

Using Interface Builder, set the tag properties of the two UIPickerViews to 1 and 2, respectively. Then, in each delegate method, use if statements to check the tag of the UIPickerView argument.



Related Topics



Leave a reply



Submit