Sample project -> Contains latest code
Step one:
Find a subview that has the right frame!
This little function will return the frame and address of all nested subviews.
We are looking for a subview that has a height that is smaller than that of the UIAlertController
and has an Origin
of 0,0
.
Height of my alertController was 128
func subViewStack(view:UIView, levels: [Int]) {
var currentLevels = levels
currentLevels.append(0)
for i in 0..<view.subviews.count {
currentLevels[currentLevels.count - 1] = i
let subView = view.subviews[i]
print(subView.frame, "depth:", currentLevels)
subViewStack(subView, levels: currentLevels)
}
}
This prints stuff like this:
(0.0, 0.0, 270.0, 128.0) depth: [0, 0]
//frame - firstsubview - firstsubview
This might be a good candidate:
(0.0, 0.0, 270.0, 84.0) depth: [0, 1, 0, 0]
//frame - firstsubview - secondsubiew - firstsubview - firstsubview
This translates to this:
print(alertController.view.subviews[0].subviews[1].subviews[0].subviews[0].frame)
// prints (0.0, 0.0, 270.0, 84.0) -> Bingo!
Step two:
Color that subview!
alertController.view.subviews[0].subviews[1].subviews[0].subviews[0].backgroundColor = UIColor.redColor()
If you set the colour before presenting the alertController it will crash.
Setting it in the completionHandler works.
presentViewController(alertController, animated: true) { () -> Void in
self.subViewStack(alertController.view, levels: [])
print(alertController.view.subviews[0].subviews[1].subviews[0].subviews[0].frame)
alertController.view.subviews[0].subviews[1].subviews[0].subviews[0].backgroundColor = UIColor.redColor()
}
Step three:
Make it safe!
If they ever change anything to the stack this will prevent your app from crashing. Your view might not be coloured but a crash is worse.
Extend UIView to be able to fetch subviews with one of the addresses we printed earlier. Use guard
or if let
to prevent accessing non-existent subviews.
extension UIView {
// use the printed addresses to access the views. This is safe even if the UIView changes in an update.
func getSubView(withAddress address:[Int]) -> UIView? {
var currentView : UIView = self
for index in address {
guard currentView.subviews.count > index else {
return nil
}
currentView = currentView.subviews[index]
}
return currentView
}
}
Step four:
Since it is not allowed to subclass UIAlertController
we might try to extend it. This won't give you access to viewDidAppear
so you can't display the UIAlertController
animated.
protocol CustomAlert {
var view : UIView! { get }
}
extension CustomAlert {
func titleBackgroundColor(color:UIColor) {
if view == nil {
return
}
if let titleBackground = view.getSubView(withAddress: [0, 1, 0, 0]) {
titleBackground.backgroundColor = color
}
}
}
extension UIAlertController : CustomAlert {
}
This gives you both the method to find any subviews you might want to alter and a way to add a custom function for it to that class. Without subclassing.
Call titleBackgroundColor
in the completionHandler of presentViewController
to set the color.