Как изменить цвет фона UIAlertController?

Из-за странного поведения UIActionSheet в iOS 8 я реализовал UIAlertController с UIAction в качестве кнопок. Я хотел бы изменить весь фон UIAlertController. Но я не могу найти какие-либо способы сделать это.

Пробовал даже с,

actionController.view.backgroundColor = [UIColor blackColor];

Но не помогли мне. Любые вклады в этом отношении будут заметны.

Заранее спасибо.

9 ответов

Вы должны углубиться в некоторые взгляды:

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

А может быть, вы хотите сохранить исходный радиус угла:

 alertContentView.layer.cornerRadius = 5;

Извините за "Swifting", но я не знаком с Objective-C. Я надеюсь, что это похоже.

Конечно, также важно изменить цвет заголовка действий. К сожалению, я не знаю, как установить цвет действий отдельно. Но вот как вы меняете все цвета текста кнопок:

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

РЕДАКТИРОВАТЬ:

Угловой радиус UIAlertController изменился, так как этот ответ был опубликован! Заменить это:

 alertContentView.layer.cornerRadius = 5;

к этому:

 actionContentView.layer.cornerRadius = 15

Может быть, вам нравится использовать эффект размытия в темном режиме. Вот очень простой способ получить это:

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

Я нашел хакерский способ сделать это. Сначала вам нужно расширение, которое позволит вам искать UIVisualEffectView внутри 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
    }
}

Важно: Вы должны вызывать эту функцию после вызова presentViewController, потому что только после загрузки контроллера представления, что визуальные эффекты вид вставляется на место. Затем вы можете изменить эффект, связанный с ним, на эффект темного размытия:

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

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

И это конечный результат:

демонстрационная картинка

Я искренне удивлен, насколько хорошо это работает! Я думаю, что это то, что Apple забыла добавить. Кроме того, я еще не прошел одобрение приложения с этим "хаком" (это не хак, потому что мы используем только публичные API), но я уверен, что проблем не будет.

Для 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

введите описание изображения здесь,

Вот UIAlertController расширение, которое работает как на iPad, так и на iPhone. Кнопка "Отмена" автоматически изменит цвет с темного на белый, в зависимости от того, какой стиль размытия выбран:

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

Следующие UIView расширение также необходимо:

extension UIView {

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

Пример:

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:

введите описание изображения здесь

Swift3

Шаг в еще один слой по сравнению с 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
    }
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) {
        // ...
    }
}

Для цели - код C может быть как.

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];

Вы можете использовать внешний прокси.

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

Кажется, это относится ко всем, кроме действия отмены, при представлении в виде листа действий.

Лучшее решение, которое я нашел (без белых пятен по бокам)

Ссылка на оригинальный ответ Vadim Akhmerov

Ответ:

Это легче подкласс UIAlertController,

Идея состоит в том, чтобы обходить иерархию представлений каждый раз viewDidLayoutSubviews вызывается, убрать эффект для UIVisualEffectViewи обновлять их 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
    }

}

Использование:

  1. Создайте контроллер предупреждений (используйте теперь AlertController вместо UIAlertController)

let testAlertController = AlertController (название: ноль, сообщение: ноль, предпочитаемый стиль: .actionSheet)

  1. Установите цвет фона для пользовательского класса "AlertController":

var buttonBackgroundColor: UIColor =.darkGray

Другие вопросы по тегам