为啥当 textview 被 resignedFirstResponder() 时导航栏按钮项不起作用?
Posted
技术标签:
【中文标题】为啥当 textview 被 resignedFirstResponder() 时导航栏按钮项不起作用?【英文标题】:Why a navigation bar button item doesn't work when the textview has been resignedFirstResponder()?为什么当 textview 被 resignedFirstResponder() 时导航栏按钮项不起作用? 【发布时间】:2018-01-11 16:50:53 【问题描述】:这是一个奇怪的问题。我有两个条形按钮项目,一个是取消,一个是完成。完成按钮将文本保存在 coreData 中的 textview 中,然后从 navigationController 弹出当前视图并转到上一个视图控制器。取消按钮只是转到前一个控制器。当 textview 是 firstResponder() 或键盘启动时,整个事情都有效。当键盘作为 firstResponder 退出时,两个按钮都不起作用。就像什么都不做一样。完成按钮不会保存文本,也不会将我带到上一个视图控制器。
这是两个按钮的代码:
完成按钮:
@objc func handleDoneButton()
print("hello")
if edittaskview.text.isEmpty == true
moContext.delete(editnotes!)
else
editnotes?.sNote = edittaskview.text
var error: NSError?
do
// Save The object
try moContext.save()
print("SAVED")
catch let error1 as NSError
error = error1
_ = navigationController?.popViewController(animated: true)
取消按钮:
@objc func handleCancelButton()
_ = navigationController?.popViewController(animated: true)
同样,这两个函数中的代码仅在文本视图是第一响应者时才在点击按钮时起作用,否则它们不会。
ViewController 代码:
import UIKit
import CoreData
class editViewController: UIViewController, UICollectionViewDelegate,
UICollectionViewDataSource, UICollectionViewDelegateFlowLayout,
UITextViewDelegate, UIGestureRecognizerDelegate
var editnotes: addednotes?
var prioritynumber: Int = 1
let moContext = (UIApplication.shared.delegate as!
AppDelegate).persistentContainer.viewContext
var colorArray = [prioritylevels]()
lazy var edittaskview: UITextView =
let textview = UITextView()
textview.isScrollEnabled = false
textview.font = UIFont.systemFont(ofSize: 16)
textview.backgroundColor = .white
textview.delegate = self
return textview
()
var clearview: UIView =
let view = UIView()
view.backgroundColor = .clear
//view.isUserInteractionEnabled = false
return view
()
let rightBarButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(handleDoneButton))
let leftBarButton = UIBarButtonItem(title: "Cancel", style: .done, target: self, action: #selector(handleCancelButton))
lazy var priorityCV: UICollectionView =
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 0
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.delegate = self
cv.dataSource = self
return cv
()
let line: UIView =
let line = UIView()
return line
()
let reminderView: UIView =
let reminder = UIView()
reminder.backgroundColor = .white
return reminder
()
let reminderTitle: UILabel =
let title = UILabel()
title.text = "Due Date"
title.textColor = .black
title.font = UIFont(name: "Avenir Next", size: 16)
title.font = UIFont.boldSystemFont(ofSize: 16)
return title
()
let reminderMsg: UILabel =
let title = UILabel()
title.text = "No Due Date Set"
title.textColor = .black
// title.font = UIFont(name: "Avenir Next", size: 12)
title.font = UIFont.systemFont(ofSize: 12)
return title
()
let reminderAddBtn: UIButton =
let btn = UIButton(type: .system)
btn.setTitle("Set", for: .normal)
btn.setTitleColor(.black, for: .normal)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 14)
btn.addTarget(self, action: #selector(handleSetReminderBtn), for: .touchUpInside)
return btn
()
var textHeightConstraint: NSLayoutConstraint?
override func viewWillAppear(_ animated: Bool)
adjustTextViewHeight()
let datePicker: UIDatePicker = UIDatePicker()
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = UIColor(red:0.97, green:0.97, blue:0.97, alpha:1.0)
datePicker.addTarget(self, action: #selector(datePickerValueChanged(_:)), for: .valueChanged)
//ColorArray for PriorityCV
colorArray = [prioritylevels(color: UIColor(red:0.00, green:0.78, blue:0.73, alpha:1.0), name: " Low", prioritynumber: 1), prioritylevels(color: UIColor(red:0.00, green:0.78, blue:0.35, alpha:1.0), name: "Medium", prioritynumber: 2),prioritylevels(color: UIColor(red:0.88, green:0.63, blue:0.00, alpha:1.0), name: " High", prioritynumber: 3), prioritylevels(color: UIColor(red:0.96, green:0.28, blue:0.70, alpha:1.0), name: "Urgent", prioritynumber: 4)]
priorityCV.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cvid")
navigationItem.title = "Edit Task"
self.navigationItem.setHidesBackButton(true, animated: false)
self.navigationController?.navigationBar.tintColor = UIColor.white
navigationItem.rightBarButtonItem = rightBarButton
navigationItem.leftBarButtonItem = leftBarButton
view.addSubview(clearview)
clearview.addSubview(line)
clearview.addSubview(edittaskview)
clearview.addSubview(priorityCV)
clearview.addSubview(reminderView)
reminderView.addSubview(reminderTitle)
reminderView.addSubview(reminderMsg)
reminderView.addSubview(reminderAddBtn)
edittaskview.text = editnotes?.sNote
edittaskview.becomeFirstResponder() //taskview becomes first responder here when the view is loaded.
// view.addSubview(datePicker)
setupViews()
line.backgroundColor = editnotes?.sPriorityColor
let dismissbytap = UITapGestureRecognizer()
dismissbytap.addTarget(self, action: #selector(handleDismissByTap))
dismissbytap.delegate = self
clearview.addGestureRecognizer(dismissbytap) //a clear uiview is added behind all the subviews. this uiview is used to dismiss keyboard when tapped on it.
override func didReceiveMemoryWarning()
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return colorArray.count
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cvid", for: indexPath)
cell.backgroundColor = colorArray[indexPath.row].color
let prioritylabel = UILabel(frame: CGRect(x: view.frame.width / 20, y: 0, width: view.frame.width / 4, height: 30))
cell.addSubview(prioritylabel)
prioritylabel.text = colorArray[indexPath.row].name
prioritylabel.textColor = .white
return cell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
return CGSize(width: view.frame.width / 4, height: 30)
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
prioritynumber = colorArray[indexPath.row].prioritynumber
line.backgroundColor = colorArray[indexPath.row].color
editnotes?.sPriorityColor = colorArray[indexPath.row].color
editnotes?.sPriorityNumber = prioritynumber
print(prioritynumber)
func setupViews()
_ = edittaskview.anchor(line.bottomAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, topConstant: 2, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 0)
self.edittaskview.contentInset = UIEdgeInsetsMake(2, 8, 4, 8)
self.textHeightConstraint = edittaskview.heightAnchor.constraint(equalToConstant: 80)
self.textHeightConstraint?.isActive = true
self.adjustTextViewHeight()
_ = clearview.anchor(view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 0)
_ = priorityCV.anchor(edittaskview.bottomAnchor, left: clearview.leftAnchor, bottom: nil, right: clearview.rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 30)
_ = line.anchor(clearview.topAnchor, left: clearview.leftAnchor, bottom: nil, right: clearview.rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 4)
_ = reminderView.anchor(priorityCV.bottomAnchor, left: clearview.leftAnchor, bottom: nil, right: view.rightAnchor, topConstant: 30, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 60)
_ = reminderTitle.anchor(reminderView.topAnchor, left: reminderView.leftAnchor, bottom: nil, right: nil, topConstant: 8, leftConstant: 8, bottomConstant: 0, rightConstant: 0, widthConstant: 100, heightConstant: 20)
_ = reminderMsg.anchor(reminderTitle.bottomAnchor, left: reminderView.leftAnchor, bottom: nil, right: nil, topConstant: 4, leftConstant: 8, bottomConstant: 0, rightConstant: 0, widthConstant: 100, heightConstant: 24)
_ = reminderAddBtn.anchor(reminderView.topAnchor, left: nil, bottom: nil, right: reminderView.rightAnchor, topConstant: 20, leftConstant: 0, bottomConstant: 20, rightConstant: 32, widthConstant: 30, heightConstant: 20)
@objc func handleDoneButton()
print("hello")
if edittaskview.text.isEmpty == true
moContext.delete(editnotes!)
else
editnotes?.sNote = edittaskview.text
var error: NSError?
do
// Save The object
try moContext.save()
print("SAVED")
catch let error1 as NSError
error = error1
_ = navigationController?.popViewController(animated: true)
func textViewDidChange(_ textView: UITextView)
self.adjustTextViewHeight()
func adjustTextViewHeight()
let fixedWidth = edittaskview.frame.size.width
let newSize = edittaskview.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
self.textHeightConstraint?.constant = newSize.height + 15
self.view.layoutIfNeeded()
@objc func handleCancelButton()
_ = navigationController?.popViewController(animated: true)
@objc func datePickerValueChanged(_ sender: UIDatePicker)
let componenets = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: sender.date)
if let day = componenets.day, let month = componenets.month, let year = componenets.year, let hour = componenets.hour, let minute = componenets.minute
print("\(day) \(month) \(year) \(hour) \(minute)")
@objc func handleDismissByTap()
edittaskview.resignFirstResponder() // Resigning textview as first responder
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
return touch.view == gestureRecognizer.view
【问题讨论】:
奇怪。检查您的视图层次结构...... TextView 和按钮之间没有任何联系(除非您手动禁用它们)。 这些是导航栏按钮。是的,这两件事之间没有联系。不知道是什么问题! 用户如何在您的应用中关闭键盘? 点击文本视图之外的任何地方。 如果您包含文本视图退出第一响应者的代码,这可能会有所帮助。 【参考方案1】:这看起来很奇怪 - 我不知道为什么导航栏按钮仅在您的文本视图处于活动状态时调用函数,但是...
解决它的一种方法是将您的左右 UIBarButtonItem
声明移动到 viewDidLoad()
而不是类级别。
顺便说一句,如果将 edittaskview.becomeFirstResponder()
从 viewDidLoad()
移动到 viewDidAppear()
,您将获得更好的动画和键盘外观。
【讨论】:
【参考方案2】:我不知道如何,而是单独创建条形按钮,然后像这样分配它们self.navigationBarItem.leftbarbutton = leftBarbutton
我决定使用 UIBarButtonItem 构造函数来构造它们。
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .done, target: self, action: #selector(handleCancelButton))
成功了!
说实话很奇怪。
【讨论】:
【参考方案3】:您也可以将UIBarButtonItem
声明为lazy var
。
它被类实例化了。如果您将其设为lazy var
,它将在您需要使用它时创建。如果您想将其保留为 let
并保持在班级级别,请执行以下操作:
override func viewDidLoad()
super.viewDidLoad()
yourBarButtonItem.target = self
这两种方法中的任何一种都会使目标保持不变。我遇到了同样的问题。
这仍然是一个奇怪的问题,我认为编译器应该对你在初始化之前尝试使用 self 大喊大叫。当您输入 self()
时,它会这样做。
【讨论】:
以上是关于为啥当 textview 被 resignedFirstResponder() 时导航栏按钮项不起作用?的主要内容,如果未能解决你的问题,请参考以下文章
为啥当我遇到真实设备或模拟器时没有显示此 TextViews
为啥当 Interstitial 关闭时先前输入的文本会从 TextView 中消失
为啥 textView:shouldInteractWithTextAttachment:inRange: 永远不会在 UITextView 委托上被调用?
为啥 Android TabHost 从 TextView 中窃取焦点?