Here's an alternative.It's not adding subview to UIAlertControl
's view hierarchy, but to UIWindow
instead in appropriate position. To track UIAlertControl
's view frame the view controller is extended with a custom .view getter using obj-c runtime/swift extension which calls UIViewController
super class implementation. This allows avoiding genuine view's private class dependence and is neither subclassing UIAlertControl
or modifying it's view hierarchy.
Objective-C
#import <objc/runtime.h>
#import <objc/message.h>
@implementation AppDelegate
+ (UIView*)alertHelperView
{
static UIView *alertHelperView = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
alertHelperView = [UIView new];
alertHelperView.backgroundColor = [UIColor redColor];
alertHelperView.frame = CGRectZero;
});
return alertHelperView;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self.window addSubview:[AppDelegate alertHelperView]];
return YES;
}
@end
@implementation ViewController
- (void)viewDidAppear:(BOOL)animated {
Class class = [UIAlertController class];
class_addMethod(class, @selector(view), imp_implementationWithBlock(^(__unsafe_unretained UIAlertController* self) {
struct objc_super super = {
.receiver = self,
.super_class = class_getSuperclass(class)
};
id (*objc_msgSendSuper_typed)(struct objc_super *, SEL) = (void *)&objc_msgSendSuper;
UIView* myView = objc_msgSendSuper_typed(&super, @selector(view));
CGRect newFrame = myView.frame;
if (!self.isBeingPresented) {
[AppDelegate alertHelperView].frame = CGRectZero;
} else {
[[AppDelegate alertHelperView].superview bringSubviewToFront:[AppDelegate alertHelperView]];
[AppDelegate alertHelperView].frame = CGRectMake(newFrame.origin.x,
newFrame.origin.y,
newFrame.size.width/2,
newFrame.size.height/2);
}
return myView;
}), "@@:");
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:@"Info"
message:@"You are using UIAlertController"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction
actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
}];
UIAlertAction* cancel = [UIAlertAction
actionWithTitle:@"Cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
}];
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
}
@end
Swift 3.1
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
static var alertHelperView : UIView!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
AppDelegate.alertHelperView = UIView()
AppDelegate.alertHelperView.backgroundColor = .red
self.window?.addSubview(AppDelegate.alertHelperView!)
return true
}
}
extension UIAlertController {
open override var view: UIView! {
get {
let newFrame : CGRect = super.view.frame
if !self.isBeingPresented {
AppDelegate.alertHelperView.frame = CGRect.zero
} else {
AppDelegate.alertHelperView.superview?.bringSubview(toFront: AppDelegate.alertHelperView)
AppDelegate.alertHelperView.frame = CGRect(x:newFrame.origin.x,y:newFrame.origin.y,width:newFrame.size.width/2,height:newFrame.size.height/2)
}
return super.view
}
set(newValue) {
super.view = newValue
}
}
}
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
let alertController = UIAlertController(title: "Default Style", message: "A standard alert.", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { action in
// ...
}
alertController.addAction(cancelAction)
let OKAction = UIAlertAction(title: "OK", style: .default) { action in
// ...
}
alertController.addAction(OKAction)
self.present(alertController, animated: false) {
}
}
}
}
UIPresentationController
you can easily replicateUIAlertController
's behaviour and provide whatever views/controllers you'd like. – Magness