1

I change title font and color like this:

let titleAttributes = [NSAttributedStringKey.font: UIFont(name: "HelveticaNeue-Bold", size: 25)!, NSAttributedStringKey.foregroundColor: UIColor.purple]

alert.setValue(titleString, forKey: "attributedTitle")

Before iOS13 this worked fine both for preferredStyle .alert and .actionSheet. Now it only works for .alert and doesn't work for .actionSheet.

Someone please any help?

Ashish Kakkad
  • 23,586
  • 12
  • 103
  • 136
  • That code was never supported. You were accessing internal, private APIs. Such code is always fragile and prone to breaking in any iOS update. `UIAlertController` does not support any such customizations. The proper solution is to write (or find) a custom action sheet that does what you want. – rmaddy Sep 19 '19 at 07:22
  • @rmaddy Using KVC / KVO is not accessing a private API. It is brittle, and it's not always a good idea, but it has its place. There is always a trade-off in any decision... KVO may stop working or look wrong after an iOS update. But a 3rd party or custom action sheet may ALSO look wrong or stop working after an iOS / XCode update. I think it's a valid choice as long as it is documented in the code and part of the test surface. – Eli Burke Feb 26 '20 at 20:48

1 Answers1

0

iOS 13 now embeds the UIAlertController title in a UIVisualEffectView, and only the title's alpha channel affects its appearance. If it is critical to control the exact color, I think you could try finding the subview class _UIInterfaceActionGroupHeaderScrollView, remove its child UIVisualEffectView, and then add your own UILabel back in. But no guarantees it will work or that it won't trigger a crash. You can use the allSubviews extension below and compare each to this:

let grpHeader = NSClassFromString("_UIInterfaceActionGroupHeaderScrollView")

Or if you just need to ensure that your title is visible based on your app's color scheme I had success with the following, which should be quite safe to use at least until iOS 14 is released.

let alertController = UIAlertController(....)
for subView in UIView.allSubviews(of: alertController.view) {
    if let effectView = subView as? UIVisualEffectView {
        if effectView.effect is UIVibrancyEffect {
            if #available(iOS 13.0, *) {
                // iOS 13.1 default blur style is UIBlurEffectStyleSystemVibrantBackgroundRegular which is NOT currently defined anywhere
                // if your alert controller color is dark, set to .systemMaterialDark                                           
                // if your alert controller color is light, set to .systemMaterialLight
                effectView.effect = UIVibrancyEffect(blurEffect: UIBlurEffect(style: UIBlurEffect.Style.systemMaterialDark), style: .secondaryLabel)
            }
            break
        }
    }
}

// You will need this UIView category to get an array of all subviews,
// as the view heirarchy is complex. In iOS13 it is:
// UIAlertController.UIView -> 
//    _UIAlertControllerInterfaceActionGroupView ->
//        UIView
//            _UIInterfaceActionGroupHeaderScrollView
//                ** UIVisualEffectView **
//            _UIInterfaceActionRepresentationsSequenceView
//        _UIDimmingKnockoutBackdropView ]
//            ** all your button actions **

extension UIView {
    class func allSubviews<T : UIView>(of view: UIView) -> [T] {
        var subviews = [T]()

        for subview in view.subviews {
            subviews += allSubviews(of: subview) as [T]

            if let subview = subview as? T {
                subviews.append(subview)
            }
        }

        return subviews
    }
}
Eli Burke
  • 2,729
  • 27
  • 25