在 iOS 中,如何通过点击警报弹出窗口内部来关闭警报控制器?
In iOS, how do I close an Alert Controller by tapping INSIDE the Alert popup?
有几篇文章介绍了如何通过点击 OUTSIDE 弹出窗口 window 来关闭警报控制器弹出窗口 window。但是我想通过点击 INSIDE 弹出窗口 window.
来关闭 Alert Controller 弹出窗口 window
我在 GitHub 上看到了一些类似 "Toast" 的解决方案,但我想只使用本机 iOS 代码来完成此操作。
我在 SO 周围进行了探索,并在几秒钟后获得了解除警报的代码(例如 Android Toast):
class ViewController: UIViewController {
let alertController = UIAlertController(title: "Alert", message: "SO Awesome!", preferredStyle: .alert)
@IBOutlet var alertButton: UIButton!
@IBAction func displayAlert(_ sender: UIButton) {
self.present(alertController, animated: true, completion: {
self.perform(#selector(self.dismissAlert), with: self.alertController, afterDelay: 3)
})
}
// dismiss (close) the alert popup
@objc func dismissAlert(_ alert: UIAlertController) {
alert.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
但是如果我不想等待 3 秒弹出警报关闭怎么办?我希望能够点击弹出警报将其关闭。 (同时保留超时功能)
我发现了一些接近但不完全符合我要求的东西。所以......经过一些工作,我自己回答了这个问题,并在下面包含了一个完整的答案。
我在 SO 上发现的最简单的技巧似乎是添加一个 UIControl,其动作目标可以在任何触摸事件上关闭 AlertController。但是 UIControl 需要一个框架 CGRect 才能正常工作。
以下是 UIControl 的要点:
let dismissControl = UIControl()
// make the dismissControl execute dismissAlert for all touch events
dismissControl.addTarget(self, action: #selector(self.dismissAlert), for: .allTouchEvents)
// NOTE: must use the bounds, not the frame for it to work
self.dismissControl.frame = self.alertController.view.bounds
// add the UIControl on top of the UIAlertControl view
self.alertController.view.addSubview(self.dismissControl)
我探索了 4 种不同的可能性:
- 超时 - 在数秒后关闭警报Window(如Android Toast)
- 点击警报外部 Window(点击警报 Window 不会关闭它)
- 点击屏幕上的任意位置将其关闭(在警报 Window 或警报之外)
- 点击警报 Window(点击其他任何地方都不会关闭它)
这是一个完整的 ViewController (Xcode 9.2, Swift 4) 处理所有 4 种情况:
//
// ViewController.swift
// AlertDemo - Display and dismiss UIAlertControllers with UIControl (no action buttons)
// (similar to Toast on Android OS)
//
// 4 different ways to close a UIAlertController popup window:
//
// 1) Timeout - dismiss an Alert Window after a number of seconds (like Android Toast)
// 2) Tap outside the Alert Window (tap on Alert Window DOES NOT close it)
// 3) Tap the Alert Window (tap anywhere else DOES NOT close it)
// 4) Tap anywhere on the screen (on Alert Window or outside of it) to close it
//
// Created by ByteSlinger on 2018-01-19.
// Copyright © 2018 ByteSlinger. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
let alertController = UIAlertController(title: "Alert", message: "SO Awesome!", preferredStyle: .alert)
let timeoutController = UIAlertController(title: "Timeout", message: "That Alert timed out!", preferredStyle: .actionSheet)
let dismissControl = UIControl()
@IBOutlet var alertButton1: UIButton!
@IBOutlet var alertButton2: UIButton!
@IBOutlet var alertButton3: UIButton!
@IBOutlet var alertButton4: UIButton!
// display a modal popup in the middle, wait for timeout to close
@IBAction func displayAlert1(_ sender: UIButton) {
alertController.message = "You must wait for this Alert to timeout"
self.present(alertController, animated: true, completion: {
self.perform(#selector(self.timeoutAlert), with: self.alertController, afterDelay: 3)
})
}
// display a modal popup in the middle, tap outside popup to close
@IBAction func displayAlert2(_ sender: UIButton) {
alertController.message = "Tap outside this Alert to close"
self.present(alertController, animated: true, completion: {
self.dismissControl.frame = self.alertController.view.superview?.bounds ?? CGRect.zero
self.alertController.view.superview?.insertSubview(self.dismissControl, belowSubview: self.alertController.view)
self.perform(#selector(self.timeoutAlert), with: self.alertController, afterDelay: 3)
})
}
// display a modal popup in the middle, tap on or outside popup to close
@IBAction func displayAlert3(_ sender: UIButton) {
alertController.message = "Tap anywhere to close"
self.present(alertController, animated: true, completion: {
self.dismissControl.frame = self.alertController.view.superview?.bounds ?? CGRect.zero
self.alertController.view.superview?.addSubview(self.dismissControl)
self.perform(#selector(self.timeoutAlert), with: self.alertController, afterDelay: 3)
})
}
// display a modal popup in the middle, tap on popup to close
@IBAction func displayAlert4(_ sender: UIButton) {
alertController.message = "Tap on this Alert to close"
self.present(alertController, animated: true, completion: {
// important - use bounds: alertController.view.frame WILL NOT WORK
self.dismissControl.frame = self.alertController.view.bounds
self.alertController.view.addSubview(self.dismissControl)
self.perform(#selector(self.timeoutAlert), with: self.alertController, afterDelay: 3)
})
}
// close the current alert popup (middle) and display the timeout alert (bottom)
@objc func timeoutAlert(_ alertController: UIAlertController) {
if (alertController == UIApplication.shared.keyWindow?.rootViewController?.presentedViewController) {
timeoutController.message = alertController.message!
alertController.view.willRemoveSubview(self.dismissControl)
alertController.dismiss(animated: true, completion: {
self.present(self.timeoutController,animated: true, completion: {
self.perform(#selector(self.dismissTimeout), with: self.timeoutController, afterDelay: 2)
})
})
}
}
// dimiss (close) the alert popup
@objc func dismissAlert() {
// make sure there are no timeoutAlert calls waiting
NSObject.cancelPreviousPerformRequests(withTarget: self)
alertController.view.willRemoveSubview(self.dismissControl)
alertController.dismiss(animated: true, completion: nil)
}
// dimiss (close) the timeout popup
@objc func dismissTimeout(_ alert: UIAlertController) {
alert.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// make the dismissControl execute dismissAlert for all touch events
dismissControl.addTarget(self, action: #selector(self.dismissAlert), for: .allTouchEvents)
// make the buttons a little prettier
alertButton1.layer.cornerRadius = 32
alertButton2.layer.cornerRadius = 32
alertButton3.layer.cornerRadius = 32
alertButton4.layer.cornerRadius = 32
}
}
要使用它,只需在 IB 中创建 4 个 UIButton 并将它们连接到 @IBOutlet 变量和函数。
注意:我不得不取消执行请求,否则警报有时会被之前的超时取消。 dismissAlert()
中的这一行起到了作用:
NSObject.cancelPreviousPerformRequests(withTarget: self)
这是 GitHub 上的完整 Xcode 项目:AlertDemo
感谢 @Apoc 以及他对 UIControl
的启发 post:UIAlertController handle dismiss upon click outside (IPad)。从 UIAlertController 边界设置 UIControl 框架是最终让它工作的原因。
有几篇文章介绍了如何通过点击 OUTSIDE 弹出窗口 window 来关闭警报控制器弹出窗口 window。但是我想通过点击 INSIDE 弹出窗口 window.
来关闭 Alert Controller 弹出窗口 window我在 GitHub 上看到了一些类似 "Toast" 的解决方案,但我想只使用本机 iOS 代码来完成此操作。
我在 SO 周围进行了探索,并在几秒钟后获得了解除警报的代码(例如 Android Toast):
class ViewController: UIViewController {
let alertController = UIAlertController(title: "Alert", message: "SO Awesome!", preferredStyle: .alert)
@IBOutlet var alertButton: UIButton!
@IBAction func displayAlert(_ sender: UIButton) {
self.present(alertController, animated: true, completion: {
self.perform(#selector(self.dismissAlert), with: self.alertController, afterDelay: 3)
})
}
// dismiss (close) the alert popup
@objc func dismissAlert(_ alert: UIAlertController) {
alert.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
但是如果我不想等待 3 秒弹出警报关闭怎么办?我希望能够点击弹出警报将其关闭。 (同时保留超时功能)
我发现了一些接近但不完全符合我要求的东西。所以......经过一些工作,我自己回答了这个问题,并在下面包含了一个完整的答案。
我在 SO 上发现的最简单的技巧似乎是添加一个 UIControl,其动作目标可以在任何触摸事件上关闭 AlertController。但是 UIControl 需要一个框架 CGRect 才能正常工作。
以下是 UIControl 的要点:
let dismissControl = UIControl()
// make the dismissControl execute dismissAlert for all touch events
dismissControl.addTarget(self, action: #selector(self.dismissAlert), for: .allTouchEvents)
// NOTE: must use the bounds, not the frame for it to work
self.dismissControl.frame = self.alertController.view.bounds
// add the UIControl on top of the UIAlertControl view
self.alertController.view.addSubview(self.dismissControl)
我探索了 4 种不同的可能性:
- 超时 - 在数秒后关闭警报Window(如Android Toast)
- 点击警报外部 Window(点击警报 Window 不会关闭它)
- 点击屏幕上的任意位置将其关闭(在警报 Window 或警报之外)
- 点击警报 Window(点击其他任何地方都不会关闭它)
这是一个完整的 ViewController (Xcode 9.2, Swift 4) 处理所有 4 种情况:
//
// ViewController.swift
// AlertDemo - Display and dismiss UIAlertControllers with UIControl (no action buttons)
// (similar to Toast on Android OS)
//
// 4 different ways to close a UIAlertController popup window:
//
// 1) Timeout - dismiss an Alert Window after a number of seconds (like Android Toast)
// 2) Tap outside the Alert Window (tap on Alert Window DOES NOT close it)
// 3) Tap the Alert Window (tap anywhere else DOES NOT close it)
// 4) Tap anywhere on the screen (on Alert Window or outside of it) to close it
//
// Created by ByteSlinger on 2018-01-19.
// Copyright © 2018 ByteSlinger. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
let alertController = UIAlertController(title: "Alert", message: "SO Awesome!", preferredStyle: .alert)
let timeoutController = UIAlertController(title: "Timeout", message: "That Alert timed out!", preferredStyle: .actionSheet)
let dismissControl = UIControl()
@IBOutlet var alertButton1: UIButton!
@IBOutlet var alertButton2: UIButton!
@IBOutlet var alertButton3: UIButton!
@IBOutlet var alertButton4: UIButton!
// display a modal popup in the middle, wait for timeout to close
@IBAction func displayAlert1(_ sender: UIButton) {
alertController.message = "You must wait for this Alert to timeout"
self.present(alertController, animated: true, completion: {
self.perform(#selector(self.timeoutAlert), with: self.alertController, afterDelay: 3)
})
}
// display a modal popup in the middle, tap outside popup to close
@IBAction func displayAlert2(_ sender: UIButton) {
alertController.message = "Tap outside this Alert to close"
self.present(alertController, animated: true, completion: {
self.dismissControl.frame = self.alertController.view.superview?.bounds ?? CGRect.zero
self.alertController.view.superview?.insertSubview(self.dismissControl, belowSubview: self.alertController.view)
self.perform(#selector(self.timeoutAlert), with: self.alertController, afterDelay: 3)
})
}
// display a modal popup in the middle, tap on or outside popup to close
@IBAction func displayAlert3(_ sender: UIButton) {
alertController.message = "Tap anywhere to close"
self.present(alertController, animated: true, completion: {
self.dismissControl.frame = self.alertController.view.superview?.bounds ?? CGRect.zero
self.alertController.view.superview?.addSubview(self.dismissControl)
self.perform(#selector(self.timeoutAlert), with: self.alertController, afterDelay: 3)
})
}
// display a modal popup in the middle, tap on popup to close
@IBAction func displayAlert4(_ sender: UIButton) {
alertController.message = "Tap on this Alert to close"
self.present(alertController, animated: true, completion: {
// important - use bounds: alertController.view.frame WILL NOT WORK
self.dismissControl.frame = self.alertController.view.bounds
self.alertController.view.addSubview(self.dismissControl)
self.perform(#selector(self.timeoutAlert), with: self.alertController, afterDelay: 3)
})
}
// close the current alert popup (middle) and display the timeout alert (bottom)
@objc func timeoutAlert(_ alertController: UIAlertController) {
if (alertController == UIApplication.shared.keyWindow?.rootViewController?.presentedViewController) {
timeoutController.message = alertController.message!
alertController.view.willRemoveSubview(self.dismissControl)
alertController.dismiss(animated: true, completion: {
self.present(self.timeoutController,animated: true, completion: {
self.perform(#selector(self.dismissTimeout), with: self.timeoutController, afterDelay: 2)
})
})
}
}
// dimiss (close) the alert popup
@objc func dismissAlert() {
// make sure there are no timeoutAlert calls waiting
NSObject.cancelPreviousPerformRequests(withTarget: self)
alertController.view.willRemoveSubview(self.dismissControl)
alertController.dismiss(animated: true, completion: nil)
}
// dimiss (close) the timeout popup
@objc func dismissTimeout(_ alert: UIAlertController) {
alert.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// make the dismissControl execute dismissAlert for all touch events
dismissControl.addTarget(self, action: #selector(self.dismissAlert), for: .allTouchEvents)
// make the buttons a little prettier
alertButton1.layer.cornerRadius = 32
alertButton2.layer.cornerRadius = 32
alertButton3.layer.cornerRadius = 32
alertButton4.layer.cornerRadius = 32
}
}
要使用它,只需在 IB 中创建 4 个 UIButton 并将它们连接到 @IBOutlet 变量和函数。
注意:我不得不取消执行请求,否则警报有时会被之前的超时取消。 dismissAlert()
中的这一行起到了作用:
NSObject.cancelPreviousPerformRequests(withTarget: self)
这是 GitHub 上的完整 Xcode 项目:AlertDemo
感谢 @Apoc 以及他对 UIControl
的启发 post:UIAlertController handle dismiss upon click outside (IPad)。从 UIAlertController 边界设置 UIControl 框架是最终让它工作的原因。