Сценарий: пользователь нажимает кнопку на контроллере представления. Контроллер представления является самым верхним (очевидно) в стеке навигации. Tap вызывает метод вспомогательного класса, вызываемый для другого класса. Там происходит что-то плохое, и я хочу отобразить предупреждение прямо перед тем, как управление вернется в контроллер представления.
+ (void)myUtilityMethod {
// do stuff
// something bad happened, display an alert.
}
Это было возможно с UIAlertView
(но, возможно, не совсем правильно).
В этом случае, как вы представляете UIAlertController
, прямо там myUtilityMethod
?
источник
На WWDC я зашел в одну из лабораторий и спросил у инженера Apple тот же вопрос: «Как лучше всего показывать
UIAlertController
?» И он сказал, что они часто получают этот вопрос, и мы пошутили, что у них должна была быть сессия по этому вопросу. Он сказал, что внутренне Apple создаетUIWindow
прозрачный пакетUIViewController
и затем представляетUIAlertController
его. В основном то, что в ответе Дилана Беттермана.Но я не хотел использовать подкласс,
UIAlertController
потому что это потребовало бы от меня изменения кода во всем приложении. Итак, с помощью связанного объекта я создал категорию,UIAlertController
которая предоставляетshow
метод в Objective-C.Вот соответствующий код:
Вот пример использования:
UIWindow
, Который создается будет разрушен , когдаUIAlertController
это dealloced, так как это единственный объект, сохраняяUIWindow
. Но если вы присваиваетеUIAlertController
свойству свойство или заставляете его увеличивать счет хранения, обращаясь к предупреждению в одном из блоков действий, оноUIWindow
будет оставаться на экране, блокируя ваш пользовательский интерфейс. См. Пример кода использования выше, чтобы избежать в случае необходимости доступаUITextField
.Я сделал репозиторий GitHub с тестовым проектом: FFGlobalAlertController
источник
viewDidDisappear:
на категории выглядит как плохая идея. По сути, вы конкурируете с реализацией платформыviewDidDisappear:
. Пока это может быть хорошо, но если Apple решит внедрить этот метод в будущем, у вас не будет возможности вызвать его (т. Е. Нет аналога тому,super
что указывает на первичную реализацию метода из реализации категории) ,prefersStatusBarHidden
иpreferredStatusBarStyle
без лишних подклассов?стриж
Objective-C
источник
Вы можете сделать следующее с Swift 2.2:
И Swift 3.0:
источник
Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!
.visibleViewController
в любое время получить свойство, чтобы узнать, с какого контроллера выдается предупреждение. Проверьте документыДовольно общий
UIAlertController
extension
для всех случаевUINavigationController
и / илиUITabBarController
. Также работает, если на экране есть модальный ВК.Использование:
Это расширение:
источник
UI
класс , который держит (слабый!)currentVC
ТипаUIViewController
.I имеетBaseViewController
который наследует отUIViewController
и набораUI.currentVC
дляself
наviewDidAppear
то , чтобыnil
наviewWillDisappear
. Все мои контроллеры представления в приложении наследуютсяBaseViewController
. Таким образом, если у вас есть что-тоUI.currentVC
(это неnil
...) - это определенно не в середине анимации презентации, и вы можете попросить ее представить своюUIAlertController
.else { if let presentedViewController = controller.presentedViewController { presentedViewController.presentViewController(self, animated: animated, completion: completion) } else { controller.presentViewController(self, animated: animated, completion: completion) } }
Улучшение на ответ agilityvision в , вам нужно создать окно с прозрачным контроллером зрения корневого и представить вид предупреждения оттуда.
Однако, пока у вас есть действие в контроллере оповещений, вам не нужно хранить ссылку на окно . В качестве последнего шага блока обработчика действий, вам просто нужно скрыть окно как часть задачи очистки. Имея ссылку на окно в блоке обработчика, это создает временную циклическую ссылку, которая будет разорвана, как только контроллер оповещения будет отклонен.
источник
Следующее решение не сработало, хотя оно выглядело многообещающе для всех версий. Это решение генерирует ПРЕДУПРЕЖДЕНИЕ .
Внимание! Попытайтесь представить, чей вид не находится в иерархии окон!
https://stackoverflow.com/a/34487871/2369867 => Тогда это выглядело многообещающе. Но это было не в
Swift 3
. Поэтому я отвечаю на это в Swift 3, и это не пример шаблона.Это довольно полнофункциональный код сам по себе, когда вы вставляете в любую функцию.
Это проверенный и рабочий код в Swift 3.
источник
UIWindow
окно, иначе окно будет выпущено и исчезнет вскоре после выхода из области видимости.Вот ответ мифического кодера как расширение, протестированное и работающее в Swift 4:
Пример использования:
источник
Это работает в Swift для обычных контроллеров представления, и даже если на экране есть контроллер навигации:
источник
UIWindow
оно не отвечает. Что-то связанное с,windowLevel
вероятно, Как я могу сделать это отзывчивым?alertWindow
To ,nil
когда вы закончите с ним.Добавив к ответу Зев (и переключившись обратно на Objective-C), вы можете столкнуться с ситуацией, когда ваш корневой контроллер представления представляет какой-то другой виртуальный канал через сеанс или что-то еще. Вызов представленногоViewController в корневом VC позаботится об этом:
Это устранило проблему, возникшую у меня, когда корневой VC перешел на другой VC, и вместо представления контроллера предупреждений было выдано предупреждение, подобное описанным выше:
Я не проверял это, но это также может быть необходимо, если ваш корневой VC оказывается контроллером навигации.
источник
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController?.presentViewController(controller, animated: true, completion: nil)
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x15cd4afe0>)
rootViewController.presentedViewController
если его не ноль, в противном случае, используяrootViewController
. Для полностью универсального решения, возможно, потребуется пройтись по цепочкеpresentedViewController
s, чтобы попасть вtopmost
ВКОтвет @ agilityvision переведен на Swift4 / iOS11. Я не использовал локализованные строки, но вы можете легко это изменить:
источник
window.backgroundColor = UIColor.clear
исправил это.viewController.view.backgroundColor = UIColor.clear
не кажется необходимым.UIAlertController
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.
Создайте расширение как в ответе Aviel Gross. Здесь у вас есть расширение Objective-C.
Здесь у вас есть заголовочный файл * .h
И реализация: * .m
Вы используете это расширение в вашем файле реализации следующим образом:
источник
Перекрестите мой ответ, так как эти две темы не помечены как обманщики ...
Теперь это
UIViewController
часть цепочки респондента, вы можете сделать что-то вроде этого:источник
Зев Айзенберг ответ прост и прямолинеен, но он не всегда работает, и он может потерпеть неудачу с этим предупреждением:
Это связано с тем, что rootViewController для Windows находится не в верхней части представленных представлений. Чтобы исправить это, нам нужно пройтись по цепочке презентаций, как показано в моем коде расширения UIAlertController, написанном в Swift 3:
Обновления от 15.09.2017:
Протестировано и подтверждено, что вышеуказанная логика все еще отлично работает в недавно появившемся семени iOS 11 GM. Однако метод agilityvision, получивший наибольшее количество голосов, не делает этого: представление предупреждений, представленное недавно отчеканенным,
UIWindow
находится под клавиатурой и потенциально не позволяет пользователю нажимать на его кнопки. Это связано с тем, что в iOS 11 все уровни окна выше, чем у окна клавиатуры, опускаются до уровня ниже него.Одним из артефактов представления из-за этого
keyWindow
является анимация, когда клавиатура скользит вниз при появлении предупреждения и снова сдвигается вверх при отклонении предупреждения. Если вы хотите, чтобы клавиатура оставалась там во время презентации, вы можете попытаться представить ее из самого верхнего окна, как показано в приведенном ниже коде:Единственная не столь значительная часть приведенного выше кода состоит в том, что он проверяет имя класса,
UIRemoteKeyboardWindow
чтобы убедиться, что мы тоже можем его включить. Тем не менее приведенный выше код отлично работает в iOS 9, 10 и 11 GM seed, с правильным оттенком цвета и без артефактов скольжения клавиатуры.источник
Свифт 4+
Решение я использую годами без каких-либо проблем. Прежде всего, я расширяю,
UIWindow
чтобы найти его visibleViewController. ПРИМЕЧАНИЕ : если вы используете пользовательские классы коллекции * (например, боковое меню), вы должны добавить обработчик для этого случая в следующем расширении. После того, как вы получили самый верхний вид контроллера, его легко представитьUIAlertController
какUIAlertView
.источник
Для iOS 13, опираясь на ответы мифического кодера и bobbyrehm :
В iOS 13, если вы создаете свое собственное окно для представления оповещения, вы должны иметь четкую ссылку на это окно, иначе ваше оповещение не будет отображаться, потому что окно будет немедленно освобождено, когда его ссылка выходит из области действия.
Кроме того, вам нужно будет снова установить ссылку на nil после того, как предупреждение будет отклонено, чтобы удалить окно, чтобы продолжить взаимодействие с пользователем в главном окне под ним.
Вы можете создать
UIViewController
подкласс для инкапсуляции логики управления памятью окна:Вы можете использовать это как есть, или, если вам нужен удобный метод
UIAlertController
, вы можете добавить его в расширение:источник
dismiss
отключить предупреждение вручную, обязательно вызовите WindowAlertPresentationController напрямуюalert.presentingViewController?.dismiss(animated: true, completion: nil)
Сокращенный способ сделать предупреждение в Objective-C:
Где
alertController
твойUIAlertController
объектПРИМЕЧАНИЕ. Вам также необходимо убедиться, что ваш вспомогательный класс расширяется.
UIViewController
источник
Если кому-то интересно, я создал версию @agilityvision Swift 3. Код:
источник
С этим вы можете легко представить свое предупреждение, как так
Стоит отметить, что если в данный момент отображается UIAlertController,
UIApplication.topMostViewController
он вернет aUIAlertController
. Представление сверхуUIAlertController
имеет странное поведение и его следует избегать. Таким образом, вы должны либо вручную проверить это!(UIApplication.topMostViewController is UIAlertController)
перед представлением, либо добавитьelse if
случай, чтобы вернуть ноль, еслиself is UIAlertController
источник
Вы можете отправить текущий вид или контроллер в качестве параметра:
источник
Кевин Слих предоставил отличное решение.
Теперь я использую приведенный ниже код в моем основном подклассе UIViewController.
Я сделал небольшое изменение, чтобы проверить, не является ли лучший контроллер представления простым UIViewController. Если нет, то это должен быть какой-то ВК, представляющий простой ВК. Таким образом мы возвращаем VC, который представлен вместо этого.
Кажется, все получилось в моем тестировании.
Спасибо Кевин!
источник
В дополнение к отличным ответам ( agilityvision , adib , malhal ). Чтобы достичь поведения в очереди, как в старых добрых UIAlertViews (избегайте перекрытия окон предупреждений), используйте этот блок для наблюдения за доступностью на уровне окна:
Полный пример:
Это позволит вам избежать перекрытия окон предупреждений. Тот же метод можно использовать для разделения и помещения контроллеров представления очереди для любого числа слоев окна.
источник
Я перепробовал все упомянутое, но безуспешно. Метод, который я использовал для Swift 3.0:
источник
Некоторые из этих ответов работали только частично для меня, объединение их в следующем методе класса в AppDelegate было для меня решением. Он работает на iPad, в представлениях UITabBarController, в UINavigationController, en, когда представляет модалы. Протестировано на iOS 10 и 13.
Использование:
источник
Поддержка сцены iOS13 (при использовании UIWindowScene)
источник
Вы можете попытаться реализовать категорию
UIViewController
с помощью метода, такого как- (void)presentErrorMessage;
А, и внутри этого метода вы реализуете UIAlertController, а затем представите егоself
. Чем в вашем клиентском коде у вас будет что-то вроде:[myViewController presentErrorMessage];
Таким образом вы избежите ненужных параметров и предупреждений о том, что представление не находится в иерархии окон.
источник
myViewController
в коде, где случается плохое. Это в служебном методе, который ничего не знает о контроллере вида, который его вызвал.UIAlertView
привело меня к нарушению этого правила в нескольких местах.Есть 2 подхода, которые вы можете использовать:
-Используйте
UIAlertView
или «UIActionSheet» вместо этого (не рекомендуется, потому что он устарел в iOS 8, но теперь работает)Как-то помните последний представленный контроллер представления. Вот пример.
Использование:
источник
Я использую этот код с небольшими изменениями в своем классе AppDelegate
источник
Кажется, работает:
источник
создать вспомогательный класс AlertWindow и затем использовать как
источник