为啥当 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 中窃取焦点?

当我添加自动布局约束时,为啥我的 textview 不显示?

Swift iOS - 当 TextField、TextView 和背景被点击但丢失事件时删除视图