UIAlertController 中的自定义视图

Custom View in UIAlertController

我正在尝试将自定义视图放入 UIAlertController。我 运行 遇到了自定义视图大小的一些奇怪问题。

我希望自定义视图跨越 UIAlertController 的宽度,无论它是什么。我正在使用 CGRectGetWidth(alertController.view.bounds) 来获取警报控制器的宽度。但是,这似乎是返回整个视图的宽度。使用 view.frame 没有区别。

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"My Title" message:nil preferredStyle:UIAlertControllerStyleAlert];

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(alertController.view.bounds), 50)];
view.backgroundColor = [UIColor blueColor];

[alertController.view addSubview:view];
[self presentViewController:alertController animated:YES completion:nil];

这会产生以下结果。我在尝试获取 UIAlertController 的 X、Y 宽度和高度属性时遇到了同样的问题。有谁知道如何在不使用硬编码数字的情况下将此视图定位在警报控制器的中间?

你不应该那样做。引用文档:

The UIAlertController class is intended to be used as-is and does not support subclassing.

The view hierarchy for this class is private and must not be modified.

如果您反对 Apple 的明确声明,则所有赌注都会被取消,即使您可以让它在当前 OS 版本上运行,它也可能会与任何未来版本中断。

这里有一个 alternative.It 没有将子视图添加到 UIAlertControl 的视图层次结构,而是添加到 UIWindow 的适当位置。为了跟踪 UIAlertControl 的视图框架,视图控制器使用自定义 .view getter 进行扩展,使用调用 UIViewController super class 实现的 obj-c runtime/swift 扩展.这允许避免真正视图的私有 class 依赖,并且既不子classing UIAlertControl 也不修改它的视图层次结构。

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

这是我对这个问题的解决方案:

protocol AlertPresenter {
  func showAlert()
}

extension AlertPresenter where Self: UIViewController {
  func showAlert() {
    
    guard let customView = CustomAlertView.instance else { return  }
    
    let alert = UIAlertController(title: "", message: nil, preferredStyle: .alert)
            
    let alertSize = customView.frame.size
    
    let view = UIView(frame: customView.bounds)
    view.center = CGPoint(x: alertSize.width / 2, y: alertSize.height / 2)
    view.backgroundColor = .clear
    view.addSubview(customView)
    
    alert.view.widthAnchor.constraint(equalToConstant: alertSize.width).isActive = true
    alert.view.heightAnchor.constraint(equalToConstant: alertSize.height).isActive = true

    alert.view.addSubview(view)

    present(alert, animated: true, completion: nil)
  }
}

CustomAlertView - 您的自定义视图(在我的例子中是使用 .xib 创建的)

.instance = CustomAlertView 实例

然后得到类似的东西