2

I have a strange issue with regard to entering text into a text field. I am currently using the code below. My code is modeled after the answer here.

class RocketViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, NSFetchedResultsControllerDelegate {

   var offsetY:CGFloat = 0

   override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(RocketViewController.keyboardFrameChangeNotification(notification:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
    }

    @objc func keyboardFrameChangeNotification(notification: Notification) {
    if let userInfo = notification.userInfo {
        let keyBoardFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect
        let animationDuration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double ?? 0
        let animationCurveRawValue = (userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int) ?? Int(UIViewAnimationOptions.curveEaseInOut.rawValue)
        let animationCurve = UIViewAnimationOptions(rawValue: UInt(animationCurveRawValue))
        if let _ = keyBoardFrame, keyBoardFrame!.intersects(self.mainStackView.frame) {
            self.offsetY = self.mainStackView.frame.maxY - keyBoardFrame!.minY
            UIView.animate(withDuration: animationDuration, delay: TimeInterval(0), options: animationCurve, animations: {
                self.mainStackView.frame.origin.y = self.mainStackView.frame.origin.y - self.offsetY
                self.rocketSelectTable.frame.origin.y = self.rocketSelectTable.frame.origin.y - self.offsetY
            }, completion: nil)
        } else {
            if self.offsetY != 0 {
                UIView.animate(withDuration: animationDuration, delay: TimeInterval(0), options: animationCurve, animations: {
                    self.mainStackView.frame.origin.y = self.mainStackView.frame.origin.y + self.offsetY
                    self.rocketSelectTable.frame.origin.y = self.rocketSelectTable.frame.origin.y + self.offsetY
                    self.offsetY = 0
                  }, completion: nil)
               }
           }
       }
   }
}

In my view I have a table view with a fetched results controller as its data source, and below that are the text fields in a stack view, called mainStackView, that are eventually saved in a core data store.

I have gone through several iterations of this code with the same result, whether I compute the offset off the first responder, or simply the stack view. When a text field becomes the first responder, the view slides up nicely with the keyboard. However, as soon as I attempt to type in the field, the view snaps back to its original position. I am sure I am making a newbie mistake, but I can't figure out what I am doing wrong, and I have found nothing in my searches, except a similar question for android. Thanks in advance.

Yrb
  • 8,103
  • 2
  • 14
  • 44
  • An indent got lost somewhere in your code; see the trailing close curly? – SIGSTACKFAULT Apr 12 '18 at 12:00
  • The code shown in your question above looks good. I guess the problem is somewhere else in the code that you haven't added to the question. Perhaps you can add some more details? – Stephan Schlecht Apr 12 '18 at 18:56
  • I am really not sure what to add. I have the standard delegate methods for the table view. There are 7 text fields and corresponding labels. I was concerned about a function that does an autocomplete in one of the text fields, but I commented it out with no change. The only other function deals with the text fields is `@IBAction func didEndEditing` which is triggered by "editing did end" sent event from IB. I also put breakpoints in every function, and none of them are called. I was hoping there was some error in my code that I posted causing it. – Yrb Apr 12 '18 at 22:58

1 Answers1

0

While I have not determined why I was seeing the behavior with the text field that I was seeing with changing the frame, I was able to stop the behavior by using a CGAffineTransform instead. My code is now:

  @objc func keyboardFrameChangeNotification(notification: Notification) {
       if let userInfo = notification.userInfo {
           let keyBoardFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect
           let animationDuration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double ?? 0
           let animationCurveRawValue = (userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int) ?? Int(UIViewAnimationOptions.curveEaseInOut.rawValue)
           let animationCurve = UIViewAnimationOptions(rawValue: UInt(animationCurveRawValue))
           if let _ = keyBoardFrame, keyBoardFrame!.intersects(self.mainStackView.frame) {
               self.offsetY = self.mainStackView.frame.maxY - keyBoardFrame!.minY
               let transformUp = CGAffineTransform.init(translationX: 0, y: (0 - self.offsetY))
               UIView.animate(withDuration: animationDuration, delay: TimeInterval(0), options: animationCurve, animations: {
                   self.mainStackView.transform = transformUp
                   self.rocketSelectTable.transform = transformUp
               }, completion: nil)
           } else {
               if self.offsetY != 0 {
                   UIView.animate(withDuration: animationDuration, delay: TimeInterval(0), options: animationCurve, animations: {
                       self.mainStackView.transform = CGAffineTransform.identity
                       self.rocketSelectTable.transform = CGAffineTransform.identity
                       self.offsetY = 0
                   }, completion: nil)
               }
           }
       }

This code smoothly animates the movement of the views, and there is no snap back to the original position while typing in the text field. I hope this helps someone else.

Yrb
  • 8,103
  • 2
  • 14
  • 44