Move view when a specific textfield is selected swift

1.7k Views Asked by At

I am created a meme generator app to better learn Swift and Xcode. I am learning to move the view when the user interacts with a text field that would be obstructed by the keyboard. I have this functionality working, with one exception. The desired functionality is to have the view slide up when the user is entering text for the bottom textfield, bot the top. The view slides up regardless of the text field the user is interacting with.

enter image description here

How can I assign this functionality only to the bottom text field? Here is my source code:

import UIKit

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate {

    @IBOutlet weak var imagePickerView: UIImageView!
    @IBOutlet weak var cameraButton: UIBarButtonItem!
    @IBOutlet weak var topText: UITextField!
    @IBOutlet weak var bottomText: UITextField!

    let memeTextAttributes:[String:Any] = [
        NSAttributedStringKey.strokeColor.rawValue: UIColor.black,
        NSAttributedStringKey.foregroundColor.rawValue: UIColor.white,
        NSAttributedStringKey.font.rawValue: UIFont(name: "HelveticaNeue-CondensedBlack", size: 30)!,
        NSAttributedStringKey.strokeWidth.rawValue: -5.0]

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        subscribeToKeyboardNotifications()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Diable camer a button if camera ource isn't available
        cameraButton.isEnabled = UIImagePickerController.isSourceTypeAvailable(.camera)
        topText.delegate = self
        bottomText.delegate = self
        topText.textAlignment = .center
        bottomText.textAlignment = .center
        topText.defaultTextAttributes = memeTextAttributes
        bottomText.defaultTextAttributes = memeTextAttributes
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        unsubscribeFromKeyboardNotifications()
    }

    // MARK: Delegate Methods

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
            imagePickerView.image = image
            self.dismiss(animated: true, completion: nil)
        }
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        self.dismiss(animated: true, completion: nil)
    }

    func textFieldDidBeginEditing(_ textField: UITextField) {
        textField.text = ""
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        self.topText.resignFirstResponder()
        self.bottomText.resignFirstResponder()
        return true
    }

    // MARK: Move the keyboard up when the bottom textfield is tapped

    @objc func keyboardWillShow(_ notification:Notification) {
        view.frame.origin.y = 0 - getKeyboardHeight(notification)
    }

    func getKeyboardHeight(_ notification:Notification) -> CGFloat {
        let userInfo = notification.userInfo
        let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue // of CGRect
        return keyboardSize.cgRectValue.height
    }

    func subscribeToKeyboardNotifications() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: nil)
    }

    func unsubscribeFromKeyboardNotifications() {
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
    }

    // MARK: Move view down when keyboard is dismissed

    @objc func keyboardWillHide(_ notification: Notification) {
        view.frame.origin.y = 0
    }

    // MARK: IBActions

    @IBAction func pickAnImageFromAlbum(_ sender: Any) {
        let pickerController = UIImagePickerController()
        pickerController.delegate = self
        pickerController.sourceType = .photoLibrary
        present(pickerController, animated: true, completion: nil)
    }

    @IBAction func pickAnImageFromCamera(_ sender: Any) {
        let imagePicker = UIImagePickerController()
        imagePicker.delegate = self
        imagePicker.sourceType = .camera
        present(imagePicker, animated: true, completion: nil)
    }
}
4

There are 4 best solutions below

5
On BEST ANSWER

You can simply try this

  //make a global textField to keep reference
var currentTappedTextField : UITextField?
//use this method to get tapped textField
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
   currentTappedTextField = textField
    return true
}
// now move view only when textfield is bottom
@objc func keyboardWillShow(_ notification:Notification) {
    if(currentTappedTextField == bottomText){
   view.frame.origin.y = 0 - getKeyboardHeight(notification)
    }
}
5
On

Add view in scrollview. Use

pod 'IQKeyboardManagerSwift'

It will automatically handle that. In app delegate write this code :

 IQKeyboardManager.sharedManager().enable = true
 IQKeyboardManager.sharedManager().keyboardDistanceFromTextField = 30.0

If you want for one textfield:

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { 
    if textField == toptextField {
        IQKeyboardManager.sharedManager().enable = false 
    }
    else {
        IQKeyboardManager.sharedManager().enable = true
    } 
    return true
}
0
On

You just need to observe Keyboard Notification in your viewDidLoad :

    override func viewDidLoad() {
        super.viewDidLoad()            
        NotificationCenter.default.addObserver(self,
                                           selector: #selector(keyboardWillShow(_:)),
                                           name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self,
                                           selector: #selector(keyboardWillHide),
                                           name: NSNotification.Name.UIKeyboardWillHide, object: nil)   
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

And declare selector methods to change your view constraint :

@objc
func keyboardWillShow(_ notification: Notification) {
    if let keyboardHeight = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
        yourViewBottomConstraint.constant = keyboardHeight.cgRectValue.height + constantHeight
        UIView.animate(withDuration: 0.25, animations: {
            self.view.layoutIfNeeded()
        })
    }
}

@objc
func keyboardWillHide() {
    yourViewBottomConstraint.constant = constantHeight
    UIView.animate(withDuration: 0.25, animations: {
        self.view.layoutIfNeeded()
    })
}

Just don't forget to implement UITextFieldDelegate :

extension ViewController: UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        self.view.endEditing(true)
        return false
    }
}
0
On

Approach without using external framework:

  1. Use a bottom constraint from the text field to the parent view.
  2. Adjust the constant value based on whether the keyboard is shown or hidden.

Steps:

  1. Create a bottom constraint from your text field to the parent view.
  2. Set the constraint's constant to an initial desired value
  3. Add store the constraint as a property in the view controller
  4. Observe UIKeyboardDidShow notification and get the end frame of the keyboard. Use the negative height of the end frame as the bottom constraint's constant.
  5. Similarly do the same in UIKeyboardWillHide and set the bottom constraint constant to the original constant value