How to change the background color of the UIAlertController?

前端 未结 10 1445
耶瑟儿~
耶瑟儿~ 2020-11-30 03:49

Due to strange behavior of UIActionSheet in iOS 8, I have implemented UIAlertController with UIAction as buttons in it. I would like to change the entire background of the U

相关标签:
10条回答
  • 2020-11-30 04:17
    func Alert(View: ViewController, Title: String, TitleColor: UIColor, Message: String, MessageColor: UIColor, BackgroundColor: UIColor, BorderColor: UIColor, ButtonColor: UIColor) {
    
        let TitleString = NSAttributedString(string: Title, attributes: [NSFontAttributeName : UIFont.systemFontOfSize(15), NSForegroundColorAttributeName : TitleColor])
        let MessageString = NSAttributedString(string: Message, attributes: [NSFontAttributeName : UIFont.systemFontOfSize(15), NSForegroundColorAttributeName : MessageColor])
    
        let alertController = UIAlertController(title: Title, message: Message, preferredStyle: .Alert)
    
        alertController.setValue(TitleString, forKey: "attributedTitle")
        alertController.setValue(MessageString, forKey: "attributedMessage")
    
        let okAction = UIAlertAction(title: "OK", style: .Default) { (action) in
    
        }
    
        let cancelAction = UIAlertAction(title: "Cancel", style: .Default, handler: nil)
    
        alertController.addAction(okAction)
        alertController.addAction(cancelAction)
    
    
        let subview = alertController.view.subviews.first! as UIView
        let alertContentView = subview.subviews.first! as UIView
        alertContentView.backgroundColor = BackgroundColor
        alertContentView.layer.cornerRadius = 10
        alertContentView.alpha = 1
        alertContentView.layer.borderWidth = 1
        alertContentView.layer.borderColor = BorderColor.CGColor
    
    
        //alertContentView.tintColor = UIColor.whiteColor()
        alertController.view.tintColor = ButtonColor
    
        View.presentViewController(alertController, animated: true) {
            // ...
        }
    }
    
    0 讨论(0)
  • 2020-11-30 04:19

    The best decision that i found (without white spots on the sides)

    Link to original answer by Vadim Akhmerov

    Answer:

    It is easier to subclass UIAlertController.

    The idea is to traverse through view hierarchy each time viewDidLayoutSubviews gets called, remove effect for UIVisualEffectView's and update their backgroundColor:

    class AlertController: UIAlertController {
    
        /// Buttons background color.
        var buttonBackgroundColor: UIColor = .darkGray {
            didSet {
                // Invalidate current colors on change.
                view.setNeedsLayout()
            }
        }
    
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
    
            // Traverse view hierarchy.
            view.allViews.forEach {
                // If there was any non-clear background color, update to custom background.
                if let color = $0.backgroundColor, color != .clear {
                    $0.backgroundColor = buttonBackgroundColor
                }
                // If view is UIVisualEffectView, remove it's effect and customise color.
                if let visualEffectView = $0 as? UIVisualEffectView {
                    visualEffectView.effect = nil
                    visualEffectView.backgroundColor = buttonBackgroundColor
                }
            }
    
            // Update background color of popoverPresentationController (for iPads).
            popoverPresentationController?.backgroundColor = buttonBackgroundColor
        }
    
    }
    
    
    extension UIView {
    
        /// All child subviews in view hierarchy plus self.
        fileprivate var allViews: [UIView] {
            var views = [self]
            subviews.forEach {
                views.append(contentsOf: $0.allViews)
            }
    
            return views
        }
    
    }
    

    Usage:

    1. Create alert controller(use now AlertController instead UIAlertController)

    let testAlertController = AlertController(title: nil, message: nil, preferredStyle: .actionSheet)

    1. Set background color on custom class "AlertController":

    var buttonBackgroundColor: UIColor = .darkGray

    0 讨论(0)
  • 2020-11-30 04:20

    maybe you like the use the blur effect in the dark mode. Here is a very easy way to get this:

    UIVisualEffectView.appearance(whenContainedInInstancesOf: [UIAlertController.classForCoder() as! UIAppearanceContainer.Type]).effect = UIBlurEffect(style: .dark)
    
    0 讨论(0)
  • 2020-11-30 04:23

    You have to step some views deeper:

    let subview = actionController.view.subviews.first! as UIView
    let alertContentView = subview.subviews.first! as UIView
    alertContentView.backgroundColor = UIColor.blackColor()
    

    And maybe you want to keep original corner radius:

     alertContentView.layer.cornerRadius = 5;
    

    Sorry for the "Swifting" but i'm not familiar with Objective-C. I hope that's similar.

    Of course it's also important to change the title color of the actions. Unfortunately I don't know, how to set the color of actions separately. But this is, how you change all button text colors:

    actionController.view.tintColor = UIColor.whiteColor();
    

    EDIT:

    The corner radius of the UIAlertController has changed since this answer's been posted! Replace this:

     alertContentView.layer.cornerRadius = 5;
    

    to this:

     actionContentView.layer.cornerRadius = 15
    
    0 讨论(0)
  • 2020-11-30 04:24

    Here is a UIAlertController extension that works on both iPad and iPhone. Cancel button will change from a dark colour to white automatically depending on what blurStyle is selected:

    extension UIAlertController {
    
        private struct AssociatedKeys {
            static var blurStyleKey = "UIAlertController.blurStyleKey"
        }
    
        public var blurStyle: UIBlurEffectStyle {
            get {
                return objc_getAssociatedObject(self, &AssociatedKeys.blurStyleKey) as? UIBlurEffectStyle ?? .extraLight
            } set (style) {
                objc_setAssociatedObject(self, &AssociatedKeys.blurStyleKey, style, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    
                view.setNeedsLayout()
                view.layoutIfNeeded()
            }
        }
    
        public var cancelButtonColor: UIColor? {
            return blurStyle == .dark ? UIColor(red: 28.0/255.0, green: 28.0/255.0, blue: 28.0/255.0, alpha: 1.0) : nil
        }
    
        private var visualEffectView: UIVisualEffectView? {
            if let presentationController = presentationController, presentationController.responds(to: Selector(("popoverView"))), let view = presentationController.value(forKey: "popoverView") as? UIView // We're on an iPad and visual effect view is in a different place.
            {
                return view.recursiveSubviews.flatMap({$0 as? UIVisualEffectView}).first
            }
    
            return view.recursiveSubviews.flatMap({$0 as? UIVisualEffectView}).first
        }
    
        private var cancelActionView: UIView? {
            return view.recursiveSubviews.flatMap({
                $0 as? UILabel}
            ).first(where: {
                $0.text == actions.first(where: { $0.style == .cancel })?.title
            })?.superview?.superview
        }
    
        public convenience init(title: String?, message: String?, preferredStyle: UIAlertControllerStyle, blurStyle: UIBlurEffectStyle) {
            self.init(title: title, message: message, preferredStyle: preferredStyle)
            self.blurStyle = blurStyle
        }
    
        open override func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()
    
            visualEffectView?.effect = UIBlurEffect(style: blurStyle)
            cancelActionView?.backgroundColor = cancelButtonColor
        }
    }
    

    The following UIView extension is also needed:

    extension UIView {
    
        var recursiveSubviews: [UIView] {
            var subviews = self.subviews.flatMap({$0})
            subviews.forEach { subviews.append(contentsOf: $0.recursiveSubviews) }
            return subviews
        }
    }
    

    Example:

    let controller = UIAlertController(title: "Dark Alert Controller", message: nil, preferredStyle: .actionSheet, blurStyle: .dark)
    
    // Setup controller actions etc...
    
    present(controller, animated: true, completion: nil)
    

    iPhone:

    iPad:

    0 讨论(0)
  • 2020-11-30 04:25

    You can use the appearance proxy.

    [[UIView appearanceWhenContainedIn:[UIAlertController class], nil] setBackgroundColor:[UIColor blackColor]];
    

    This seems to apply for all but the cancel action when presenting as an action sheet.

    0 讨论(0)
提交回复
热议问题