Câu trả lời của Zev Eisenberg rất đơn giản và dễ hiểu, nhưng không phải lúc nào nó cũng hoạt động và nó có thể thất bại với thông điệp cảnh báo này:
Warning: Attempt to present <UIAlertController: 0x7fe6fd951e10>
on <ThisViewController: 0x7fe6fb409480> which is already presenting
<AnotherViewController: 0x7fe6fd109c00>
Điều này là do các cửa sổ rootViewControll không nằm ở đầu các khung nhìn được trình bày. Để sửa lỗi này, chúng ta cần đi lên chuỗi trình bày, như được hiển thị trong mã mở rộng UIAlertControll của tôi được viết bằng Swift 3:
/// show the alert in a view controller if specified; otherwise show from window's root pree
func show(inViewController: UIViewController?) {
if let vc = inViewController {
vc.present(self, animated: true, completion: nil)
} else {
// find the root, then walk up the chain
var viewController = UIApplication.shared.keyWindow?.rootViewController
var presentedVC = viewController?.presentedViewController
while presentedVC != nil {
viewController = presentedVC
presentedVC = viewController?.presentedViewController
}
// now we present
viewController?.present(self, animated: true, completion: nil)
}
}
func show() {
show(inViewController: nil)
}
Cập nhật ngày 15/9/2017:
Đã thử nghiệm và xác nhận rằng logic trên vẫn hoạt động tuyệt vời trong hạt giống iOS 11 GM mới có. Tuy nhiên, phương pháp được bình chọn hàng đầu bởi agilityvision không: chế độ xem cảnh báo được trình bày trong một bản mới được đúcUIWindow
được đặt bên dưới bàn phím và có khả năng ngăn người dùng chạm vào các nút của nó. Điều này là do trong iOS 11, tất cả các cửa sổ Độ cao hơn so với cửa sổ bàn phím được hạ xuống một mức dưới nó.
Một yếu tố của việc trình bày từ đó keyWindow
là hình ảnh động của bàn phím trượt xuống khi cảnh báo được trình bày và trượt lên một lần nữa khi cảnh báo bị loại bỏ. Nếu bạn muốn bàn phím ở đó trong khi trình bày, bạn có thể thử trình bày từ chính cửa sổ trên cùng, như được hiển thị trong đoạn mã dưới đây:
func show(inViewController: UIViewController?) {
if let vc = inViewController {
vc.present(self, animated: true, completion: nil)
} else {
// get a "solid" window with the highest level
let alertWindow = UIApplication.shared.windows.filter { $0.tintColor != nil || $0.className() == "UIRemoteKeyboardWindow" }.sorted(by: { (w1, w2) -> Bool in
return w1.windowLevel < w2.windowLevel
}).last
// save the top window's tint color
let savedTintColor = alertWindow?.tintColor
alertWindow?.tintColor = UIApplication.shared.keyWindow?.tintColor
// walk up the presentation tree
var viewController = alertWindow?.rootViewController
while viewController?.presentedViewController != nil {
viewController = viewController?.presentedViewController
}
viewController?.present(self, animated: true, completion: nil)
// restore the top window's tint color
if let tintColor = savedTintColor {
alertWindow?.tintColor = tintColor
}
}
}
Phần duy nhất không quá lớn của đoạn mã trên là nó kiểm tra tên lớp UIRemoteKeyboardWindow
để đảm bảo chúng ta cũng có thể bao gồm nó. Tuy nhiên, đoạn mã trên hoạt động rất tốt trong hạt giống GM 9, 10 và 11 GM, với màu sắc phù hợp và không có các tạo tác trượt bàn phím.