可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am trying to create a UIAlertController with the action sheet style but I want to change the background color to a gray color. I have been able to find a way to change the background color of the UIAlertController, but not the Cancel button. The Cancel button, which is separate, remains white.
This is the code that I have right now:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[alert addAction:[UIAlertAction actionWithTitle:@"Option 1" style:UIAlertActionStyleDefault handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"Option 2" style:UIAlertActionStyleDefault handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"Option 3" style:UIAlertActionStyleDefault handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"Delete" style:UIAlertActionStyleDestructive handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
UIView *firstSubview = alert.view.subviews.firstObject;
UIView *alertContentView = firstSubview.subviews.firstObject;
for (UIView *subSubView in alertContentView.subviews) {
subSubView.backgroundColor = [UIColor darkGrayColor]; // Here you change background
}
alert.view.tintColor = [UIColor whiteColor];
[self.controller presentViewController:alert animated:YES completion:nil];
And this gives me the following result:
Link
I have visited How to change the background color of the UIAlertController?
but none of the solutions have a custom color for the background of the Cancel button.
Any help would be appreciated!
回答1:
You cannot change color of a default cancel style button. You need to create a custom view controller for the cancel button and set it as a content view controller of a cancel alert action. This way keeps the cancel button separately
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: "Option 1", style: .default, handler: nil))
alertController.addAction(UIAlertAction(title: "Option 2", style: .default, handler: nil))
alertController.addAction(UIAlertAction(title: "Option 3", style: .default, handler: nil))
alertController.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: nil))
if let firstSubview = alertController.view.subviews.first, let alertContentView = firstSubview.subviews.first {
for view in alertContentView.subviews {
view.backgroundColor = .darkGray
}
}
alertController.view.tintColor = .white
let cancelButtonViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "CancelButtonViewController")
let cancelAction = UIAlertAction(title: "", style: .cancel, handler: nil)
cancelAction.setValue(cancelButtonViewController, forKey: "contentViewController")
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
回答2:
There is a very very dirty solution but it works. We are going to use UIAppearance for this purpose.
First of all we need to prepare a special private extension for UIView to be able to change it's subviews background color.
fileprivate extension UIView {
private struct AssociatedKey {
static var subviewsBackgroundColor = "subviewsBackgroundColor"
}
@objc dynamic var subviewsBackgroundColor: UIColor? {
get {
return objc_getAssociatedObject(self, &AssociatedKey.subviewsBackgroundColor) as? UIColor
}
set {
objc_setAssociatedObject(self,
&AssociatedKey.subviewsBackgroundColor,
newValue,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
subviews.forEach { $0.backgroundColor = newValue }
}
}
}
Each time we set subiewsBackgroundColor value UIView will iterate it's subviews and set a new background color for each.
As you can see in the answer below there is a special UIView called _UIAlertControlleriOSActionSheetCancelBackgroundView in UIAlertController's hierarchy which contains a subview with white color (for the cancel button)
Let's try to get it's appearance and use our property to change it's subview color. I guess it's kind of a private api.
if let cancelBackgroundViewType = NSClassFromString("_UIAlertControlleriOSActionSheetCancelBackgroundView") as? UIView.Type {
cancelBackgroundViewType.appearance().subviewsBackgroundColor = .red
}
Place this code in the AppDelegate's didFinishLaunching or a dedicated class.
That's it. Now you can see the cancel button on a red background. Tested on iOS 11.
回答3:
If you want a separate Cancel Button(UIAlertActionStyleCancel) you can't change the background color of the cancel button. If it's your priority then you have to make your own custom View. Or else you can simply add a default action with the title "Cancel".
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:nil]]
(But it won't give you a separate button).
I have debugged the view hierarchy and found this.
回答4:
I used the answer by mbryzinski in my mixed Objective-C / Swift project and was able to modify the Cancel button background color from Objective-C like this:
((UIView *)[NSClassFromString(@"_UIAlertControlleriOSActionSheetCancelBackgroundView")
appearance]).subviewsBackgroundColor = [UIColor yourBackgroundColor];
I included the UIView extension as a .swift file in the project and I had to remove the fileprivate keyword:
extension UIView {
private struct AssociatedKey {
static var subviewsBackgroundColor = "subviewsBackgroundColor"
}
@objc dynamic var subviewsBackgroundColor: UIColor? {
...
Also, the bridging header needs to be imported in the file where the extension is used:
#import "{Your Project's name}-Swift.h"
Modifying the background and text colors of an UIAlertController is a quick (and honestly dirty) way for implementing a dark theme.
There are some slight artifacts around the rounded corners, that become more visible, the darker the background color gets. Especially on the iPad, the artifacts are quite visible.
The correct solution would probably be to use a custom Alert Controller. (Haven't found one yet that looks mostly like the stock one.)
回答5:
I got the solution now. I created sample project and worked out for your question and I got it.
ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
ViewController.m
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[alert addAction:[UIAlertAction actionWithTitle:@"Option 1" style:UIAlertActionStyleDefault handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"Option 2" style:UIAlertActionStyleDefault handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"Option 3" style:UIAlertActionStyleDefault handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"Delete" style:UIAlertActionStyleDestructive handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
UIView *subView = alert.view.subviews.lastObject; //firstObject
UIView *alertContentView = subView.subviews.lastObject; //firstObject
[alertContentView setBackgroundColor:[UIColor darkGrayColor]];
alertContentView.layer.cornerRadius = 5;
[self presentViewController:alert animated:YES completion:nil];
alert.view.tintColor = [UIColor darkGrayColor];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
See the output