UITextField in UIAlertController (border, backgroundColor) - ios

Here is a screenshot of a UIAlertController. I was just playing around custom fonts and textfield properties but I was unable to accomplish the following:
clear background of the UITextField
no ugly border (black box) as shown below
As I dived more into the code and iOS runtime headers, I was able to modify border and background color but the above issue still remains as those properties belong to a container UITextView. Changing background to clearColor doesn't help.
Has anybody ever played around with this? Not sure if I would ever take my app into production with such ugly text fields.
EDIT (May 13, 15) The answer below by Rory McKinnel is tested for iOS 8 - 8.3 and works just fine. The result is below:

Had some fun with this. The following seems to work. Obviously judging by what was required, it has no future proofing and is a patch away from not working.
I figured this out by walking the view hierarchy in the debugger, from which I noticed a UIVisualEffectView. Removing that seems to give you what you want along with setting the containing view to a clear background. Without removing the visual effect, a clear background shows what is behind the alert view itself for some reason.
UIAlertController *alertController =
[UIAlertController alertControllerWithTitle:#"Its Not Pretty!"
message:#"Some times things get ugly!"
preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField){
textField.text = #"Text: No border and clear 8^)";
}];
[self presentViewController:alertController animated:TRUE completion:^{
}];
for (UIView* textfield in alertController.textfields) {
UIView *container = textField.superview;
UIView *effectView = container.superview.subviews[0];
if (effectView && [effectView class] == [UIVisualEffectView class]){
container.backgroundColor = [UIColor clearColor];
[effectView removeFromSuperview];
}
}

here is the important part in swift:
for textfield: UIView in alertController.textfields {
var container: UIView = textField.superview
var effectView: UIView = container.superview.subviews[0]
container.backgroundColor = UIColor.clearColor()
effectView.removeFromSuperview()
}

Swift 3 clear version
alertController.textFields?.forEach {
$0.superview?.backgroundColor = .clear
$0.superview?.superview?.subviews[0].removeFromSuperview()
}

You can try this.
As you need only clear color to textfield of your alertview.
simply add lines of code after your alertview is created.
UITextField *textField = [alertView textFieldAtIndex:0];
textField.backgroundColor=[UIColor clearColor];
textField.superview.backgroundColor=[UIColor clearColor];
EDIT
for alertviewCoontroller you can add
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.backgroundColor=[UIColor clearColor];
textField.superview.backgroundColor=[UIColor clearColor];
}];
Thanks, revert if any confusion.

You can change the border and background color like this:
let subview = alertController!.view.subviews.first! as UIView
let alertContentView = subview.subviews.first! as UIView
alertContentView.backgroundColor = UIColor.lightGrayColor()
alertContentView.layer.cornerRadius = 10;
alertContentView.layer.borderWidth = 2;

Swift 2.0 version:
for textField in alert.textFields! {
if let container = textField.superview, let effectView = container.superview?.subviews.first where effectView is UIVisualEffectView {
container.backgroundColor = UIColor.clearColor()
effectView.removeFromSuperview()
}
}

To address the situation as discussed in #Rory McKinnel and #Matthew where the superview are NULL and address modifying presented view:
extension UIAlertController {
override open func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.textFields?.forEach {
$0.superview?.backgroundColor = .color
$0.superview?.superview?.subviews[0].removeFromSuperview()
}
}
}

This is very hacky, so examine it well before using (tested on iOS 8.3):
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"My Alert"
message:#"This is an alert."
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = #"This is my placeholder";
textField.backgroundColor = [UIColor colorWithRed:246.0/255.0 green:246.0/255.0 blue:246.0/255.0 alpha:1.0]; // You can change it to whatever color you want
[textField superview].backgroundColor = textField.backgroundColor;
[[textField superview] superview].backgroundColor = [UIColor whiteColor];
}];

Related

UIAlertController background color iOS10

