보기 컨트롤러에 없을 때 UIAertController를 표시하는 방법은 무엇입니까?
시나리오:사용자가 보기 컨트롤러의 단추를 누릅니다.보기 컨트롤러는 탐색 스택에서 가장 위쪽에 있습니다(분명히).이 탭은 다른 클래스에서 호출되는 유틸리티 클래스 메서드를 호출합니다.여기서 나쁜 일이 발생하여 컨트롤이 보기 컨트롤러로 돌아가기 전에 경고를 바로 표시하려고 합니다.
+ (void)myUtilityMethod {
// do stuff
// something bad happened, display an alert.
}
은 이는로가다니습했능▁with다▁possible니▁this습으로 가능했습니다.UIAlertView
(그러나 아마도 꽤 적절하지 않을 것입니다.)
이 경우, 어떻게 다음을 제시합니까?UIAlertController
myUtilityMethod
?
중에 들러 . "WWDC를 하는 데 UIAlertController
그리고 그는 그들이 이 질문을 많이 받았다고 말했고 우리는 그들이 그것에 대해 세션을 했어야 했다고 농담을 했습니다.그는 애플이 내부적으로 다음을 만들고 있다고 말했습니다.UIWindow
한 명하로UIViewController
그리고 나서 발표합니다.UIAlertController
기본적으로 딜런 베터맨의 대답에 있는 것입니다
하지만 하위 클래스를 사용하고 싶지 않았습니다.UIAlertController
앱 전체에서 코드를 변경해야 하기 때문입니다.그래서 연관된 물체의 도움으로, 저는 카테고리를 만들었습니다.UIAlertController
합니다.show
목표-C의 방법.
다음은 관련 코드입니다.
#import "UIAlertController+Window.h"
#import <objc/runtime.h>
@interface UIAlertController (Window)
- (void)show;
- (void)show:(BOOL)animated;
@end
@interface UIAlertController (Private)
@property (nonatomic, strong) UIWindow *alertWindow;
@end
@implementation UIAlertController (Private)
@dynamic alertWindow;
- (void)setAlertWindow:(UIWindow *)alertWindow {
objc_setAssociatedObject(self, @selector(alertWindow), alertWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIWindow *)alertWindow {
return objc_getAssociatedObject(self, @selector(alertWindow));
}
@end
@implementation UIAlertController (Window)
- (void)show {
[self show:YES];
}
- (void)show:(BOOL)animated {
self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.alertWindow.rootViewController = [[UIViewController alloc] init];
id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate;
// Applications that does not load with UIMainStoryboardFile might not have a window property:
if ([delegate respondsToSelector:@selector(window)]) {
// we inherit the main window's tintColor
self.alertWindow.tintColor = delegate.window.tintColor;
}
// window level is above the top window (this makes the alert, if it's a sheet, show over the keyboard)
UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
self.alertWindow.windowLevel = topWindow.windowLevel + 1;
[self.alertWindow makeKeyAndVisible];
[self.alertWindow.rootViewController presentViewController:self animated:animated completion:nil];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// precaution to ensure window gets destroyed
self.alertWindow.hidden = YES;
self.alertWindow = nil;
}
@end
다음은 사용 예입니다.
// need local variable for TextField to prevent retain cycle of Alert otherwise UIWindow
// would not disappear after the Alert was dismissed
__block UITextField *localTextField;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Global Alert" message:@"Enter some text" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSLog(@"do something with text:%@", localTextField.text);
// do NOT use alert.textfields or otherwise reference the alert in the block. Will cause retain cycle
}]];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
localTextField = textField;
}];
[alert show];
그UIWindow
생성된 것은 다음에 파괴될 것입니다.UIAlertController
할이해었니다습을 해제됩니다.UIWindow
하지만 만약 당신이 그것을 할당한다면.UIAlertController
블록 중 정보를 하거나 해당 시킵니다.UIWindow
UI를 잠그면서 화면에 유지됩니다.하여 액스야하경방우지면위려의오샘참시에 접속해야 하는 를 피하십시오.UITextField
.
테스트 프로젝트로 GitHub repo를 만들었습니다: FFGlobalAlertController
스위프트
let alertController = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
//...
var rootViewController = UIApplication.shared.keyWindow?.rootViewController
if let navigationController = rootViewController as? UINavigationController {
rootViewController = navigationController.viewControllers.first
}
if let tabBarController = rootViewController as? UITabBarController {
rootViewController = tabBarController.selectedViewController
}
//...
rootViewController?.present(alertController, animated: true, completion: nil)
목표-C
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Title" message:@"message" preferredStyle:UIAlertControllerStyleAlert];
//...
id rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
if([rootViewController isKindOfClass:[UINavigationController class]])
{
rootViewController = ((UINavigationController *)rootViewController).viewControllers.firstObject;
}
if([rootViewController isKindOfClass:[UITabBarController class]])
{
rootViewController = ((UITabBarController *)rootViewController).selectedViewController;
}
//...
[rootViewController presentViewController:alertController animated:YES completion:nil];
Swift 2.2를 사용하여 다음을 수행할 수 있습니다.
let alertController: UIAlertController = ...
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
그리고 Swift 3.0:
let alertController: UIAlertController = ...
UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)
저는 몇 달 전에 비슷한 질문을 올렸고 마침내 문제를 해결했다고 생각합니다.코드만 보고 싶다면 제 게시물 하단에 있는 링크를 따라오세요.
솔루션은 추가 UI 창을 사용하는 것입니다.
UIAlertController를 표시하려는 경우:
- 창을 키로 만들고 볼 수 있는 창으로 만듭니다.
window.makeKeyAndVisible()
) - UIViewController는 rootViewController입니다 (()
window.rootViewController = UIViewController()
) - 창의 rootViewController에 UIAertController 표시
주의해야 할 몇 가지 사항:
- UI 창을 강력하게 참조해야 합니다.강하게 참조되지 않으면 릴리스되었기 때문에 표시되지 않습니다.속성을 사용하는 것이 좋지만 연결된 개체도 사용할 수 있습니다.
- 이 다른 항목( UIAertControllers) 하기 했습니다. (으)로.
window.windowLevel = UIWindowLevelAlert + 1
)
마지막으로, 여러분이 그것을 보고 싶다면 저는 완성된 구현을 가지고 있습니다.
https://github.com/dbettermann/DBAlertController
일반적인 꽤적인반일.UIAlertController
extension
UINavigationController
및/는UITabBarController
현재 화면에 모달 VC가 있는 경우에도 작동합니다.
용도:
//option 1:
myAlertController.show()
//option 2:
myAlertController.present(animated: true) {
//completion code...
}
다음은 내선 번호입니다.
//Uses Swift1.2 syntax with the new if-let
// so it won't compile on a lower version.
extension UIAlertController {
func show() {
present(animated: true, completion: nil)
}
func present(#animated: Bool, completion: (() -> Void)?) {
if let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController {
presentFromController(rootVC, animated: animated, completion: completion)
}
}
private func presentFromController(controller: UIViewController, animated: Bool, completion: (() -> Void)?) {
if let navVC = controller as? UINavigationController,
let visibleVC = navVC.visibleViewController {
presentFromController(visibleVC, animated: animated, completion: completion)
} else {
if let tabVC = controller as? UITabBarController,
let selectedVC = tabVC.selectedViewController {
presentFromController(selectedVC, animated: animated, completion: completion)
} else {
controller.presentViewController(self, animated: animated, completion: completion)
}
}
}
}
민첩성 비전의 답변을 개선하려면 투명 루트 뷰 컨트롤러가 있는 창을 만들고 거기서 경고 뷰를 표시해야 합니다.
그러나 알림 컨트롤러에 작업이 있는 한 창에 대한 참조를 유지할 필요는 없습니다.작업 처리기 블록의 마지막 단계로 정리 작업의 일부로 창을 숨기기만 하면 됩니다.처리기 블록의 창에 대한 참조가 있으면 경고 컨트롤러가 해제되면 중단되는 임시 순환 참조가 생성됩니다.
UIWindow* window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.rootViewController = [UIViewController new];
window.windowLevel = UIWindowLevelAlert + 1;
UIAlertController* alertCtrl = [UIAlertController alertControllerWithTitle:... message:... preferredStyle:UIAlertControllerStyleAlert];
[alertCtrl addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK",@"Generic confirm") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
... // do your stuff
// very important to hide the window afterwards.
// this also keeps a reference to the window until the action is invoked.
window.hidden = YES;
}]];
[window makeKeyAndVisible];
[window.rootViewController presentViewController:alertCtrl animated:YES completion:nil];
다음 솔루션은 모든 버전에서 매우 유망해 보였지만 작동하지 않았습니다.이 솔루션은 경고를 생성하는 중입니다.
경고:창 계층 구조에 없는 보기를 표시하려고 합니다!
https://stackoverflow.com/a/34487871/2369867 => 그렇다면 이것은 유망해 보입니다.하지만 그것은 없었습니다.Swift 3
그래서 Swift 3에서 답변을 드리는데 이것은 템플릿 예시가 아닙니다.
이 코드는 함수 내부에 붙여넣으면 그 자체로 완전히 기능하는 코드입니다.
ㅠㅠ
Swift 3
독립된 코드
let alertController = UIAlertController(title: "<your title>", message: "<your message>", preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.cancel, handler: nil))
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alertController, animated: true, completion: nil)
이것은 Swift 3에서 테스트되고 작동하는 코드입니다.
다음은 Swift 4에서 테스트되고 작동하는 확장자로서의 신화적인 코덱의 답변입니다.
extension UIAlertController {
func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) {
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(self, animated: animated, completion: completion)
}
}
사용 예:
let alertController = UIAlertController(title: "<Alert Title>", message: "<Alert Message>", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
alertController.presentInOwnWindow(animated: true, completion: {
print("completed")
})
이 기능은 화면에 탐색 컨트롤러가 있는 경우에도 일반 보기 컨트롤러의 경우 Swift에서 작동합니다.
let alert = UIAlertController(...)
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.presentViewController(alert, animated: true, completion: nil)
Zev의 답변에 더해(그리고 다시 목표-C로 전환) 루트 뷰 컨트롤러가 세그 등을 통해 다른 VC를 표시하는 상황에 직면할 수 있습니다.루트 VC에서 presentedViewController를 호출하면 다음 작업이 처리됩니다.
[[UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController presentViewController:alertController animated:YES completion:^{}];
이를 통해 루트 VC가 다른 VC에 segue한 문제가 해결되었으며 경고 컨트롤러를 제시하는 대신 위에서 보고된 것과 같은 경고가 발생했습니다.
Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!
테스트하지 않았지만 루트 VC가 탐색 컨트롤러인 경우에도 필요할 수 있습니다.
스위프트 5
메시지를 표시한 후 창을 숨기는 것이 중요합니다.
func showErrorMessage(_ message: String) {
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
let alertController = UIAlertController(title: "Error", message: message, preferredStyle: UIAlertController.Style.alert)
alertController.addAction(UIAlertAction(title: "Close", style: UIAlertAction.Style.cancel, handler: { _ in
alertWindow.isHidden = true
}))
alertWindow.windowLevel = UIWindow.Level.alert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alertController, animated: true, completion: nil)
}
@Swift4/iOS11로 번역된 AgilityVision의 답변입니다.현지화된 문자열을 사용해 본 적은 없지만 쉽게 변경할 수 있습니다.
import UIKit
/** An alert controller that can be called without a view controller.
Creates a blank view controller and presents itself over that
**/
class AlertPlusViewController: UIAlertController {
private var alertWindow: UIWindow?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.alertWindow?.isHidden = true
alertWindow = nil
}
func show() {
self.showAnimated(animated: true)
}
func showAnimated(animated _: Bool) {
let blankViewController = UIViewController()
blankViewController.view.backgroundColor = UIColor.clear
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = blankViewController
window.backgroundColor = UIColor.clear
window.windowLevel = UIWindowLevelAlert + 1
window.makeKeyAndVisible()
self.alertWindow = window
blankViewController.present(self, animated: true, completion: nil)
}
func presentOkayAlertWithTitle(title: String?, message: String?) {
let alertController = AlertPlusViewController(title: title, message: message, preferredStyle: .alert)
let okayAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
alertController.addAction(okayAction)
alertController.show()
}
func presentOkayAlertWithError(error: NSError?) {
let title = "Error"
let message = error?.localizedDescription
presentOkayAlertWithTitle(title: title, message: message)
}
}
iOS 13의 경우, 신화적인 코덱과 바비림의 답을 기반으로 합니다.
iOS 13에서는 경고를 표시할 창을 직접 만드는 경우 해당 창에 대한 강력한 참조가 필요합니다. 그렇지 않으면 창 참조가 범위를 종료할 때 창이 즉시 할당 해제되므로 경고가 표시되지 않습니다.
또한 창 아래의 기본 창에서 사용자 상호 작용을 계속 허용하려면 경고가 해제된 후 참조를 다시 0으로 설정해야 합니다.
다음을 생성할 수 있습니다.UIViewController
윈도우 메모리 관리 로직을 캡슐화하는 하위 클래스:
class WindowAlertPresentationController: UIViewController {
// MARK: - Properties
private lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
private let alert: UIAlertController
// MARK: - Initialization
init(alert: UIAlertController) {
self.alert = alert
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("This initializer is not supported")
}
// MARK: - Presentation
func present(animated: Bool, completion: (() -> Void)?) {
window?.rootViewController = self
window?.windowLevel = UIWindow.Level.alert + 1
window?.makeKeyAndVisible()
present(alert, animated: animated, completion: completion)
}
// MARK: - Overrides
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
super.dismiss(animated: flag) {
self.window = nil
completion?()
}
}
}
이 기능을 그대로 사용할 수도 있고, 사용자의 컴퓨터에서 편리한 방법을 사용할 수도 있습니다.UIAlertController
확장으로 던질 수 있습니다.
extension UIAlertController {
func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) {
let windowAlertPresentationController = WindowAlertPresentationController(alert: self)
windowAlertPresentationController.present(animated: animated, completion: completion)
}
}
Aviel Gross 답변과 같이 Extension을 만듭니다.여기 목표-C 내선번호가 있습니다.
여기 헤더 파일 *.h가 있습니다.
// UIAlertController+Showable.h
#import <UIKit/UIKit.h>
@interface UIAlertController (Showable)
- (void)show;
- (void)presentAnimated:(BOOL)animated
completion:(void (^)(void))completion;
- (void)presentFromController:(UIViewController *)viewController
animated:(BOOL)animated
completion:(void (^)(void))completion;
@end
구현: *.m
// UIAlertController+Showable.m
#import "UIAlertController+Showable.h"
@implementation UIAlertController (Showable)
- (void)show
{
[self presentAnimated:YES completion:nil];
}
- (void)presentAnimated:(BOOL)animated
completion:(void (^)(void))completion
{
UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
if (rootVC != nil) {
[self presentFromController:rootVC animated:animated completion:completion];
}
}
- (void)presentFromController:(UIViewController *)viewController
animated:(BOOL)animated
completion:(void (^)(void))completion
{
if ([viewController isKindOfClass:[UINavigationController class]]) {
UIViewController *visibleVC = ((UINavigationController *)viewController).visibleViewController;
[self presentFromController:visibleVC animated:animated completion:completion];
} else if ([viewController isKindOfClass:[UITabBarController class]]) {
UIViewController *selectedVC = ((UITabBarController *)viewController).selectedViewController;
[self presentFromController:selectedVC animated:animated completion:completion];
} else {
[viewController presentViewController:self animated:animated completion:completion];
}
}
@end
구현 파일에서 이 확장명을 다음과 같이 사용하고 있습니다.
#import "UIAlertController+Showable.h"
UIAlertController* alert = [UIAlertController
alertControllerWithTitle:@"Title here"
message:@"Detail message here"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction
actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
// Add more actions if needed
[alert show];
스위프트 4+
몇 년 동안 문제 없이 사용하는 솔루션입니다. 저는 우선확니다합장다니를 연장합니다.UIWindow
표시되는 ViewController를 찾습니다.참고: 사용자 정의 컬렉션* 클래스(예: 사이드 메뉴)를 사용하는 경우 다음 확장에 이 경우의 핸들러를 추가해야 합니다.가장 보기 좋은 컨트롤러를 얻은 후에는 쉽게 표시할 수 있습니다.UIAlertController
은같과 UIAlertView
.
extension UIAlertController {
func show(animated: Bool = true, completion: (() -> Void)? = nil) {
if let visibleViewController = UIApplication.shared.keyWindow?.visibleViewController {
visibleViewController.present(self, animated: animated, completion: completion)
}
}
}
extension UIWindow {
var visibleViewController: UIViewController? {
guard let rootViewController = rootViewController else {
return nil
}
return visibleViewController(for: rootViewController)
}
private func visibleViewController(for controller: UIViewController) -> UIViewController {
var nextOnStackViewController: UIViewController? = nil
if let presented = controller.presentedViewController {
nextOnStackViewController = presented
} else if let navigationController = controller as? UINavigationController,
let visible = navigationController.visibleViewController {
nextOnStackViewController = visible
} else if let tabBarController = controller as? UITabBarController,
let visible = (tabBarController.selectedViewController ??
tabBarController.presentedViewController) {
nextOnStackViewController = visible
}
if let nextOnStackViewController = nextOnStackViewController {
return visibleViewController(for: nextOnStackViewController)
} else {
return controller
}
}
}
이 두 스레드는 Dupes로 플래그가 지정되지 않았으므로 내 답변을 교차 게시합니다...
그 제는이▁now.UIViewController
응답기 체인의 일부인 경우 다음과 같은 작업을 수행할 수 있습니다.
if let vc = self.nextResponder()?.targetForAction(#selector(UIViewController.presentViewController(_:animated:completion:)), withSender: self) as? UIViewController {
let alert = UIAlertController(title: "A snappy title", message: "Something bad happened", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
vc.presentViewController(alert, animated: true, completion: nil)
}
Zev Eisenberg의 대답은 간단하고 간단하지만 항상 효과가 있는 것은 아니며 다음과 같은 경고 메시지와 함께 실패할 수 있습니다.
Warning: Attempt to present <UIAlertController: 0x7fe6fd951e10>
on <ThisViewController: 0x7fe6fb409480> which is already presenting
<AnotherViewController: 0x7fe6fd109c00>
이는 창 rootViewController가 표시된 보기의 맨 위에 있지 않기 때문입니다.이 문제를 해결하려면 Swift 3으로 작성된 UIAertController 확장 코드에 표시된 대로 프레젠테이션 체인을 위로 이동해야 합니다.
/// 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)
}
2017년 9월 15일 업데이트:
새로 출시된 iOS 11 GM 시드에서 위의 로직이 여전히 잘 작동하는지 테스트하고 확인했습니다.그러나 민첩성 비전에 의해 투표된 상위 방법은 그렇지 않습니다: 새로 만들어진 경고 보기UIWindow
키보드 아래에 있으므로 사용자가 단추를 누를 수 없습니다.의 모든 window 11에서는 이 그 입니다.키보드 창의 수준보다 높은 수준은 그 아래 수준으로 낮아집니다.
프레젠테이션의 한 가지 아티팩트keyWindow
경고가 표시되면 키보드가 아래로 미끄러지고 경고가 해제되면 다시 위로 미끄러지는 애니메이션입니다.에 머무르기를 , 맨할 수 .
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
}
}
}
위의 코드에서 유일하게 그다지 좋지 않은 부분은 클래스 이름을 확인한다는 것입니다.UIRemoteKeyboardWindow
우리가 그것도 포함시킬 수 있는지 확인하기 위해.그럼에도 불구하고 위의 코드는 iOS 9, 10, 11 GM 시드에서 적절한 색조와 키보드 슬라이딩 아티팩트 없이 잘 작동합니다.
이러한 답변 중 일부는 저에게 부분적으로만 효과가 있었고, AppDelegate에서 다음과 같은 클래스 방법으로 결합하는 것이 저에게 해결책이었습니다.모델을 제시할 때 iPad, UI 탭 바 컨트롤러 보기, UI 내비게이션 컨트롤러에서 작동합니다.iOS 10 및 13에서 테스트되었습니다.
+ (UIViewController *)rootViewController {
UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
if([rootViewController isKindOfClass:[UINavigationController class]])
rootViewController = ((UINavigationController *)rootViewController).viewControllers.firstObject;
if([rootViewController isKindOfClass:[UITabBarController class]])
rootViewController = ((UITabBarController *)rootViewController).selectedViewController;
if (rootViewController.presentedViewController != nil)
rootViewController = rootViewController.presentedViewController;
return rootViewController;
}
용도:
[[AppDelegate rootViewController] presentViewController ...
간단한 방법은 목표-C에서 경고를 표시합니다.
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alertController animated:YES completion:nil];
에▁where디alertController
의 신의입니다.UIAlertController
물건.
가 확장되었는지 .UIViewController
관심 있는 사람이 있다면 스위프트 3 버전의 @agilityvision 답변을 만들었습니다.코드:
import Foundation
import UIKit
extension UIAlertController {
var window: UIWindow? {
get {
return objc_getAssociatedObject(self, "window") as? UIWindow
}
set {
objc_setAssociatedObject(self, "window", newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.window?.isHidden = true
self.window = nil
}
func show(animated: Bool = true) {
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = UIViewController(nibName: nil, bundle: nil)
let delegate = UIApplication.shared.delegate
if delegate?.window != nil {
window.tintColor = delegate!.window!!.tintColor
}
window.windowLevel = UIApplication.shared.windows.last!.windowLevel + 1
window.makeKeyAndVisible()
window.rootViewController!.present(self, animated: animated, completion: nil)
self.window = window
}
}
iOS13 장면 지원(UI WindowsScene 사용 시)
import UIKit
private var windows: [String:UIWindow] = [:]
extension UIWindowScene {
static var focused: UIWindowScene? {
return UIApplication.shared.connectedScenes
.first { $0.activationState == .foregroundActive && $0 is UIWindowScene } as? UIWindowScene
}
}
class StyledAlertController: UIAlertController {
var wid: String?
func present(animated: Bool, completion: (() -> Void)?) {
//let window = UIWindow(frame: UIScreen.main.bounds)
guard let window = UIWindowScene.focused.map(UIWindow.init(windowScene:)) else {
return
}
window.rootViewController = UIViewController()
window.windowLevel = .alert + 1
window.makeKeyAndVisible()
window.rootViewController!.present(self, animated: animated, completion: completion)
wid = UUID().uuidString
windows[wid!] = window
}
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if let wid = wid {
windows[wid] = nil
}
}
}
새로운 UI Window 접근 방식을 깨는 iOS 13 Scene과 함께 작동하도록 업데이트되었습니다.스위프트 5.1.
fileprivate var alertWindows = [UIAlertController:UIWindow]()
extension UIAlertController {
func presentInNewWindow(animated: Bool, completion: (() -> Void)?) {
let foregroundActiveScene = UIApplication.shared.connectedScenes.filter { $0.activationState == .foregroundActive }.first
guard let foregroundWindowScene = foregroundActiveScene as? UIWindowScene else { return }
let window = UIWindow(windowScene: foregroundWindowScene)
alertWindows[self] = window
window.rootViewController = UIViewController()
window.windowLevel = .alert + 1
window.makeKeyAndVisible()
window.rootViewController!.present( self, animated: animated, completion: completion)
}
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
alertWindows[self] = nil
}
}
Kevin Sliech는 훌륭한 해결책을 제공했습니다.
저는 이제 메인 UIViewController 하위 클래스에서 아래 코드를 사용합니다.
제가 한 작은 변화는 최고의 프레젠테이션 컨트롤러가 일반 UIView 컨트롤러가 아닌지 확인하는 것이었습니다.그렇지 않다면 일반 VC를 제공하는 VC여야 합니다.따라서 우리는 대신 제시되는 VC를 반환합니다.
- (UIViewController *)bestPresentationController
{
UIViewController *bestPresentationController = [UIApplication sharedApplication].keyWindow.rootViewController;
if (![bestPresentationController isMemberOfClass:[UIViewController class]])
{
bestPresentationController = bestPresentationController.presentedViewController;
}
return bestPresentationController;
}
제 테스트에서는 지금까지 모든 것이 해결된 것 같습니다.
감사합니다, 케빈!
extension UIApplication {
/// The top most view controller
static var topMostViewController: UIViewController? {
return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
}
}
extension UIViewController {
/// The visible view controller from a given view controller
var visibleViewController: UIViewController? {
if let navigationController = self as? UINavigationController {
return navigationController.topViewController?.visibleViewController
} else if let tabBarController = self as? UITabBarController {
return tabBarController.selectedViewController?.visibleViewController
} else if let presentedViewController = presentedViewController {
return presentedViewController.visibleViewController
} else {
return self
}
}
}
이것으로 당신은 그렇게 쉽게 당신의 경고를 제시할 수 있습니다.
UIApplication.topMostViewController?.present(viewController, animated: true, completion: nil)
가지 UIAertController가 에는 UIAertController가 표시되어 있는 것입니다.UIApplication.topMostViewController
를 합니다.UIAlertController
위에표 위에 UIAlertController
이상한 행동을 하고 있으므로 피해야 합니다.때문에 을 해야 .!(UIApplication.topMostViewController is UIAlertController)
발기전에를 제시하기 전에 하거나, 추기전에하가또는을 else if
다음 경우에는 영이 반환됩니다.self is UIAlertController
extension UIViewController {
/// The visible view controller from a given view controller
var visibleViewController: UIViewController? {
if let navigationController = self as? UINavigationController {
return navigationController.topViewController?.visibleViewController
} else if let tabBarController = self as? UITabBarController {
return tabBarController.selectedViewController?.visibleViewController
} else if let presentedViewController = presentedViewController {
return presentedViewController.visibleViewController
} else if self is UIAlertController {
return nil
} else {
return self
}
}
}
현재 뷰 또는 컨트롤러를 매개 변수로 전송할 수 있습니다.
+ (void)myUtilityMethod:(id)controller {
// do stuff
// something bad happened, display an alert.
}
훌륭한 답변(어질리티 비전, 아디브, 말할)을 제공합니다.오래된 UIAertView와 같은 대기열 동작(경고 창 중복 방지)에 도달하려면 이 블록을 사용하여 창 수준의 가용성을 관찰합니다.
@interface UIWindow (WLWindowLevel)
+ (void)notifyWindowLevelIsAvailable:(UIWindowLevel)level withBlock:(void (^)())block;
@end
@implementation UIWindow (WLWindowLevel)
+ (void)notifyWindowLevelIsAvailable:(UIWindowLevel)level withBlock:(void (^)())block {
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
if (keyWindow.windowLevel == level) {
// window level is occupied, listen for windows to hide
id observer;
observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIWindowDidBecomeHiddenNotification object:keyWindow queue:nil usingBlock:^(NSNotification *note) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
[self notifyWindowLevelIsAvailable:level withBlock:block]; // recursive retry
}];
} else {
block(); // window level is available
}
}
@end
전체 예:
[UIWindow notifyWindowLevelIsAvailable:UIWindowLevelAlert withBlock:^{
UIWindow *alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
alertWindow.windowLevel = UIWindowLevelAlert;
alertWindow.rootViewController = [UIViewController new];
[alertWindow makeKeyAndVisible];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:nil preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
alertWindow.hidden = YES;
}]];
[alertWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}];
이렇게 하면 알림 창이 겹치지 않도록 할 수 있습니다.동일한 방법을 사용하여 임의의 수의 창 계층에 대해 대기열 뷰 컨트롤러를 분리하여 배치할 수 있습니다.
저는 언급된 모든 것을 시도했지만 성공하지 못했습니다.Swift 3.0에 사용한 방법:
extension UIAlertController {
func show() {
present(animated: true, completion: nil)
}
func present(animated: Bool, completion: (() -> Void)?) {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
topController.present(self, animated: animated, completion: completion)
}
}
}
효과가 있는 것 같습니다.
static UIViewController *viewControllerForView(UIView *view) {
UIResponder *responder = view;
do {
responder = [responder nextResponder];
}
while (responder && ![responder isKindOfClass:[UIViewController class]]);
return (UIViewController *)responder;
}
-(void)showActionSheet {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
[alertController addAction:[UIAlertAction actionWithTitle:@"Do it" style:UIAlertActionStyleDefault handler:nil]];
[viewControllerForView(self) presentViewController:alertController animated:YES completion:nil];
}
다른 옵션:
var topController:UIViewController = UIApplication.shared.keyWindow!.rootViewController!
while ((topController.presentedViewController) != nil) {
topController = topController.presentedViewController!
}
topController.present(alert, animated:true, completion:nil)
저는 이것이 iOS를 위한 것이라는 것을 알고 있고 검색 엔진의 거의 모든 링크가 iOS 게시물을 찾기 때문에 저는 이것을 macOS 개발자들에게 제공할 것이라고 생각했습니다.
MacOS에서 Swift 5.5 지원
저는 Darkngs의 대답을 바탕으로 다른 수업에서 제 방법 중 하나에 이것을 추가했습니다.
let alert = NSAlert()
let viewController = NSApplication.shared.keyWindow?.contentViewController
alert.messageText = "An Alert Message."
alert.addButton(withTitle: "Ok")
alert.beginSheetModal(for: (viewController?.view.window)!) {
(returnCode: NSApplication.ModalResponse) -> Void in
}
언급URL : https://stackoverflow.com/questions/26554894/how-to-present-uialertcontroller-when-not-in-a-view-controller
'programing' 카테고리의 다른 글
레일 3: 랜덤 레코드 가져오기 (0) | 2023.05.31 |
---|---|
프로젝트를 열 수 없습니다...프로젝트 파일을 구문 분석할 수 없으므로 열 수 없습니다. (0) | 2023.05.31 |
왜 수학을 합니까?라운드(2.5)는 3 대신 2를 반환합니까? (0) | 2023.05.26 |
대용량 파일(80GB)을 수집하여 속도를 높이는 방법은 없습니까? (0) | 2023.05.26 |
XSLT에 스플릿() 기능이 있습니까? (0) | 2023.05.26 |