web-dev-qa-db-fra.com

Comment changer la couleur de fond de UIAlertController?

En raison du comportement étrange de UIActionSheet dans iOS 8, j'ai implémenté UIAlertController avec UIAction en tant que boutons. J'aimerais changer tout l'arrière-plan de UIAlertController. Mais je ne trouve aucun moyen de le faire.

Essayé même avec,

actionController.view.backgroundColor = [UIColor blackColor];

Mais ne m'a pas aidé. Toute contribution à cet égard sera appréciable.

Merci d'avance.

28
GJDK

Vous devez approfondir certains points de vue:

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

Et peut-être que vous voulez conserver le rayon de coin d'origine:

 alertContentView.layer.cornerRadius = 5;

Désolé pour le "Swifting" mais je ne connais pas Objective-C. J'espère que c'est pareil.

Bien sûr, il est également important de changer la couleur du titre des actions. Malheureusement, je ne sais pas comment définir la couleur des actions séparément. Mais voici comment vous modifiez toutes les couleurs du texte des boutons:

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

MODIFIER:

Le rayon de coin de UIAlertController a changé depuis la publication de cette réponse! Remplacez ceci:

 alertContentView.layer.cornerRadius = 5;

pour ça:

 actionContentView.layer.cornerRadius = 15
38
nischtname

peut-être que vous aimez l’utilisation de l’effet de flou dans le mode sombre. Voici un moyen très simple d'obtenir ceci:

UIVisualEffectView.appearance(whenContainedInInstancesOf: [UIAlertController.classForCoder() as! UIAppearanceContainer.Type]).effect = UIBlurEffect(style: .dark)
17
Jeanette Müller

J'ai trouvé une façon très astucieuse de le faire. Vous avez d’abord besoin d’une extension vous permettant de rechercher la UIVisualEffectView dans la UIAlertController:

extension UIView
{
    func searchVisualEffectsSubview() -> UIVisualEffectView?
    {
        if let visualEffectView = self as? UIVisualEffectView
        {
            return visualEffectView
        }
        else
        {
            for subview in subviews
            {
                if let found = subview.searchVisualEffectsSubview()
                {
                    return found
                }
            }
        }

        return nil
    }
}

Important : Vous devez appeler cette fonction après en appelant presentViewController, car ce n'est qu'après le chargement du contrôleur de vue que la vue des effets visuels est insérée en place. Ensuite, vous pouvez changer l’effet qui lui est associé en un effet de flou foncé:

self.presentViewController(actionController, animated: true, completion: nil)

if let visualEffectView = actionController.view.searchVisualEffectsSubview()
{
    visualEffectView.effect = UIBlurEffect(style: .Dark)
}

Et voici le résultat final:

demo picture

Honnêtement, je suis moi-même surpris de voir à quel point cela fonctionne! Je pense que c'est probablement quelque chose que Apple a oublié d'ajouter. De plus, je n'ai pas encore passé l'approbation d'une application à l'aide de ce "hack" (ce n'est pas un hack, nous n'utilisons que des API publiques), mais je suis convaincu qu'il n'y aura pas de problème .

14
Bruno Philipe

pour Swift 3/Swift 4 

let subview =(alert.view.subviews.first?.subviews.first?.subviews.first!)! as UIView

            subview.backgroundColor = UIColor(red: (145/255.0), green: (200/255.0), blue: (0/255.0), alpha: 1.0)

            alert.view.tintColor = UIColor.black

 enter image description here .

7
Shakeel Ahmed

Swift3

Entrez dans une couche supplémentaire, comparez avec Swift2

    let subview1 = alert.view.subviews.first! as UIView
    let subview2 = subview1.subviews.first! as UIView
    let view = subview2.subviews.first! as UIView

    subview.backgroundColor = backgroundColor
    view.backgroundColor = backgroundColor
    view.layer.cornerRadius = 10.0

    // set color to UILabel font
    setSubviewLabelsToTextColor(textColor, view: view)

    // set font to alert via KVC, otherwise it'll get overwritten
    let titleAttributed = NSMutableAttributedString(
        string: alert.title!,
        attributes: [NSFontAttributeName:UIFont.boldSystemFont(ofSize: 17)])
    alert.setValue(titleAttributed, forKey: "attributedTitle")

    let messageAttributed = NSMutableAttributedString(
        string: alert.message!,
        attributes: [NSFontAttributeName:UIFont.systemFont(ofSize: 13)])
    alert.setValue(messageAttributed, forKey: "attributedMessage")

    // set the buttons to non-blue, if we have buttons
    if let buttonColor = buttonColor {
        alert.view.tintColor = buttonColor
    }
5
Leandro Jiao

Voici une extension UIAlertController qui fonctionne aussi bien sur iPad que sur iPhone. Le bouton Annuler passera automatiquement de la couleur sombre au blanc en fonction du style de flou sélectionné:

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
    }
}

L’extension UIView suivante est également nécessaire:

extension UIView {

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

Exemple: 

let controller = UIAlertController(title: "Dark Alert Controller", message: nil, preferredStyle: .actionSheet, blurStyle: .dark)

// Setup controller actions etc...

present(controller, animated: true, completion: nil)

iPhone:

 enter image description here

iPad: 

 enter image description here

4
Mark Bourke
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) {
        // ...
    }
}
4
user2643679

Pour objectif - le code C peut être comme.

UIAlertController * alert=[UIAlertController alertControllerWithTitle:@"Title"
                                                              message:@"Message"
                                                       preferredStyle:UIAlertControllerStyleAlert];
UIView *firstSubview = alert.view.subviews.firstObject;
UIView *alertContentView = firstSubview.subviews.firstObject;
for (UIView *subSubView in alertContentView.subviews) {
    subSubView.backgroundColor = [UIColor colorWithRed:255/255.0f green:255/255.0f blue:255/255.0f alpha:1.0f];
}
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){
   //Close Action
}];
[alert addAction:cancelAction];
[self presentViewController:alert animated:YES completion:nil];
1
Yogesh Tarsariya

La meilleure décision que j'ai trouvée (sans taches blanches sur les côtés)

Lien vers la réponse originale par Vadim Akhmerov

Réponse:

Il est plus facile de sous-classer UIAlertController.

L'idée est de parcourir la hiérarchie des vues à chaque appel de viewDidLayoutSubviews, de supprimer l'effet pour UIVisualEffectView 'et de mettre à jour leur 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
    }

}

Utilisation:

  1. Créer un contrôleur d'alerte (utilisez maintenant AlertController à la place de UIAlertController)

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

  1. Définissez la couleur d'arrière-plan de la classe personnalisée "AlertController":

var buttonBackgroundColor: UIColor = .darkGray

0
Bandyliuk