I am creating an registration dialog in swift with 3 text field and one Switch and I successfully add three text field two the Alert. The following code shows the same.
You can use the RightView of the TextField to add a button. Adding a switch would be nice but the switch does not fit into the TextField height nor can you change the height. To this end you can add a button and use images to make a TickBox.
I have ripped this out of a project so the example image is a little more than below.
In the ViewController header add the TextField Delegate
@interface CustomTableViewController : UITableViewController <UITextFieldDelegate>
Then create your AlertController and add the TextField
// create an alert controller
UIAlertController *alertWithText = [UIAlertController alertControllerWithTitle:title message:body preferredStyle:UIAlertControllerStyleAlert];
// create the actions handled by each button
UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}];
UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
}];
// add the actions to the alert
[alertWithText addAction:action1];
[alertWithText addAction:action2];
// Establish the weak self reference
__weak typeof(self) weakSelf = self;
[alertWithText addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
// Create button
UIButton *checkbox = [UIButton buttonWithType:UIButtonTypeCustom];
[checkbox setFrame:CGRectMake(2 , 2, 18, 18)]; // Not sure about size
[checkbox setTag:1];
[checkbox addTarget:weakSelf action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
// Setup image for button
[checkbox.imageView setContentMode:UIViewContentModeScaleAspectFit];
[checkbox setImage:[UIImage imageNamed:@"unchecked_checkbox.png"] forState:UIControlStateNormal];
[checkbox setImage:[UIImage imageNamed:@"checked_checkbox.png"] forState:UIControlStateSelected];
[checkbox setImage:[UIImage imageNamed:@"checked_checkbox.png"] forState:UIControlStateHighlighted];
[checkbox setAdjustsImageWhenHighlighted:TRUE];
// Setup the right view in the text field
[textField setClearButtonMode:UITextFieldViewModeAlways];
[textField setRightViewMode:UITextFieldViewModeAlways];
[textField setRightView:checkbox];
// Setup Tag so the textfield can be identified
[textField setTag:-1];
[textField setDelegate:weakSelf];
// Setup textfield
[textField setText:@"Essential"]; // Could be place holder text
}];
[self presentViewController:alertWithText animated:YES completion:nil];
You need to stop the textfield from editing if you purely want that line to be a tick.
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
if(textField.tag == -1){
return NO;
}
return YES;
}
And your action for your button
-(void)buttonPressed:(UIButton*)sender {
if(sender.selected){
[sender setSelected:FALSE];
} else {
[sender setSelected:TRUE];
}
}
Here are some tick box images too (there are plenty out there, you could even make a switch and try and animate).
This answer is for Objective C. It doesn't use text fields but it does add a UISwitch
to a UIAlertController
as asked in the main question. I didn't find anything on SO that does exactly this so I'm posting this answer here, rather than posting another question that will get dinged as a duplicate.
This solution is used to enable users to sort a UITableView
menu (of a list of projects...)
Thanks to the answer by @technerd, I also made the UISwitch
change the text of a UILabel
that is also on the same UIAlertController
view. It uses KVC (Key-Value Coding) in the layer to pass the UILabel
id to the target action when the UISwitch
value is changed. (See the setOrderLabelText
method in the code)
I was also trying to get around the trick of adding newlines ("\n\n\n\n") to the title or message to artificially move things around, by using constraints.
I used a horizontal UIStackView
to hold the UISwitch
and it's corresponding UILabel
, and then used constraints to set the top anchor of the UIStack
and a height constraint on the UIAlertController
view to make it big enough to contain the UIStackView
and the UIAlertController title.
I don't think it is possible to get the height of the title of the UIAlertController
or the height of the action buttons. So I came up with values that worked well on an iPhone X and an iPad 2. As in other SO answers, I will likely come up with a home grown (or find one on GitHub) solution to make this more robust. But since I got this far and got so much from other awesome SO answers, I wanted to give back a bit and share my results.
Here's a screenshot:
And here's the code:
// using KVC, set the label text based on the label tag and toggle the tag
- (void)setOrderLabelText:(UISwitch *)orderSwitch {
UILabel *label = (UILabel *)[orderSwitch.layer valueForKey:@"label"];
label.text = label.tag ? @"Ascending" : @"Descending";
label.tag = label.tag ? 0 : 1;
}
// sort the data based on the user's selections
- (IBAction)sort:(UIButton *)sortButton {
UILabel *label = [[UILabel alloc] init];
label.text = @"Ascending";
label.textColor = UIColor.grayColor;
label.tag = 0;
[label sizeToFit];
UISwitch *orderSwitch = [[UISwitch alloc] init];
orderSwitch.on = YES;
[orderSwitch setOn:YES animated:YES];
// allow the switch to change the text in the label using KVC (key-value coding)
[orderSwitch addTarget:self action:@selector(setOrderLabelText:) forControlEvents:UIControlEventValueChanged];
[orderSwitch.layer setValue:label forKey:@"label"];
UIStackView *stackView = [[UIStackView alloc] init];
stackView.axis = UILayoutConstraintAxisHorizontal;
stackView.spacing = 8;
[stackView addArrangedSubview:orderSwitch];
[stackView addArrangedSubview:label];
UIAlertController *alert = [UIAlertController
alertControllerWithTitle: @"Sort Projects By"
message: nil
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *createdButton = [UIAlertAction
actionWithTitle:@"Created"
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action) {
[self sortBy:@"created" ascending:orderSwitch.isOn];
}];
UIAlertAction *titleButton = [UIAlertAction
actionWithTitle:@"Title"
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action) {
[self sortBy:@"title" ascending:orderSwitch.isOn];
}];
UIAlertAction *subtitleButton = [UIAlertAction
actionWithTitle:@"Subtitle"
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action) {
[self sortBy:@"subtitle" ascending:orderSwitch.isOn];
}];
UIAlertAction *cancelButton = [UIAlertAction
actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
}];
// add action buttons to the alert
[alert addAction:createdButton];
[alert addAction:titleButton];
[alert addAction:subtitleButton];
[alert addAction:cancelButton];
[alert.view addSubview:stackView];
// center the stack in the alert
[stackView.centerXAnchor constraintEqualToAnchor:alert.view.centerXAnchor].active = YES;
// turn off the autoresizing mask or things get weird
stackView.translatesAutoresizingMaskIntoConstraints = NO;
// use a topAnchor constraint to place the stackview, just below the title
// TODO: figure out how to get the height of the alert title (use 64 for now)
[stackView.topAnchor constraintEqualToAnchor:alert.view.topAnchor constant:64].active = YES;
// layout now to set the view bounds so far - NOTE this does not include the action buttons
[alert.view layoutIfNeeded];
// use a height constraint to make the alert view big enough to hold my stack view
// NOTE: strange, but this must include the header view AND all the action buttons
// TODO: figure out how to get the height of the action buttons (use 52 for each action button for now)
CGFloat height = alert.view.bounds.size.height + alert.actions.count * 52 + stackView.bounds.size.height;
[alert.view.heightAnchor constraintEqualToConstant:height].active = YES;
[self presentViewController:alert animated:YES completion:nil];
}
If you use Recycled Steel's answer above with iOS 13 you can use SF Symbols instead of PNGs. It will solve any scaling issues you might have.
checkbox.imageView.tintColor = UIColor.blackColor;
if (@available(iOS 13.0, *)) {
[checkbox setImage: [UIImage systemImageNamed:@"square"] forState: UIControlStateNormal];
[checkbox setImage: [UIImage systemImageNamed:@"checkmark.square"] forState: UIControlStateHighlighted];
[checkbox setImage: [UIImage systemImageNamed:@"checkmark.square"] forState: UIControlStateSelected];
}
This may help you.
Add this method call alertController.view.addSubview(createSwitch())
in above code after alertController.addAction(OKAction)
.
func createSwitch () -> UISwitch{
let switchControl = UISwitch(frame:CGRectMake(10, 20, 0, 0));
switchControl.on = true
switchControl.setOn(true, animated: false);
switchControl.addTarget(self, action: "switchValueDidChange:", forControlEvents: .ValueChanged);
return switchControl
}
func switchValueDidChange(sender:UISwitch!){
print("Switch Value : \(sender.on))")
}
OutPut :