The question is similar to iOS 8 UIActivityViewController and UIAlertController button text color uses window's tintColor but in iOS 9.
I have a UIAlertController and the dismiss button keeps white colour even I have tried to set
[[UIView appearanceWhenContainedIn:[UIAlertController class], nil] setTintColor:[UIColor blackColor]];
UIAlertController *strongController = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:preferredStyle];
strongController.view.tintColor = [UIColor black];
I've run into something similar in the past and the issue seems to stem from the fact that the alert controller's view isn't ready to accept tintColor changes before it's presented. Alternatively, try setting the tint color AFTER you present your alert controller:
[self presentViewController:strongController animated:YES completion:nil];
strongController.view.tintColor = [UIColor black];
In Swift 3.x:
I found the following to work effectively. I call this at app launch .
UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = UIColor.black
So this would change the tint color of all UIAlertViewController button labels in your app globally. The only button label color it doesn't change are those which have a UIAlertActionStyle of destructive.
Objective-C
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Title text" message:#"Message text" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:#"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
//code here…
}];
UIAlertAction* cancel = [UIAlertAction actionWithTitle:#"Later" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
//code here….
}];
[ok setValue:[UIColor greenColor] forKey:#"titleTextColor"];
[cancel setValue:[UIColor redColor] forKey:#"titleTextColor"];
[alertController addAction:ok];
[alertController addAction:cancel];
[alertController.view setTintColor:[UIColor yellowColor]];
[self presentViewController:alertController animated:YES completion:nil];
Swift 3
let alertController = UIAlertController(title: "Title text", message: "Message text", preferredStyle: .alert)
let ok = UIAlertAction(title: "Yes" , style: .default) { (_ action) in
//code here…
}
let cancel = UIAlertAction(title: "Later" , style: .default) { (_ action) in
//code here…
}
ok.setValue(UIColor.green, forKey: "titleTextColor")
cancel.setValue(UIColor.red, forKey: "titleTextColor")
alertController.addAction(ok)
alertController.addAction(cancel)
alertController.view.tintColor = .yellow
self.present(alertController, animated: true, completion: nil)
I was able to solve this by subclassing UIAlertController:
class MyUIAlertController: UIAlertController {
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
//set this to whatever color you like...
self.view.tintColor = UIColor.blackColor()
}
}
This survives a device rotation while the alert is showing.
You also don't need to set the tintColor after presenting the alert when using this subclass.
Though it isn't necessary on iOS 8.4, this code does work on iOS 8.4 as well.
Objective-C implementation should be something like this:
#interface MyUIAlertController : UIAlertController
#end
#implementation MyUIAlertController
-(void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
//set this to whatever color you like...
self.view.tintColor = [UIColor blackColor];
}
#end
After alot of research, I found out how to make this work:
let cancelButton = UIAlertAction(title: button, style: UIAlertAction.Style.cancel, handler: { (action) in alert.dismiss(animated: true, completion: nil)
})
cancelButton.setValue(UIColor.systemBlue, forKey: "titleTextColor")
alert.addAction(cancelButton)
Just change the UIColor.systemBlue to what ever color you want, and it will make just that button a special color. I made this example (I created 3 UIAlertActions to make it.):
With just UIAlertAction.Style.whatever, it can only make it blue or red. If you change the UIColor, it will make it any color you want!
swift3
Tried to use UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = MyColor but this prevents other items unrelated to the UIAlertController from tintColor configuration. I saw it while trying to change the color of navigation bar button items.
I switched to an extension (based on Mike Taverne's response above) and it works great.
extension UIAlertController {
override open func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
//set this to whatever color you like...
self.view.tintColor = MyColor
}
}
You can change it using: Swift 3.x
strongController.view.tintColor = UIColor.green
There is a problem with setting the tint color on the view after presenting; even if you do it in the completion block of presentViewController:animated:completion:, it causes a flicker effect on the color of the button titles. This is sloppy, unprofessional and completely unacceptable.
The one sure-fire way to solve this problem and to do it everywhere, is via adding a category to UIAlertController and swizzling the viewWillAppear.
The header:
//
// UIAlertController+iOS9TintFix.h
//
// Created by Flor, Daniel J on 11/2/15.
//
#import <UIKit/UIKit.h>
#interface UIAlertController (iOS9TintFix)
+ (void)tintFix;
- (void)swizzledViewWillAppear:(BOOL)animated;
#end
The implementation:
//
// UIAlertController+iOS9TintFix.m
//
// Created by Flor, Daniel J on 11/2/15.
//
#import "UIAlertController+iOS9TintFix.h"
#import <objc/runtime.h>
#implementation UIAlertController (iOS9TintFix)
+ (void)tintFix {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method method = class_getInstanceMethod(self, #selector(viewWillAppear:));
Method swizzle = class_getInstanceMethod(self, #selector(swizzledViewWillAppear:));
method_exchangeImplementations(method, swizzle);});
}
- (void)swizzledViewWillAppear:(BOOL)animated {
[self swizzledViewWillAppear:animated];
for (UIView *view in self.view.subviews) {
if (view.tintColor == self.view.tintColor) {
//only do those that match the main view, so we don't strip the red-tint from destructive buttons.
self.view.tintColor = [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0];
[view setNeedsDisplay];
}
}
}
#end
Add a .pch (precompiled header) to your project and include the category:
#import "UIAlertController+iOS9TintFix.h"
Make sure you register your pch in the project properly, and it will include the category methods in every class that uses the UIAlertController.
Then, in your app delegates didFinishLaunchingWithOptions method, import your category and call
[UIAlertController tintFix];
and it will automatically propagate to every single instance of UIAlertController within your app, whether launched by your code or anyone else's.
This solution works for both iOS 8.X and iOS 9.X and lacks the flicker of the tint change post-presentation approach.
Mad props to Brandon above for starting this journey, unfortunately my reputation was not sufficient enough to comment on his post, or else I would have left it there!
[[UIView appearance] setTintColor:[UIColor black]];
this will change all the UIView tintColor as well as UIAlertController's view
The most reasonable way is set the main window's tintColor. As a uniform appearance is what we usually need.
// in app delegate
window.tintColor = ...
Other solutions have defects
Use apperance
UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = ...
Not works on iOS 9, tests with iOS 11 SDK.
[[UIView appearance] setTintColor:[UIColor black]];
Are you serious?
Set UIAlertController view's tintColor is unstable. The color may change when user press the button or after view layout.
Subclass UIAlertController and overwrite layout method is hack way which is unacceptable.
I found a solution to this. Not an elegant solution, but a solution.
I swizzled viewWillAppear: on UIAlertController, then looped through the views and modified the tint color. In my case I had a tintColor set on the entire window and despite setting the tintColor via appearance the UIAlertController maintained the color on the window. I check if the color is equal to that of the window and if so apply a new one. Blindly applying the tintColor to all views will result in the red tint on destructive actions to be reset.
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method swizzleMethod = class_getInstanceMethod(self, #selector(viewWillAppear:));
Method method = class_getInstanceMethod(self, #selector(alertSwizzle_viewWillAppear:));
method_exchangeImplementations(method, swizzleMethod);
});
}
- (void)alertSwizzle_viewWillAppear:(BOOL)animated
{
[self alertSwizzle_viewWillAppear:animated];
[self applyTintToView:self.view];
}
- (void)applyTintToView:(UIView *)view
{
UIWindow *mainWindow = [UIApplication sharedApplication].keyWindow;
for (UIView *v in view.subviews) {
if ([v.tintColor isEqual:mainWindow.tintColor]) {
v.tintColor = [UIColor greenColor];
}
[self applyTintToView:v];
}
}
However this doesn't work on iOS 8, so you'll still need to set the apperance tint color.
[[UIView appearanceWhenContainedIn:[UIAlertController class], nil] setTintColor:[UIColor greenColor]];
In Swift 2.2 you can use following code
// LogOut or Cancel
let logOutActionSheet: UIAlertController = UIAlertController(title: "Hello Mohsin!", message: "Are you sure you want to logout?", preferredStyle: .Alert)
self.presentViewController(logOutActionSheet, animated: true, completion: nil)
let cancelActionButton: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
print("Cancel Tapped")
}
logOutActionSheet.addAction(cancelActionButton)
let logOutActionButton: UIAlertAction = UIAlertAction(title: "Clear All", style: .Default)
{ action -> Void in
//Clear All Method
print("Logout Tapped")
}
logOutActionButton.setValue(UIColor.redColor(), forKey: "titleTextColor")
logOutActionSheet.addAction(logOutActionButton)
I wanted to make the delete button to appear red, so I used .destructive style:
alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler:{(UIAlertAction) in
You have 3 styles for the action buttons:
let style : UIAlertActionStyle = .default
// default, cancel (bold) or destructive (red)
let alertCtrl = UIAlertController(....)
alertCtrl.addAction( UIAlertAction(title: "click me", style: style, handler: {
_ in doWhatever()
}))
Related
How to create a view controller that is used as a picker? It shows up from the bottom of a screen over the current context, and covers just a part of the screen. After a value is picked, it is passed back to a view controller that presented the picker in iOS.
Try this in Objective C:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Success" message:#"Thank you for coming here" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:#"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){
//define your custom action here or data passing here
[self dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:okAction];
[self presentViewController:alert animated:YES completion:nil];
Try following useful library :
https://github.com/madjid/MMPickerView
Code snippet
NSArray *strings = #[#"This", #"is", #"just", #"an array", #"of strings."];
[MMPickerView showPickerViewInView:self.view
withStrings:strings
withOptions:nil
completion:^(NSString *selectedString) {
//selectedString is the return value which you can use as you wish
self.label.text = selectedString;
}];
Add a UIViewController and Design as You Want.. Give that View Controller a Stroyboard ID...
add the following code in picker view controller Class
var mainVC : YourSendingClassName!
when user didSelectRowAtIndexPath..
call self.dismiss(true , completion {
self.mainVC.userPickedValue(theValueYouWannaPass)
})
now in your sender class if you wanna use a button create an Action from it... according to your requirement the button should be at bottom..
add the following code in Button Action
let sliderViewController = storyboard?.instantiateViewController(withIdentifier: "YourStoryBoard ID") as! YourPickerClass
sliderViewController.modalPresentationStyle = .popover
//set width and height as you need
sliderViewController.preferredContentSize = CGSize(width: 253, height: 160)
sliderViewController.mainVC = self
let popoverMenuViewController = sliderViewController.popoverPresentationController
popoverMenuViewController?.permittedArrowDirections = .down
popoverMenuViewController?.delegate = self
popoverMenuViewController?.sourceView = sender
popoverMenuViewController?.sourceRect = sender.frame
present(sliderViewController, animated: true, completion: nil)
after the action create a function
func userPickedValue(value : YourDataype) {
// Do something with received value
}
This is the code I am writing to display UIActionSheet.
actionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedString(#"updateresponseforrecurring", nil) delegate:self cancelButtonTitle:NSLocalizedString(#"Cancel", nil) destructiveButtonTitle:nil otherButtonTitles:
NSLocalizedString(#"updateresponseonlyforthis", nil),
NSLocalizedString(#"updateresponseforallactivities", nil),
nil];
actionSheet.tag = 2;
[actionSheet showInView:[UIApplication sharedApplication].keyWindow];
This is what I get using this :
Clearly, second option is longer and thus the size gets smaller to accommodate the width.
But I want the same font size for all the options which leaves me with multiline. Also tried with UIAlertController but not able to set multiline text.
How to achieve this ?
This seems to work in iOS 10 and Swift 3.1:
UILabel.appearance(whenContainedInInstancesOf: [UIAlertController.self]).numberOfLines = 2
This is not possible with the standard UIAlertController or UIAlertView.
I would recommend you to make it shorter. Why don't you make an alert and type something like this:
Do you want to update the response only for this instance or for all
activities in this series.
The answers would be these:
Only this instance
All activities
In iOS 10:
If you want to apply it to all UIAlertController, you can use these lines of code:
[[UILabel appearanceWhenContainedIn:[UIAlertController class], nil] setNumberOfLines:2];
[[UILabel appearanceWhenContainedIn:[UIAlertController class], nil] setFont:[UIFont systemFontOfSize:9.0]];
put this in didFinishLaunchingWithOptions method of the AppDelegate.
try this
let alert = UIAlertController(title: title,
message: "you message go here",
preferredStyle:
UIAlertControllerStyle.alert)
let cancelAction = UIAlertAction(title: "Cancel",
style: .cancel, handler: nil)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
UILabel.appearance(whenContainedInInstancesOf:
[UIAlertController.self]).numberOfLines = 0
UILabel.appearance(whenContainedInInstancesOf:
[UIAlertController.self]).lineBreakMode = .byWordWrapping
I'm fairly new to iOS, so please keep answers clear. I've been toying with encapsulating UIAlertController in another UIViewController to use it as a replacement for UIActionSheet as it's deprecated in iOS 8.
The idea would be a replacement for UIActionSheet that is backward and forward compatible, , call it ImmortalActionSheet for instance.
If the component is used in iOS 8 or greater, it can use UIAlertController, otherwise it would fall back to UIActionSheet. That would make it a backward and forward compatible action sheet to replace many action sheets around an application I'm working with. And yes I need to maintain backward compatibility.
I've prototyped this but for whatever reason, when I present ImmortalActionSheet, the UIAlertController view itself will always show up at the top left (0, 0). No matter if I change the center, it never moves.
-(void) loadView
{
UIView * child = nil;
if ( [UIAlertController class] )
{
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:#"Make Choice!!" message:#"Choose one!" preferredStyle:UIAlertControllerStyleActionSheet];
for (UIAlertAction * action in actions ){
[alertController addAction:action];
}
child = alertController.view;
[self addChildViewController:alertController];
}
self.view = [[UIView alloc] init];
self.view.backgroundColor = [UIColor clearColor];
[self.view addSubview:child];
}
I need to mark selected button in UIActionSheet.
How can I add checkmark in UIActionSheet button?
Is there some way specific for UIActionSheet, without standard adding image inside button.
otherButtonTitles:#"√ 1", #" 2",
Starting from iOS 8.0 you can use UIAlertController. In UIAlertController, each button item is know as UIAlertAction which add accordingly. UIAlertAction contains a runtime property called image which you can set as per your need.
UIAlertActioin *action1 = [UIAlertAction actionWithTitle:#"1" style: UIAlertActionStyleDefault handler:(void (^)(UIAlertAction *action)) {
[action setValue:[UIImage imageNamed:#"your-image-name"] forKey:#"_image"];
}];
Now that iOS8 deprecated UIActionsheet and UIAlertview the customization working on iOS7 is not taking effect anymore. So far the only customization I'm aware is the tint color. And what I need is changing the title's font size and style which I haven't found any way of doing so with the new UIAlertAction.
Already referred to this but I'm still hoping there's a way to change at least the title size and font.
Providing you some of my code for UIAlertAction
UIAlertController * alertActionSheetController = [UIAlertController alertControllerWithTitle:#"Settings"
message:#""
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction * aboutTheAppAction = [UIAlertAction actionWithTitle:#"About the App"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action){
NSLog(#"About the app");
[self openAbout];
}];
[alertActionSheetController addAction:aboutTheAppAction];
[self presentViewController:alertActionSheetController animated:YES completion:nil];
You can change UIAlertAction's Font and color.
First you need to add UILabel Category
#interface UILabel (FontAppearance)
#property (nonatomic, copy) UIFont * appearanceFont UI_APPEARANCE_SELECTOR;
#end
#implementation UILabel (FontAppearance)
-(void)setAppearanceFont:(UIFont *)font {
if (font)
[self setFont:font];
}
-(UIFont *)appearanceFont {
return self.font;
}
#end
Category File is also Uploaded on following URL
https://www.dropbox.com/s/em91fh00fv3ut4h/Archive.zip?dl=0
After importing That file You need to call following function.
UILabel * appearanceLabel = [UILabel appearanceWhenContainedIn:UIAlertController.class, nil];
[appearanceLabel setAppearanceFont:yourDesireFont]];
Above code is tested on Color and font. and that will only valid for iOS8 or greater.
It is possible to change alert action's font using private APIs. It may get you app rejected, I have not yet tried to submit such code.
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)
let action = UIAlertAction(title: "Some title", style: .Default, handler: nil)let attributedText = NSMutableAttributedString(string: "Some title")
let range = NSRange(location: 0, length: attributedText.length)
attributedText.addAttribute(NSKernAttributeName, value: 1.5, range: range)
attributedText.addAttribute(NSFontAttributeName, value: UIFont(name: "ProximaNova-Semibold", size: 20.0)!, range: range)
alert.addAction(action)
presentViewController(alert, animated: true, completion: nil)
// this has to be set after presenting the alert, otherwise the internal property __representer is nil
guard let label = action.valueForKey("__representer")?.valueForKey("label") as? UILabel else { return }
label.attributedText = attributedText