在弹出视图之外的任何地方点击时关闭弹出视图,包括按钮、文本字段等
Posted
技术标签:
【中文标题】在弹出视图之外的任何地方点击时关闭弹出视图,包括按钮、文本字段等【英文标题】:Close a popup view when tapping anywhere outside it, including buttons, textfields, etc 【发布时间】:2021-09-14 01:32:56 【问题描述】:问题已编辑,因为人们似乎很困惑......
请参阅下面的代码并观看随附的“视频”,了解正在发生的事情。弹出窗口关闭:
当用户点击弹出窗口中的按钮选择时 当用户在弹出窗口外点击时,父视图上的任意位置(从而关闭弹出窗口而不从弹出窗口中进行选择)这是我想要的行为。但是,如果用户点击 parent 视图中的按钮或文本字段,弹出窗口不会关闭。因此,在这种情况下,弹出窗口仍然会弹出。
如何在弹出窗口之外的任何位置检测点击手势,包括按钮、文本字段和任何其他已经拥有自己的点击处理程序的 UI 元素,以便我可以关闭弹出窗口而不劫持这些点击处理程序的行为?
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate, UITextFieldDelegate
var popup: UIView!
var label: UILabel!
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = .darkGray
let textfield = UITextField()
textfield.backgroundColor = .white
textfield.translatesAutoresizingMaskIntoConstraints = false
textfield.placeholder = "some text"
view.addSubview(textfield)
let button1 = UIButton()
button1.setTitle("Button", for: .normal)
button1.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
button1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button1)
let button2 = UIButton()
button2.setTitle("Show Popup", for: .normal)
button2.addTarget(self, action: #selector(popupButtonTapped), for: .touchUpInside)
button2.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button2)
label = UILabel()
label.textColor = .yellow
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
NSLayoutConstraint.activate([
textfield.centerXAnchor.constraint(equalTo: view.centerXAnchor),
textfield.topAnchor.constraint(equalTo: view.topAnchor, constant: 160),
textfield.widthAnchor.constraint(equalToConstant: 299),
textfield.heightAnchor.constraint(equalToConstant: 30),
button1.topAnchor.constraint(equalTo: textfield.bottomAnchor, constant: 40),
button1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button2.topAnchor.constraint(equalTo: button1.bottomAnchor, constant: 40),
button2.centerXAnchor.constraint(equalTo: view.centerXAnchor),
//label.topAnchor.constraint(equalTo: button2.bottomAnchor),
label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -260),
label.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
textfield.delegate = self
let viewTapGesture = UITapGestureRecognizer(target: self, action: #selector(viewTapped))
viewTapGesture.delegate = self
view.addGestureRecognizer(viewTapGesture)
@objc func viewTapped(gestureRecognizer: UITapGestureRecognizer)
popup?.isHidden = true
@objc func buttonTapped(_ button: UIButton)
label.text = "Button tapped!"
@objc func popupButtonTapped(_ button: UIButton)
if popup == nil
popup = UIView()
popup.backgroundColor = #colorLiteral(red: 1, green: 0.9175537825, blue: 0.79708004, alpha: 1)
popup.layer.borderWidth = 1
popup.layer.borderColor = UIColor.black.cgColor
popup.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(popup)
let stackview = UIStackView()
stackview.axis = .vertical
stackview.alignment = .fill
stackview.distribution = .fillEqually
stackview.translatesAutoresizingMaskIntoConstraints = false
popup.addSubview(stackview)
for i in 1...5
let button = UIButton()
button.setTitle("Selection \(i)", for: .normal)
button.setTitleColor(.black, for: .normal)
button.widthAnchor.constraint(equalToConstant: 120).isActive = true
button.addTarget(self, action: #selector(popupItemTapped), for: .touchUpInside)
stackview.addArrangedSubview(button)
NSLayoutConstraint.activate([
stackview.topAnchor.constraint(equalTo: popup.topAnchor),
stackview.leadingAnchor.constraint(equalTo: popup.leadingAnchor),
stackview.trailingAnchor.constraint(equalTo: popup.trailingAnchor),
stackview.bottomAnchor.constraint(equalTo: popup.bottomAnchor),
popup.topAnchor.constraint(equalTo: button.topAnchor),
popup.leadingAnchor.constraint(equalTo: button.leadingAnchor),
])
else
popup.isHidden = false
@objc func popupItemTapped(_ button: UIButton)
label.text = "\(button.currentTitle!) tapped!"
popup.isHidden = true
func textFieldShouldReturn(_ textField: UITextField) -> Bool
textField.resignFirstResponder()
label.text = "You typed: \(textField.text!)"
return true
这就是它在行动中的样子。请注意,当您在其中点击以进行选择时,弹出窗口将关闭。当您在外部点击时,它也会关闭,但在您点击文本字段或点击“按钮”时不会关闭。我希望当您点击弹出窗口外部的任何位置时关闭弹出窗口,即使在文本字段或“按钮”内也是如此。如果您通过在文本字段内或“按钮”上点击来关闭它,它们应该会继续像往常一样响应。
【问题讨论】:
"当在其中点击文本字段时弹出的视图(如工具提示)" 您在代码中的什么位置?甚至没有人知道它来自哪里以及你用什么类来展示它。 @ElTomato 我通过包含代码和演示来编辑我的问题。 提示:removeFromSuperview()
.
@ElTomato 对不起,我不关注。我在删除弹出窗口时没有问题。其实我只是在隐瞒。我的问题是如何检测弹出窗口之外的任何地方的点击,包括文本字段、按钮等,以便我可以隐藏弹出窗口。
我明白了。对于那个很抱歉。然后尝试在文本字段中添加手势。
【参考方案1】:
看来我只需要添加以下内容:
@objc func buttonTapped(_ button: UIButton)
popup?.isHidden = true
label.text = "Button tapped!"
func textFieldDidBeginEditing(_ textField: UITextField)
popup?.isHidden = true
如果父视图中有数百个文本字段和按钮(以及其他启用了用户交互的 UI 元素),我必须对它们中的每一个都执行相同的操作。我希望有一个更通用、更优雅的解决方案。
【讨论】:
【参考方案2】:修改后的答案。这使您可以在弹出窗口之外点击。我测试过,它可以工作。
将此添加到您的 viewDidLoad。
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
view.addGestureRecognizer(tap)
let pop = YourPopup()
做某事后使弹出可见。
添加如下方法:
@objc func handleTap(_ sender: UITapGestureRecognizer? = nil) pop.removeFromSuperview()
【讨论】:
对不起,我不明白。你想解决什么问题?删除弹出窗口没有问题。我的问题是如何检测弹出窗口外的点击,包括点击文本字段、按钮等,以便我可以关闭/隐藏弹出窗口。请重新阅读整个问题并观看随附的“视频”。 我明白了。如果你点击里面的任何地方,我的解决方案就可以工作:) 我用这样的东西来捕捉外面的敲击,但它需要激活某些控制。所以以此类推,我会在弹出窗口下为整个屏幕创建一个视图并使其可点击。 @IBAction func menuButtonClicked(_ sender: UIButton) if (isPopupPresented == false) //显示弹窗 isPopupPresented = true else isPopupPresented = false pop.animateOut() 【参考方案3】:经过多次摆弄,我终于找到了一种方法,可以随时随地关闭/隐藏弹出窗口,并且仍然让屏幕上任何位置的任何 UI 元素(包括弹出窗口本身)都可以进行点击操作。如果点击在弹出窗口内部,则选择弹出窗口并关闭弹出窗口。如果点击在父视图上弹出窗口外部的任何位置,弹出窗口将关闭。如果点击屏幕上的任何其他 UI 元素,即使屏幕上有上千个,弹出窗口也会关闭,但 UI 元素仍会像往常一样响应点击。
我们需要设置 UITapGestureRecognizer,但是我们没有使用#selector 函数,而是让gestureRecognizer(_:shouldReceive:)
来完成这项工作,所以我们设置了action: nil
。
// Setup TapGestureRecognizer. The ACTION PARAMETER IS NIL since we do not need a
// selector function. We'll let gestureRecognizer(_:shouldReceive:) do the work.
// But we MUST at least register the gesture recognizer's delegate with the view!
let viewTapGesture = UITapGestureRecognizer(target: self, action: nil)
viewTapGesture.delegate = self
view.addGestureRecognizer(viewTapGesture)
和
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
if !popup.isHidden
popup.isHidden = true
return false
这是完整的(修改后的)代码:
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate, UITextFieldDelegate
var popup: UIView!
var label: UILabel!
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = .darkGray
let textfield = UITextField()
textfield.backgroundColor = .white
textfield.translatesAutoresizingMaskIntoConstraints = false
textfield.placeholder = "some text"
view.addSubview(textfield)
let button1 = UIButton()
button1.setTitle("Button", for: .normal)
button1.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
button1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button1)
let button2 = UIButton()
button2.setTitle("Show Popup", for: .normal)
button2.addTarget(self, action: #selector(popupButtonTapped), for: .touchUpInside)
button2.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button2)
label = UILabel()
label.textColor = .yellow
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
NSLayoutConstraint.activate([
textfield.centerXAnchor.constraint(equalTo: view.centerXAnchor),
textfield.topAnchor.constraint(equalTo: view.topAnchor, constant: 160),
textfield.widthAnchor.constraint(equalToConstant: 299),
textfield.heightAnchor.constraint(equalToConstant: 30),
button1.topAnchor.constraint(equalTo: textfield.bottomAnchor, constant: 40),
button1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button2.topAnchor.constraint(equalTo: button1.bottomAnchor, constant: 40),
button2.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -260),
label.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
popup = UIView()
popup.backgroundColor = #colorLiteral(red: 1, green: 0.9175537825, blue: 0.79708004, alpha: 1)
popup.layer.borderWidth = 1
popup.layer.borderColor = UIColor.black.cgColor
popup.translatesAutoresizingMaskIntoConstraints = false
popup.isHidden = true
view.addSubview(popup)
let stackview = UIStackView()
stackview.axis = .vertical
stackview.alignment = .fill
stackview.distribution = .fillEqually
stackview.translatesAutoresizingMaskIntoConstraints = false
popup.addSubview(stackview)
for i in 1...5
let button = UIButton()
button.setTitle("Selection \(i)", for: .normal)
button.setTitleColor(.black, for: .normal)
button.widthAnchor.constraint(equalToConstant: 120).isActive = true
button.addTarget(self, action: #selector(popupItemTapped), for: .touchUpInside)
stackview.addArrangedSubview(button)
NSLayoutConstraint.activate([
stackview.topAnchor.constraint(equalTo: popup.topAnchor),
stackview.leadingAnchor.constraint(equalTo: popup.leadingAnchor),
stackview.trailingAnchor.constraint(equalTo: popup.trailingAnchor),
stackview.bottomAnchor.constraint(equalTo: popup.bottomAnchor),
popup.topAnchor.constraint(equalTo: button2.topAnchor),
popup.leadingAnchor.constraint(equalTo: button2.leadingAnchor),
])
textfield.delegate = self
// Setup TapGestureRecognizer. The ACTION PARAMETER IS NIL since we do not need a
// selector function. We'll let gestureRecognizer(_:shouldReceive:) do the work.
// But we MUST at least register the gesture recognizer's delegate with the view!
let viewTapGesture = UITapGestureRecognizer(target: self, action: nil)
viewTapGesture.delegate = self
view.addGestureRecognizer(viewTapGesture)
@objc func buttonTapped(_ button: UIButton)
label.text = "Button tapped!"
@objc func popupButtonTapped(_ button: UIButton)
popup.isHidden = false
@objc func popupItemTapped(_ button: UIButton)
label.text = "\(button.currentTitle!) tapped!"
func textFieldShouldReturn(_ textField: UITextField) -> Bool
label.text = "You typed: \(textField.text!)"
return true
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
if !popup.isHidden
popup.isHidden = true
return false
【讨论】:
以上是关于在弹出视图之外的任何地方点击时关闭弹出视图,包括按钮、文本字段等的主要内容,如果未能解决你的问题,请参考以下文章