Code that I wrote for iOS9, worked really well:
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"Select source"
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
alert.view.backgroundColor = DARK_BLUE;
alert.view.tintColor = NEON_GREEN;
UIView *subview = alert.view.subviews.firstObject;
UIView *alertContentView = subview.subviews.firstObject;
alertContentView.backgroundColor = DARK_BLUE;
alertContentView.layer.cornerRadius = 10;
My point of view is that UIAlertController is inheriting from UIViewController, and UIViewController have property UIView that can be changed. And this is working. Now, that view inherited from UIViewController have it's own subview that is contentView showed as Alert. I can access it as firstObject in array of subviews. Now, why message for sending background color isn't working anymore? Do anyone know some new solution?
For everyone that will bump into the same problem, I've found the solution:
UIAlertController.view contains one subview, that is only container.
That subview contains subview that contains two it's own subviews, one is container, and another is layer for blurring it.
So, it needs for in loop to iterate through that two subviews and change background color of both.
Full code:
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"Select source"
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
alert.view.tintColor = NEON_GREEN;
UIView *firstSubview = alert.view.subviews.firstObject;
UIView *alertContentView = firstSubview.subviews.firstObject;
for (UIView *subSubView in alertContentView.subviews) { //This is main catch
subSubView.backgroundColor = DARK_BLUE; //Here you change background
}
In Swift 3.0
let FirstSubview = alertController.view.subviews.first
let AlertContentView = FirstSubview?.subviews.first
for subview in (AlertContentView?.subviews)! {
subview.backgroundColor = UIColor.black
subview.layer.cornerRadius = 10
subview.alpha = 1
subview.layer.borderWidth = 1
subview.layer.borderColor = UIColor.yellow.cgColor
}
I use this:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Alert!"
message:#"Message of alert"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:#"Ok"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
//Your code
}];
[alert addAction:defaultAction];
UIView * topview = alert.view.subviews.firstObject;
UIView * colorView = topview.subviews.firstObject;
colorView.backgroundColor = [UIColor yellowColor];
colorView.layer.cornerRadius = 10;
[self presentViewController:alert animated:YES completion:nil];

Switch in UIAlert Controller programmatically

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.
let alertController = UIAlertController(title: "Register", message: "", preferredStyle: .Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in
// ...
exit(0)
}
alertController.addAction(cancelAction)
let OKAction = UIAlertAction(title: "Sign UP", style: .Default) { (action) in
// ...
let name0 = alertController.textFields![0] as UITextField
print("Text field: \(name0.text)")
let email1 = alertController.textFields![1] as UITextField
print("Text field: \(email1.text)")
let company2 = alertController.textFields![2] as UITextField
print("Text field: \(company2.text)")
}
alertController.addAction(OKAction)
alertController.addTextFieldWithConfigurationHandler { (textField) in
textField.placeholder = "Name"
textField.keyboardType = .EmailAddress
}
alertController.addTextFieldWithConfigurationHandler { (textField) in
textField.placeholder = "Email"
textField.secureTextEntry = false
}
alertController.addTextFieldWithConfigurationHandler { (textField) in
textField.placeholder = "Company"
textField.secureTextEntry = false
}
self.presentViewController(alertController, animated: true) {
// ...
}
Now I need to add a switch programmatically to the Alert View.We are doing this in Swift2. Is it possible?, i am new to Swift.
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 :
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];
}

Add UISlider on UIAlertView

