Swift iOS - 当 TextField、TextView 和背景被点击但丢失事件时删除视图
Swift iOS -Remove View When TextField, TextView, and Background Tapped But Losing Events
首先让我说我玩过程序化,但我目前是新手。
我混合了编程视图和故事板对象:
故事板对象:
- 按钮
- 文本字段
- TextView
程序化视图:
- 消息标签
- viewForMessageLabel
当我按下按钮时,添加了 viewForMessageLabel
。在 viewDidLoad
中,我添加了一个点击手势以在点击背景时删除 viewForMessageLabel
。我还将相同的点击手势添加到 textField 以删除 viewForMessageLabel
(如果存在)。我再次将相同的点击手势添加到 textField 以将其也删除。
如果键盘存在,我会在 viewDidLoad
中向 textField 添加另一个点击手势以关闭它。我发现事情很古怪,我失去了触摸事件。
如果我在触摸背景时按下按钮添加标签,它不会消失。如果我按下 textField,它将关闭它并显示键盘。当我再次按下按钮时 textField 仍然存在时,标签出现,我再次按下 textField 但没有任何反应。当我按 return 隐藏键盘时(我实现了该方法),键盘消失,按下按钮, viewForMessageLabel
出现,现在当我按下 textField 时 viewForMessageLabel
消失. textField 基本上发生了同样的事情。
我要的是
如果 viewForMessageLabel
存在并且我按下背景、textField 或 textView,它应该会消失。
如果 textField 或 textView 的键盘存在并且我按下背景,键盘也应该消失。
我的代码:
class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
//MARK:- Outlets
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var textView: UITextView!
@IBOutlet weak var button: UIButton!
let messagelabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Pizza Pizza Pizza Pizza Pizza"
label.font = UIFont(name: "Helvetica-Regular", size: 17)
label.sizeToFit()
label.numberOfLines = 0
label.textAlignment = .center
label.textColor = UIColor.white
label.backgroundColor = UIColor.clear
return label
}()
let viewForMessageLabel: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.red
return view
}()
//View Controller Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
textView.delegate = self
// 0. hide viewForMessageLabel is background is tapped
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(removeViewForMessageLabel))
view.addGestureRecognizer(tapGesture)
// 1. hide viewForMessageLabel if textView is tapped
textView.addGestureRecognizer(tapGesture)
// 2. hide keyboard if background if tapped
let hideKeyboard = UITapGestureRecognizer(target: self, action: #selector(hideKeyboardWhenBackGroundTapped))
view.addGestureRecognizer(hideKeyboard)
// 3. hide keyboard if textView is tapped
textView.addGestureRecognizer(hideKeyboard)
// 4. hide viewForMessageLabel for textField if background is tapped
textField.addTarget(self, action: #selector(removeViewForMessageLabel), for: .editingDidBegin)
}
//MARK:- Button
@IBAction func buttonPressed(_ sender: UIButton) {
view.addSubview(viewForMessageLabel)
setViewForMessageLabelAnchors()
setMessageLabelAnchors()
}
//MARK:- Functions
func setViewForMessageLabelAnchors(){
viewForMessageLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 44).isActive = true
viewForMessageLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
viewForMessageLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
viewForMessageLabel.addSubview(messagelabel)
}
func setMessageLabelAnchors(){
messagelabel.topAnchor.constraint(equalTo: viewForMessageLabel.topAnchor, constant: 0).isActive = true
messagelabel.widthAnchor.constraint(equalTo: viewForMessageLabel.widthAnchor).isActive = true
viewForMessageLabel.bottomAnchor.constraint(equalTo: messagelabel.bottomAnchor, constant: 0).isActive = true
}
func removeViewForMessageLabel(){
viewForMessageLabel.removeFromSuperview()
}
func hideKeyboardWhenBackGroundTapped(){
textField.resignFirstResponder()
}
//MARK:- TextField Delegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
view.endEditing(true)
return true
}
func textViewDidBeginEditing(_ textView: UITextView) {
removeViewForMessageLabel()
}
//MARK:- TextView Delegate
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
}
- If the textField's or textView's keyboard is present and I press the background the keyboard should disappear also.
您将其呈现为以当前是否显示键盘为条件,但您的代码并未反映(也不应该)。您可以根据需要多次调用 resignFirstResponder
并且不会发生任何不好的事情。您还可以在已删除的视图上调用 removeFromSuperview
(请参阅 here)。
因此我认为您可以只将一个动作附加到单击手势识别器:
var tapGesture: UITapGestureRecognizer?
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
textView.delegate = self
// 0. hide viewForMessageLabel is background is tapped
tapGesture = UITapGestureRecognizer(target: self, action: #selector(removeLabelAndHideKeyboard))
view.addGestureRecognizer(tapGesture)
// 1. hide viewForMessageLabel if textView is tapped
textView.addGestureRecognizer(tapGesture)
}
func removeLabelAndHideKeyboard() {
viewForMessageLabel.removeFromSuperview()
textField.resignFirstResponder()
}
这并不能完全回答问题,但我找到了解决方法。如果我使用@Toddg 建议的方法:
func removeLabelAndHideKeyboard() {
viewForMessageLabel.removeFromSuperview()
textField.resignFirstResponder()
}
它将退出 textField 添加到功能中,这非常有帮助。
我还在 viewDidLoad 中添加了:
textField.addTarget(self, action: #selector(removeViewForMessageLabel), for: .touchDown)
关键是使用 .touchDown 而不是 .editingDidBegin。这样我就可以在 textField 和 textView 之间来回切换,键盘将响应两者。我不得不再添加 1 个东西 - 一个 toolBar 到 textView 的键盘,上面有完成按钮以关闭 textView:
func addDoneButtonOnKeyboard(){
let toolBar = UIToolbar()
toolBar.sizeToFit()
doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissKeyboard))
toolBar.setItems([doneButton!], animated: true)
textView.inputAccessoryView = toolBar
}
@objc func dismissTextViewKeyboard(){
view.endEditing(true)
}
这样当 textView 出现时我可以关闭它。
在所有情况下,如果我按下 textField、background 或 textView 并且存在 viewForMessageLabel,它就会消失。
如果 textField 是第一响应者并且它的键盘存在并且我按下背景它就会消失。
除了其他所有内容之外,我还没有弄清楚如何在触摸背景时也关闭 textView,所以我在工具栏上实现了一个完成按钮。如果我按下它并且 textView 的键盘存在,当它调用我添加的 dismissTextViewKeyboard()
函数时将被关闭。两者都在底部,其他所有内容都在 viewDidLoad 中。
如果有人有更好的答案,我会投票给它。
class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
//MARK:- Outlets
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var textView: UITextView!
@IBOutlet weak var button: UIButton!
let messagelabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Pizza Pizza Pizza Pizza Pizza"
label.font = UIFont(name: "Helvetica-Regular", size: 17)
label.sizeToFit()
label.numberOfLines = 0
label.textAlignment = .center
label.textColor = UIColor.white
label.backgroundColor = UIColor.clear
return label
}()
let viewForMessageLabel: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.red
return view
}()
fileprivate var doneButton: UIBarButtonItem?
//View Controller Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
textView.delegate = self
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(removeViewForMessageLabel))
view.addGestureRecognizer(tapGesture)
textField.addTarget(self, action: #selector(removeViewForMessageLabel), for: .touchDown)
addDoneButtonOnKeyboard()
}
//MARK:- Button
@IBAction func buttonPressed(_ sender: UIButton) {
//removeMessage()
view.addSubview(viewForMessageLabel)
setBackgroundAnchors()
setMessageAndLabelAnchors()
}
//MARK:- Functions
func setBackgroundAnchors(){
viewForMessageLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 44).isActive = true
viewForMessageLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
viewForMessageLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
viewForMessageLabel.addSubview(messagelabel)
}
func setMessageAndLabelAnchors(){
messagelabel.topAnchor.constraint(equalTo: viewForMessageLabel.topAnchor, constant: 0).isActive = true
messagelabel.widthAnchor.constraint(equalTo: viewForMessageLabel.widthAnchor).isActive = true
viewForMessageLabel.bottomAnchor.constraint(equalTo: messagelabel.bottomAnchor, constant: 0).isActive = true
}
func removeViewForMessageLabel(){
viewForMessageLabel.removeFromSuperview()
textField.resignFirstResponder()
}
//MARK:- TextField Delegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
view.endEditing(true)
return true
}
func textViewDidBeginEditing(_ textView: UITextView) {
removeViewForMessageLabel()
}
//MARK:- TextView Delegate
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
//MARK:- Additional Functions
//add a done button to the keyboard when the textView is first responder
fileprivate func addDoneButtonOnKeyboard(){
let toolBar = UIToolbar()
toolBar.sizeToFit()
doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissTextViewKeyboard))
toolBar.setItems([doneButton!], animated: true)
textView.inputAccessoryView = toolBar
}
//dismiss the keyboard when the Done button is tapped
@objc func dismissTextViewKeyboard(){
view.endEditing(true)
}
}
首先让我说我玩过程序化,但我目前是新手。
我混合了编程视图和故事板对象:
故事板对象:
- 按钮
- 文本字段
- TextView
程序化视图:
- 消息标签
- viewForMessageLabel
当我按下按钮时,添加了 viewForMessageLabel
。在 viewDidLoad
中,我添加了一个点击手势以在点击背景时删除 viewForMessageLabel
。我还将相同的点击手势添加到 textField 以删除 viewForMessageLabel
(如果存在)。我再次将相同的点击手势添加到 textField 以将其也删除。
如果键盘存在,我会在 viewDidLoad
中向 textField 添加另一个点击手势以关闭它。我发现事情很古怪,我失去了触摸事件。
如果我在触摸背景时按下按钮添加标签,它不会消失。如果我按下 textField,它将关闭它并显示键盘。当我再次按下按钮时 textField 仍然存在时,标签出现,我再次按下 textField 但没有任何反应。当我按 return 隐藏键盘时(我实现了该方法),键盘消失,按下按钮, viewForMessageLabel
出现,现在当我按下 textField 时 viewForMessageLabel
消失. textField 基本上发生了同样的事情。
我要的是
如果
viewForMessageLabel
存在并且我按下背景、textField 或 textView,它应该会消失。如果 textField 或 textView 的键盘存在并且我按下背景,键盘也应该消失。
我的代码:
class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
//MARK:- Outlets
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var textView: UITextView!
@IBOutlet weak var button: UIButton!
let messagelabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Pizza Pizza Pizza Pizza Pizza"
label.font = UIFont(name: "Helvetica-Regular", size: 17)
label.sizeToFit()
label.numberOfLines = 0
label.textAlignment = .center
label.textColor = UIColor.white
label.backgroundColor = UIColor.clear
return label
}()
let viewForMessageLabel: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.red
return view
}()
//View Controller Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
textView.delegate = self
// 0. hide viewForMessageLabel is background is tapped
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(removeViewForMessageLabel))
view.addGestureRecognizer(tapGesture)
// 1. hide viewForMessageLabel if textView is tapped
textView.addGestureRecognizer(tapGesture)
// 2. hide keyboard if background if tapped
let hideKeyboard = UITapGestureRecognizer(target: self, action: #selector(hideKeyboardWhenBackGroundTapped))
view.addGestureRecognizer(hideKeyboard)
// 3. hide keyboard if textView is tapped
textView.addGestureRecognizer(hideKeyboard)
// 4. hide viewForMessageLabel for textField if background is tapped
textField.addTarget(self, action: #selector(removeViewForMessageLabel), for: .editingDidBegin)
}
//MARK:- Button
@IBAction func buttonPressed(_ sender: UIButton) {
view.addSubview(viewForMessageLabel)
setViewForMessageLabelAnchors()
setMessageLabelAnchors()
}
//MARK:- Functions
func setViewForMessageLabelAnchors(){
viewForMessageLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 44).isActive = true
viewForMessageLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
viewForMessageLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
viewForMessageLabel.addSubview(messagelabel)
}
func setMessageLabelAnchors(){
messagelabel.topAnchor.constraint(equalTo: viewForMessageLabel.topAnchor, constant: 0).isActive = true
messagelabel.widthAnchor.constraint(equalTo: viewForMessageLabel.widthAnchor).isActive = true
viewForMessageLabel.bottomAnchor.constraint(equalTo: messagelabel.bottomAnchor, constant: 0).isActive = true
}
func removeViewForMessageLabel(){
viewForMessageLabel.removeFromSuperview()
}
func hideKeyboardWhenBackGroundTapped(){
textField.resignFirstResponder()
}
//MARK:- TextField Delegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
view.endEditing(true)
return true
}
func textViewDidBeginEditing(_ textView: UITextView) {
removeViewForMessageLabel()
}
//MARK:- TextView Delegate
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
}
- If the textField's or textView's keyboard is present and I press the background the keyboard should disappear also.
您将其呈现为以当前是否显示键盘为条件,但您的代码并未反映(也不应该)。您可以根据需要多次调用 resignFirstResponder
并且不会发生任何不好的事情。您还可以在已删除的视图上调用 removeFromSuperview
(请参阅 here)。
因此我认为您可以只将一个动作附加到单击手势识别器:
var tapGesture: UITapGestureRecognizer?
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
textView.delegate = self
// 0. hide viewForMessageLabel is background is tapped
tapGesture = UITapGestureRecognizer(target: self, action: #selector(removeLabelAndHideKeyboard))
view.addGestureRecognizer(tapGesture)
// 1. hide viewForMessageLabel if textView is tapped
textView.addGestureRecognizer(tapGesture)
}
func removeLabelAndHideKeyboard() {
viewForMessageLabel.removeFromSuperview()
textField.resignFirstResponder()
}
这并不能完全回答问题,但我找到了解决方法。如果我使用@Toddg 建议的方法:
func removeLabelAndHideKeyboard() {
viewForMessageLabel.removeFromSuperview()
textField.resignFirstResponder()
}
它将退出 textField 添加到功能中,这非常有帮助。
我还在 viewDidLoad 中添加了:
textField.addTarget(self, action: #selector(removeViewForMessageLabel), for: .touchDown)
关键是使用 .touchDown 而不是 .editingDidBegin。这样我就可以在 textField 和 textView 之间来回切换,键盘将响应两者。我不得不再添加 1 个东西 - 一个 toolBar 到 textView 的键盘,上面有完成按钮以关闭 textView:
func addDoneButtonOnKeyboard(){
let toolBar = UIToolbar()
toolBar.sizeToFit()
doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissKeyboard))
toolBar.setItems([doneButton!], animated: true)
textView.inputAccessoryView = toolBar
}
@objc func dismissTextViewKeyboard(){
view.endEditing(true)
}
这样当 textView 出现时我可以关闭它。
在所有情况下,如果我按下 textField、background 或 textView 并且存在 viewForMessageLabel,它就会消失。
如果 textField 是第一响应者并且它的键盘存在并且我按下背景它就会消失。
除了其他所有内容之外,我还没有弄清楚如何在触摸背景时也关闭 textView,所以我在工具栏上实现了一个完成按钮。如果我按下它并且 textView 的键盘存在,当它调用我添加的 dismissTextViewKeyboard()
函数时将被关闭。两者都在底部,其他所有内容都在 viewDidLoad 中。
如果有人有更好的答案,我会投票给它。
class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
//MARK:- Outlets
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var textView: UITextView!
@IBOutlet weak var button: UIButton!
let messagelabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Pizza Pizza Pizza Pizza Pizza"
label.font = UIFont(name: "Helvetica-Regular", size: 17)
label.sizeToFit()
label.numberOfLines = 0
label.textAlignment = .center
label.textColor = UIColor.white
label.backgroundColor = UIColor.clear
return label
}()
let viewForMessageLabel: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.red
return view
}()
fileprivate var doneButton: UIBarButtonItem?
//View Controller Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
textView.delegate = self
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(removeViewForMessageLabel))
view.addGestureRecognizer(tapGesture)
textField.addTarget(self, action: #selector(removeViewForMessageLabel), for: .touchDown)
addDoneButtonOnKeyboard()
}
//MARK:- Button
@IBAction func buttonPressed(_ sender: UIButton) {
//removeMessage()
view.addSubview(viewForMessageLabel)
setBackgroundAnchors()
setMessageAndLabelAnchors()
}
//MARK:- Functions
func setBackgroundAnchors(){
viewForMessageLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 44).isActive = true
viewForMessageLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
viewForMessageLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
viewForMessageLabel.addSubview(messagelabel)
}
func setMessageAndLabelAnchors(){
messagelabel.topAnchor.constraint(equalTo: viewForMessageLabel.topAnchor, constant: 0).isActive = true
messagelabel.widthAnchor.constraint(equalTo: viewForMessageLabel.widthAnchor).isActive = true
viewForMessageLabel.bottomAnchor.constraint(equalTo: messagelabel.bottomAnchor, constant: 0).isActive = true
}
func removeViewForMessageLabel(){
viewForMessageLabel.removeFromSuperview()
textField.resignFirstResponder()
}
//MARK:- TextField Delegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
view.endEditing(true)
return true
}
func textViewDidBeginEditing(_ textView: UITextView) {
removeViewForMessageLabel()
}
//MARK:- TextView Delegate
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
//MARK:- Additional Functions
//add a done button to the keyboard when the textView is first responder
fileprivate func addDoneButtonOnKeyboard(){
let toolBar = UIToolbar()
toolBar.sizeToFit()
doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissTextViewKeyboard))
toolBar.setItems([doneButton!], animated: true)
textView.inputAccessoryView = toolBar
}
//dismiss the keyboard when the Done button is tapped
@objc func dismissTextViewKeyboard(){
view.endEditing(true)
}
}