Simplify code with textfield and textview delegate methods?

Hi,
I need to move the controller up so fields are not hidden by keyboard.
So I use textfield and textview delegate methods to achieve this.

Here is the code, but it is repetitive.
Is there any way to make it less repetitive?

Code Block
  func textFieldDidBeginEditing(_ textField: UITextField) {
    self.view.setNeedsLayout()
    UIView.animate(withDuration: 0.2, animations: {
      self.view.center.y -= 150
    }, completion: { finished in
       
    })
  }
   
  func textFieldDidEndEditing(_ textField: UITextField) {
    self.view.setNeedsLayout()
    UIView.animate(withDuration: 0.2, animations: {
      self.view.center.y += 150
    }, completion: { finished in
       
    })
  }
   
  func textViewDidBeginEditing(_ textField: UITextView) {
    self.view.setNeedsLayout()
    UIView.animate(withDuration: 0.2, animations: {
      self.view.center.y -= 150
    }, completion: { finished in
       
    })
  }
   
  func textViewDidEndEditing(_ textField: UITextView) {
    self.view.setNeedsLayout()
    UIView.animate(withDuration: 0.2, animations: {
      self.view.center.y += 150
    }, completion: { finished in
       
    })
  }


Answered by Claude31 in 671538022
You could also group both actions in a single one, if tou define a property in the class : private var viewMoved = false

Code Block
@IBAction func beginEditing(_ sender: Any) {
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.2, animations: {
self.view.center.y += self.viewMoved ? -150 : 150
}, completion: { finished in
self.viewMoved.toggle()
})
}


And connect the beginEditing and endEditing events for both TextField and TextView to this IBAction.

However, I do not recommend such a design, harder to understand.
A first way to do is to create an IBAction with sender as Any:

Code Block
 @IBAction func beginEditing(_ sender: Any) {
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.2, animations: {
self.view.center.y -= 150
}, completion: { finished in
})
}

And connect in IB (connections inspector), the sent event "Editing Did Begin" to this IBAction, for both TextField and TextView.

Do similar for end editing.

Another way (I personally prefer) is to use keyboard notifications.
Register the top view to those notifications.

Create func to respond to those notifications:
Code Block
    @objc func keyboardWillShow(_ note : Notification) -> Void { }

and
Code Block
    @objc func keyboardWillHide(_ sender: Notification) { }

Accepted Answer
You could also group both actions in a single one, if tou define a property in the class : private var viewMoved = false

Code Block
@IBAction func beginEditing(_ sender: Any) {
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.2, animations: {
self.view.center.y += self.viewMoved ? -150 : 150
}, completion: { finished in
self.viewMoved.toggle()
})
}


And connect the beginEditing and endEditing events for both TextField and TextView to this IBAction.

However, I do not recommend such a design, harder to understand.
This is what I do in some of my apps.

Put all the textFields and textViews into a UIStackView, and add a proper constraint at the bottom.
Code Block
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
//...
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)),
name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)),
name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func keyboardWillShow(_ notification: Notification) {
print(notification)
if let keyboardBounds = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
bottomConstraint.constant = -keyboardBounds.height
}
}
@objc func keyboardWillHide(_ notification: Notification) {
if let _ = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
bottomConstraint.constant = 0
}
}


It is a bit difficult to add a proper constraint to the bottom of a UIStackView if you are not accustomed to it, but once done, changing bottomConstraint would move up all the fields and they will not be hidden.
Simplify code with textfield and textview delegate methods?
 
 
Q