I want to add a UISlider on a UIAlerView but I am not able to do that. I also google that but I found only two answers and both of them are not showing any any result here is code what I found and applied in my project:
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"title" message:#"msg" delegate:self cancelButtonTitle:#"cancel" otherButtonTitles:#"ok", nil];
alertView.title = #"title";
CGRect frame = CGRectMake(alertView.frame.origin.x+10,alertView.frame.origin.y+20,100, 100.0);
UISlider *slider = [[UISlider alloc] initWithFrame:frame];
[slider addTarget:self action:#selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
[slider setBackgroundColor:[UIColor clearColor]];
slider.minimumValue = 0.0;
slider.maximumValue = 10.0;
slider.continuous = YES;
slider.value = 25.0;
[alertView addSubview:slider];
[alertView show];
I think the frame of slider is not proper thats why its not being displayed.
can anyone help me in that.....
Thank you in advance...
You are adding the Slider in Alert view not in parent view of Alert view.
So your frame value will add the slider some where wide in alert view, result will be hidden on alert view.
so replace this line
CGRect frame = CGRectMake(alertView.frame.origin.x+10,alertView.frame.origin.y+20,100, 100.0);
To
CGRect frame = CGRectMake(10,20,100, 100.0);
The UIAlertView is also a UIView. I indeed, you could add the subview in it. But Apple says
"The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified."
So Better to have custom view which will act as alertview.Like this one

Change the font size of a UITextField in a UIAlertView

Does anyone know how to change the font size of a UITextField within a UIAlertView? The following is my code...
- (void) editTitle
{
NSString *string = kLocalizedString(#"Edit Title");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
message:string
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
UITextField *textField = [alert textFieldAtIndex:0];
if (!self.title) {
textField.text = nil;
}
else {
textField.text = self.title;
}
textField.clearsOnBeginEditing = NO;
textField.clearButtonMode = UITextFieldViewModeAlways;
textField.autocapitalizationType = UITextAutocapitalizationTypeWords;
textField.clearsContextBeforeDrawing = NO;
// These statements have no effect on the size of the text field's font
textField.font = [UIFont systemFontOfSize:16.0];
NSDictionary *attributes = #{
NSFontAttributeName: [UIFont systemFontOfSize:16.0]};
textField.typingAttributes = attributes;
[alert show];
}
After iOS 7.x you cannot customize the appearance of alert views, Why? Because its view hierarchy is private.
It is mentioned clearly in UIAlertView Class Reference:
The UIAlertView class is intended to be used as-is and does not
support subclassing. The view hierarchy for this class is private and
must not be modified.
So unfortunately it is impossible to change the textField font, buttons text color .. etc.
The only solution is using one of the custom UIAlertView's.
You have to use custom alertview. Just check below link.
DTAlertView
It has good animation and textfield can be added too.
Once you use this, you don't have to write such big code.
Hope it helps.
You can create a custom UIAlertView using one of this:
http://code.tutsplus.com/tutorials/ios-sdk-uialertview-custom-graphics--mobile-8886
https://github.com/eaigner/CODialog
and apply your custom textField.
For the second option, in CODialog.m (addTextFieldWithPlaceholder function) you can change the font size, or modify kCODialogTextFieldHeight constant.
field.font = [UIFont systemFontOfSize:kCODialogTextFieldHeight - 8.0];

Alert view is showing white rectangle in iOS7

The following code works perfectly from iOS 5 to 6.1. I even have applications in store with that code:
-(void)showActivityIndicator
{
if(!mLoadingView) //
{
mLoadingView = [[UIAlertView alloc] initWithTitle:#"" message:#"" delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
mLoadingView.tag = kAlertViewTag;
}
[mLoadingView show];
}
- (void)willPresentAlertView:(UIAlertView *)alertView
{
if (alertView.tag == kAlertViewTag)
{
UIActivityIndicatorView *actInd = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
actInd.frame = CGRectMake(128.0f, 45.0f, 25.0f, 25.0f);
[alertView addSubview:actInd];
[actInd startAnimating];
UILabel *l = [[UILabel alloc]init];
l.text = NSLocalizedString(#"PRODUCT_PURCHASE_INDICATOR_TITLE", #"Please wait...");
l.font = [UIFont fontWithName:#"Helvetica" size:16];
float strWidth = [l.text sizeWithFont:l.font].width;
float frameWidth = alertView.frame.size.width;
l.frame = CGRectMake((frameWidth - strWidth)/2, -25, 210, 100);
l.textColor = [UIColor whiteColor];
l.shadowColor = [UIColor blackColor];
l.shadowOffset = CGSizeMake(1.0, 1.0);
l.backgroundColor = [UIColor clearColor];
[alertView addSubview:l];
}
}
It will show alert view without buttons and with activity indicator and label. However in iOS7 I can see only white rounded rectangle, no activity indicator.
What can I do to have this work from iOS 5 to 7?
Update:
To be more descriptive I'm adding screenshots. The following is iOS 5 to 6.1 screenshot. Works fine there.
The following is iOS7. As you can see even the size is smaller. Looks like it's not fully initialized or something.
now addSubview is not available in UIAlertView in iOS7
The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified
As an alternative you can use SVProgressHUD.
From iOS 7 onwards, you can do:
[alertView setValue:customContentView forKey:#"accessoryView"];
to get custom content in a standard alert view.
I had to fix this problem very quickly, hence I have built an iOS7 UIAlertView-style UIView with its animations, which can be extended with any custom content.
There might be others who can use my solution, so I made the whole code available on Github.
Also, if you want to keep using the UIAlertView under previous OS versions, you have to fork the code. It might be as bad as it sounds, I'm using the following:
float sysVer = [[[UIDevice currentDevice] systemVersion] floatValue];
if (sysVer < 7) {
// your old solution
} else {
.. // iOS7 dialog code
}
(Please be aware that this is by no means a real solution - if Apple doesn't want us to use the dialogs for all sorts of things, then we probably shouldn't.)

Resources