ios - ¿Hay alguna manera de agregar UIPickerView en UIAlertController(Alert o ActionSheet) en Swift?
(11)
Aquí hay un proyecto de github que muestra un UIDatePicker en el centro de la pantalla. No utiliza UIAlertController o UIAlertView. No creo que esa sea la intención de las clases de alerta.
Soy totalmente nuevo en Swift (y en la programación de iOS), pero comencé a jugar con eso (no era una buena idea cuando todo sigue siendo la versión beta: D). Así que traté de averiguarlo por mi cuenta, pero todavía nada. Incluso trató de agregar subvista que contiene el selector sin ningún éxito. Entonces, ¿alguien puede ayudarme?
Bueno, este es mi código final que funcionó para mí. Es una mezcla de algunas ideas. Las razones principales por las que aceptaré mi respuesta es que mi código está en Swift, mi código usa UIAlertController, mi código es para el selector. Quiero agradecerle a Jageen, mi respuesta se basa en su idea.
func showPickerInActionSheet(sentBy: String) {
var title = ""
var message = "/n/n/n/n/n/n/n/n/n/n";
var alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.ActionSheet);
alert.modalInPopover = true;
//Create a frame (placeholder/wrapper) for the picker and then create the picker
var pickerFrame: CGRect = CGRectMake(17, 52, 270, 100); // CGRectMake(left), top, width, height) - left and top are like margins
var picker: UIPickerView = UIPickerView(frame: pickerFrame);
/* If there will be 2 or 3 pickers on this view, I am going to use the tag as a way
to identify them in the delegate and datasource. /* This part with the tags is not required.
I am doing it this way, because I have a variable, witch knows where the Alert has been invoked from.*/
if(sentBy == "profile"){
picker.tag = 1;
} else if (sentBy == "user"){
picker.tag = 2;
} else {
picker.tag = 0;
}
//set the pickers datasource and delegate
picker.delegate = self;
picker.dataSource = self;
//Add the picker to the alert controller
alert.view.addSubview(picker);
//Create the toolbar view - the view witch will hold our 2 buttons
var toolFrame = CGRectMake(17, 5, 270, 45);
var toolView: UIView = UIView(frame: toolFrame);
//add buttons to the view
var buttonCancelFrame: CGRect = CGRectMake(0, 7, 100, 30); //size & position of the button as placed on the toolView
//Create the cancel button & set its title
var buttonCancel: UIButton = UIButton(frame: buttonCancelFrame);
buttonCancel.setTitle("Cancel", forState: UIControlState.Normal);
buttonCancel.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal);
toolView.addSubview(buttonCancel); //add it to the toolView
//Add the target - target, function to call, the event witch will trigger the function call
buttonCancel.addTarget(self, action: "cancelSelection:", forControlEvents: UIControlEvents.TouchDown);
//add buttons to the view
var buttonOkFrame: CGRect = CGRectMake(170, 7, 100, 30); //size & position of the button as placed on the toolView
//Create the Select button & set the title
var buttonOk: UIButton = UIButton(frame: buttonOkFrame);
buttonOk.setTitle("Select", forState: UIControlState.Normal);
buttonOk.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal);
toolView.addSubview(buttonOk); //add to the subview
//Add the tartget. In my case I dynamicly set the target of the select button
if(sentBy == "profile"){
buttonOk.addTarget(self, action: "saveProfile:", forControlEvents: UIControlEvents.TouchDown);
} else if (sentBy == "user"){
buttonOk.addTarget(self, action: "saveUser:", forControlEvents: UIControlEvents.TouchDown);
}
//add the toolbar to the alert controller
alert.view.addSubview(toolView);
self.presentViewController(alert, animated: true, completion: nil);
}
func saveProfile(sender: UIButton){
// Your code when select button is tapped
}
func saveUser(sender: UIButton){
// Your code when select button is tapped
}
func cancelSelection(sender: UIButton){
println("Cancel");
self.dismissViewControllerAnimated(true, completion: nil);
// We dismiss the alert. Here you can add your additional code to execute when cancel is pressed
}
// returns number of rows in each component..
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
if(pickerView.tag == 1){
return self.profilesList.count;
} else if(pickerView.tag == 2){
return self.usersList.count;
} else {
return 0;
}
}
// Return the title of each row in your picker ... In my case that will be the profile name or the username string
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
if(pickerView.tag == 1){
var selectedProfile: Profiles = self.profilesList[row] as Profiles;
return selectedProfile.profileName;
} else if(pickerView.tag == 2){
var selectedUser: Users = self.usersList[row] as Users;
return selectedUser.username;
} else {
return "";
}
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if(pickerView.tag == 1){
var choosenProfile: Profiles = profilesList[row] as Profiles;
self.selectedProfile = choosenProfile.profileName;
} else if (pickerView.tag == 2){
var choosenUser: Profiles = usersList[row] as Users;
self.selectedUsername = choosenUser.username;
}
}
Necesitaba hacer lo mismo y así es como lo resolví. Hice algo parecido a Jageen. Mi código está abajo. Tenga en cuenta que puse valores en el UIPickerView en una parte diferente del código.
//Need to use an UIAlertController for iOS 8 instead of an action view
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil
message:@"/n/n/n/n/n/n/n/n"
preferredStyle:UIAlertControllerStyleAlert];
//Make a frame for the picker & then create the picker
CGRect pickerFrame = CGRectMake(0, 0, 270, 100);
UIPickerView *regionsPicker = [[UIPickerView alloc] initWithFrame:pickerFrame];
//There will be 3 pickers on this view so I am going to use the tag as a way
//to identify them in the delegate and datasource
regionsPicker.tag = 1;
//set the pickers datasource and delegate
regionsPicker.dataSource = self;
regionsPicker.delegate = self;
//set the pickers selection indicator to true so that the user will now which one that they are chosing
[regionsPicker setShowsSelectionIndicator:YES];
//Add the picker to the alert controller
[alert.view addSubview:regionsPicker];
//make the toolbar view
UIView *toolView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 270.0f, 44.f)];
toolView.backgroundColor = [UIColor blackColor]; //set it''s background
//add buttons to the view
CGRect buttonFrame = CGRectMake(0, 5, 100, 30); //size & position of the button as placed on the toolView
//make the cancel button & set the title
UIButton *button = [[UIButton alloc] initWithFrame: buttonFrame];
[button setTitle: @"Cancel" forState: UIControlStateNormal];
[button setTitleColor: [UIColor blueColor] forState: UIControlStateNormal]; //make the color blue to keep the same look as prev version
[toolView addSubview:button]; //add to the subview
//Add the tartget
[button addTarget: self
action: @selector(cancelRegionSet)
forControlEvents: UIControlEventTouchDown];
//do the same for the select button
buttonFrame = CGRectMake(90, 5, 100, 30);
UIButton *selButton = [[UIButton alloc] initWithFrame:buttonFrame];
[selButton setTitle:@"Select" forState:UIControlStateNormal];
[selButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[toolView addSubview:selButton];
[selButton addTarget: self
action: @selector(dismissRegionSet:)
forControlEvents: UIControlEventTouchDown];
//add the toolbar to the alert controller
[alert.view addSubview:toolView];
[self presentViewController:alert animated:NO completion:nil];
Puede agregar Picker como UIActionSheet en iOS 8 usando Objective-C de la siguiente manera:
colorArray = [[NSMutableArray alloc ]initWithObjects:@"One", @"Two", @"Three", @"Four", @"Five", nil];
picker = [[UIPickerView alloc]init];
picker.frame = CGRectMake(0.0, 44.0,self.view.frame.size.width, 216.0);
picker.dataSource = self;
picker.delegate = self;
picker.showsSelectionIndicator = true;
picker.backgroundColor = [UIColor whiteColor];
UIToolbar* pickerDateToolbar = [[UIToolbar alloc] initWithFrame: CGRectMake(0, 0, 320, 44)]; //(frame: CGRectMake(0, 0, 320, 44))
pickerDateToolbar.barStyle = UIBarStyleBlackTranslucent;//UIBarStyle.Black
pickerDateToolbar.barTintColor = [UIColor whiteColor];
pickerDateToolbar.translucent = true;
actionView.backgroundColor = [UIColor whiteColor];
UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(150, 5, 150, 20)];
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor blackColor];
label.shadowColor = [UIColor blackColor];
label.shadowOffset = CGSizeMake(0, 1);
label.font = [UIFont systemFontOfSize:15];//[UIFont boldSystemFontOfSize:15];
label.text = @"Select a Status";
UIBarButtonItem *labeltext= [[UIBarButtonItem alloc] initWithCustomView:label];
UIBarButtonItem *cancelBtn = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(cancel_clicked:)];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStylePlain target:self action:@selector(done_clicked:)];
pickerDateToolbar.items = [[NSArray alloc] initWithObjects:cancelBtn,flexSpace,labeltext,doneBtn,nil];
cancelBtn.tintColor = [UIColor blueColor];
doneBtn.tintColor = [UIColor blueColor];
[actionView addSubview:pickerDateToolbar];
[actionView addSubview:picker];
if (window != nil) {
[window addSubview:actionView];
}
else
{
[self.view addSubview:actionView];
}
[UIView animateWithDuration:(0.2) animations:^{
actionView.frame = CGRectMake(0, self.view.frame.size.height - 260.0, self.view.frame.size.width, 260.0);
self.view.alpha = 0.5;
actionView.alpha = 1;
}];
En ViewDidLoad agregar esto,
UIApplication * delegate = [UIApplication sharedApplication];
UIWindow *myWindow = delegate.keyWindow;
NSArray *myWindow2 = delegate.windows;
if(myWindow == [UIApplication sharedApplication].keyWindow)
window = myWindow;
else
window = myWindow2[0];
actionView = [[UIView alloc]initWithFrame:CGRectMake(0, UIScreen.mainScreen.bounds.size.height, UIScreen.mainScreen.bounds.size.width, 240.0)];
// Esto es para crear ActionView y para el fondo descolorido cuando el selector se muestra en la ventana emergente.
-(void)cancel_clicked:(id)sender{
NSLog(@"cancel clicked");
[UIView animateWithDuration:0.2 animations:^{
actionView.frame = CGRectMake(0, UIScreen.mainScreen.bounds.size.height - 260.0, UIScreen.mainScreen.bounds.size.width, 260.0);
} completion:^(BOOL finished) {
self.view.alpha = 1;
[actionView removeFromSuperview];
}];
}
// Cancel Button Click Method.
-(void)done_clicked:(id)sender{
NSLog(@"done clicked");
self.view.userInteractionEnabled = TRUE;
[UIView animateWithDuration:0.2 animations:^{
actionView.frame = CGRectMake(0, UIScreen.mainScreen.bounds.size.height - 260.0, UIScreen.mainScreen.bounds.size.width, 260.0);
} completion:^(BOOL finished) {
_permitStatus.text = [colorArray objectAtIndex:[picker selectedRowInComponent:0]];
self.view.alpha = 1;
[actionView removeFromSuperview];
}];
}
// Haga clic en el botón Hecho.
Puede usar un código similar en En iOS8 / Swift para agregar sus propios controles a una alerta (en lugar de una hoja de acción) que aparece en el centro de la pantalla.
El único problema que tuve al hacer esto con alert.addSubView fue que la vista de alerta solo se dimensiona según los controles que ha agregado a través de los métodos de clase. Luego debe agregar sus propias restricciones para asegurarse de que la alerta abarca todos sus controles.
He agregado un ejemplo aquí como la pregunta original para Alert o ActionSheet
func addAlert(){
// create the alert
let title = "This is the title"
let message = "This is the message"
var alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert);
alert.modalInPopover = true;
// add an action button
let nextAction: UIAlertAction = UIAlertAction(title: "Action", style: .Default){action->Void in
// do something
}
alert.addAction(nextAction)
// now create our custom view - we are using a container view which can contain other views
let containerViewWidth = 250
let containerViewHeight = 120
var containerFrame = CGRectMake(10, 70, CGFloat(containerViewWidth), CGFloat(containerViewHeight));
var containerView: UIView = UIView(frame: containerFrame);
alert.view.addSubview(containerView)
// now add some constraints to make sure that the alert resizes itself
var cons:NSLayoutConstraint = NSLayoutConstraint(item: alert.view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: containerView, attribute: NSLayoutAttribute.Height, multiplier: 1.00, constant: 130)
alert.view.addConstraint(cons)
var cons2:NSLayoutConstraint = NSLayoutConstraint(item: alert.view, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: containerView, attribute: NSLayoutAttribute.Width, multiplier: 1.00, constant: 20)
alert.view.addConstraint(cons2)
// present with our view controller
presentViewController(alert, animated: true, completion: nil)
}
Swift 2.0:
Cree una vista de muestra o de segmento de muestra y agréguela como subvista del controlador uialercontroller. Implemente delegados de uipickerview y presente el controlador uialert. Así es como logré lo mismo.
class ViewController:
UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {
var samplePicker: UIPickerView = UIPickerView()
var sampleSegment:UISegmentedControl = UISegmentedControl ()
var alertController:UIAlertController = UIAlertController()
var buildings:[String] = ["BankBuilding", "Cinema" , "CornerShop", "Greg''s House","14th Street"]
override func viewDidLoad() {
super.viewDidLoad()
samplePicker = UIPickerView(frame: CGRectMake(10.0, 40.0, 250, 150))
samplePicker.delegate = self;
samplePicker.dataSource = self;
samplePicker.showsSelectionIndicator = true
samplePicker.tintColor = UIColor.redColor()
samplePicker.reloadAllComponents()
sampleSegment = UISegmentedControl(items: NSArray(object: " Dismiss ") as [AnyObject])
sampleSegment.momentary = true
sampleSegment.frame = CGRectMake(25, 10.0, 100.0, 30.0)
sampleSegment.tintColor = UIColor.blackColor()
sampleSegment.backgroundColor = UIColor.orangeColor()
sampleSegment.addTarget(self, action: "dismissAlert", forControlEvents: UIControlEvents.ValueChanged)
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 3
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return buildings[row] as String
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
print(buildings[0])
}
func pickerView(pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
return 36.0
}
override func viewDidAppear(animated: Bool)
{
alertController = UIAlertController(title: " /n/n/n/n/n/n/n/n/n/n", message: "", preferredStyle: UIAlertControllerStyle.Alert)
alertController.view.addSubview(sampleSegment)
alertController.view.addSubview(samplePicker)
self.presentViewController(alertController, animated: true, completion: nil)
}
func dismissAlert()
{
alertController.dismissViewControllerAnimated(true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Swift 3.0:
func showPickerInActionSheet() {
let title = ""
let message = "/n/n/n/n/n/n/n/n/n/n";
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.actionSheet);
alert.isModalInPopover = true;
//Create a frame (placeholder/wrapper) for the picker and then create the picker
let pickerFrame = CGRect(x: 17, y: 52, width: 270, height: 100)
let picker: UIPickerView = UIPickerView(frame: pickerFrame)
//set the pickers datasource and delegate
picker.delegate = self
picker.dataSource = self
//Add the picker to the alert controller
alert.view.addSubview(picker)
//Create the toolbar view - the view witch will hold our 2 buttons
let toolFrame = CGRect(x: 17, y: 5, width: 270, height: 45)
let toolView: UIView = UIView(frame: toolFrame)
//add buttons to the view
let buttonCancelFrame = CGRect(x: 0, y: 7, width: 100, height: 30) //size & position of the button as placed on the toolView
//Create the cancel button & set its title
let buttonCancel: UIButton = UIButton(frame: buttonCancelFrame);
buttonCancel.setTitle("Cancel", for: .normal)
buttonCancel.setTitleColor(UIColor.blue, for: .normal)
toolView.addSubview(buttonCancel); //add it to the toolView
//Add the target - target, function to call, the event witch will trigger the function call
buttonCancel.addTarget(self, action: Selector("cancelSelection:"), for: UIControlEvents.touchDown);
//add buttons to the view
let buttonOkFrame = CGRect(x: 170, y: 7, width: 100, height: 30)//size & position of the button as placed on the toolView
//Create the Select button & set the title
let buttonOk: UIButton = UIButton(frame: buttonOkFrame);
buttonOk.setTitle("Select", for: UIControlState.normal);
buttonOk.setTitleColor(UIColor.blue, for: UIControlState.normal);
toolView.addSubview(buttonOk); //add to the subview
buttonOk.addTarget(self, action: #selector(HomeViewController.saveDelayTime), for: UIControlEvents.touchDown);
//add the toolbar to the alert controller
alert.view.addSubview(toolView);
self.present(alert, animated: true, completion: nil);
}
func saveProfile(sender: UIButton){
// Your code when select button is tapped
}
func saveUser(sender: UIButton){
// Your code when select button is tapped
}
func cancelSelection(sender: UIButton){
self.dismiss(animated: true, completion: nil)
// We dismiss the alert. Here you can add your additional code to execute when cancel is pressed
}
// returns number of rows in each component..
func numberOfComponents(in pickerView: UIPickerView) -> Int{
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 60
}
// Return the title of each row in your picker ... In my case that will be the profile name or the username string
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return "/(row)"
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedTime = row
}
func saveDelayTime() {
self.dismiss(animated: true, completion: nil)
UserDefaults.standard.set(selectedTime, forKey: "DelayTimeKey")
let _ = UserDefaults.standard.synchronize()
}
Intenta esto he hecho algún truco ...
El siguiente código funciona para mí en iPod iOS8beta5 + XCode6
Añado el control UIActivityIndicatorView en UIAlertController en object-c.
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil
message:@"Please wait/n/n/n"
preferredStyle:UIAlertControllerStyleAlert];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.center = CGPointMake(130.5, 65.5);
spinner.color = [UIColor blackColor];
[spinner startAnimating];
[alert.view addSubview:spinner];
[self presentViewController:alert animated:NO completion:nil];
Está en object-c, pero por esto se demuestra que también podemos hacerlo usando swift.
Swift 4.1 / Xcode 9.4.1 / iOS 11.4
func addAlert(){
// create the alert
let title = "This is the title"
let message = "This is the message"
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert);
alert.isModalInPopover = true;
// add an action button
let nextAction: UIAlertAction = UIAlertAction(title: "Action", style: .default){action->Void in
// do something
}
alert.addAction(nextAction)
// now create our custom view - we are using a container view which can contain other views
let containerViewWidth = 250
let containerViewHeight = 120
let containerFrame = CGRect(x:10, y: 70, width: CGFloat(containerViewWidth), height: CGFloat(containerViewHeight));
let containerView: UIView = UIView(frame: containerFrame);
alert.view.addSubview(containerView)
// now add some constraints to make sure that the alert resizes itself
let cons:NSLayoutConstraint = NSLayoutConstraint(item: alert.view, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.greaterThanOrEqual, toItem: containerView, attribute: NSLayoutAttribute.height, multiplier: 1.00, constant: 130)
alert.view.addConstraint(cons)
let cons2:NSLayoutConstraint = NSLayoutConstraint(item: alert.view, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.greaterThanOrEqual, toItem: containerView, attribute: NSLayoutAttribute.width, multiplier: 1.00, constant: 20)
alert.view.addConstraint(cons2)
// present with our view controller
present(alert, animated: true, completion: nil)
}
alertController = [UIAlertController alertControllerWithTitle:@" /n/n/n/n/n/n/n/n/n/n"
message:@""
preferredStyle:UIAlertControllerStyleActionSheet];
closePicker = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:@"Cancel"]];
closePicker.momentary = YES;
closePicker.frame = CGRectMake(25, 0.0f, 50.0f, 30.0f);
closePicker.segmentedControlStyle = UISegmentedControlStyleBar;
closePicker.tintColor = [UIColor blackColor];
[closePicker addTarget:self action:@selector(dismissActionSheet:) forControlEvents:UIControlEventValueChanged];
[alertController.view addSubview:closePicker];
UIPickerView *pickerFiliter=[[UIPickerView alloc]init];
pickerFiliter = [[UIPickerView alloc] initWithFrame:CGRectMake(0.0, 40.0, 320.0, 120.0)];
pickerFiliter.showsSelectionIndicator = YES;
pickerFiliter.dataSource = self;
pickerFiliter.delegate = self;
[alertController.view addSubview:pickerFiliter];
[self presentViewController:alertController animated:YES completion:nil];
- (IBAction)dismissActionSheet:(id)sender
{
[alertController dismissViewControllerAnimated:YES completion:nil];
}