How to make textFields stay in place when keyboard moves up? Swift

后端 未结 2 1123
失恋的感觉
失恋的感觉 2021-01-07 09:45

I have created a form with 4 fields and one button. The view hierarchy looks like this: Main UIVIew, View (renamed contentView), on top of contentView I have 4 fields and 1

相关标签:
2条回答
  • 2021-01-07 10:11

    I think you should only move the fields up if they are covered by the keyboard, something like what i cooked up a couple of days ago:

    let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue ?? NSValue()).cgRectValue
    let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
    UIView.animate(withDuration: 0.2) {
        if notification.name == Notification.Name.UIKeyboardWillHide {
            self.view.frame = CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height)
        } else {
            let offset = (self.view.frame.size.height - self.activeField.frame.maxY) - keyboardViewEndFrame.height
            if offset < 0 {
                self.view.frame = CGRect(x: 0, y:  offset, width: self.view.width, height: self.view.height)
            } else {
                self.view.frame = CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height)
            }
        }
    }
    

    Basically you just need to add logic for keyboard handling timing, and you should handle it if keyboard frame goes over textfield frame. Hope it helps.

    0 讨论(0)
  • 2021-01-07 10:20

    So I think you are close and I am not sure if it is a code issue or an autolayout issue. My guess is you are getting complaints about your scrollview not know the content size so I will cover both.

    EDIT: Also you button will have to be below the view container that I first add and will need to be handled separately from the scrollview. Do not put it in the scrollview.

    The methods work below except add 50 or whatever to the layout bottom for the view that will hold the scrollview. Also the method below will help make edits

    Autolayout: First for forms that will only take up on page I like to start by adding a view to the storyboard and pin in top layout guide, (whatever space you need for button), left and right. Then I add my ScrollView(pin the scrollview to that view) to the view just added.Then next I add my content view to the scrollview. Now I pin this to the scrollview. You will see that autolayout is still not happy. So why the first view and how to fix this. I drag from the contentView to the view holding the scrollview and choose equal heights and equal widths. Now you will not have auto layout screaming at you. Note:This works for content you want to fill the first view size but allow it to scroll to avoid the keyboard. See images

    After adding this equal heights I can continue with the storyboard. I set the textfields up. The bottom textfield you may or may not want to pin it to the bottom but if you do make it >= yourNumber.

    EDIT: Now add your NEXT Button to the storyboard below the view that is holding everything. The button has to be pinned to the bottom of the main view with a 0 to stick to the keyboard. It will now look like this.

    Obviously this conflicts slightly with the initial images but all you have to do is increase the space to the bottom layout guide just make sure your button is added to the main view and not the view holding the scrollview. Now connect your button to the controller in an iboutlet. we will need it.

    Next make sure you have the right keyboard in your simulator. **Not using hardware keyboard

    Finally the code. some of it you would need to substitute your textfield variables as I looped through the subviews to set the delegate. I also added padding to the scroll up. You should move your deRegister to deint(). See my code and finally you might want to scroll the scroll view on keyboard will appear instead of did appear but I did not alter this.

    import UIKit
    
    class ViewController: UIViewController,UITextFieldDelegate {
    
    //added in storyboard. removed the code
    @IBOutlet weak var nextButton: UIButton!
    
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var contentView: UIView!
        override func viewDidLoad() {
            super.viewDidLoad()
            //called whenever keyboard is shown/hidden
    
            registerForKeyboardNotifications()
    
            //when identifies single or multiple taps, call DismissKeyboard
            var tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "DismissKeyboard")
            contentView.addGestureRecognizer(tap)
    
            //disable scroll bouncing
            scrollView.bounces = false
    
            //replace with your textfields
            for subs in self.contentView.subviews{
                if subs is UITextField{
                    print("setting")
                    (subs as! UITextField).delegate = self
                }
            }
        }
    
    
        //Call this function when the tap is recognized.
        func DismissKeyboard(){
            contentView.endEditing(true)
        }
    
    
    
        // Stop Editing on Return Key Tap.
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            textField.resignFirstResponder()
            return true
        }
    
        //edited for next button
        weak var activeField: UITextField?
        func keyboardDidShow(_ notification: Notification) {
    
            //when a textfield is edited lift the button above the keyboard
            if let activeField = self.activeField,let keyboardSize =
                (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as?
                    NSValue)?.cgRectValue {
    
                //20 in insets and offset is just padding
                let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom:
                    keyboardSize.height + 20 + nextButton.bounds.height, right: 0.0)
                self.scrollView.contentInset = contentInsets
    
                var aRect = self.view.frame
                aRect.size.height -= keyboardSize.height
    
    
                let bottomPoint = CGPoint(x: activeField.frame.origin.x, y:activeField.frame.origin.y)
    
                if aRect.contains(bottomPoint){
                    let scrollPoint = CGPoint(x: 0.0, y: bottomPoint.y - keyboardSize.height - 20 - nextButton.bounds.height)
                    scrollView.setContentOffset(scrollPoint, animated: true)
                }
    
            }
    
        }
            func keyboardWillHide(_ notification: Notification) {
    
                let contentInsets = UIEdgeInsets.zero
                self.scrollView.contentInset = contentInsets
                self.scrollView.scrollIndicatorInsets = contentInsets
            }
    
            //Keep track of which textfield is being edited to make sure the field is visible when keyboard pops up
            func textFieldDidBeginEditing(_ textField: UITextField) {
                self.activeField = textField
            }
    
    
            func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
                self.activeField = nil
            }
    
            //register for keyboard notifications
            func registerForKeyboardNotifications() {
    
                NotificationCenter.default.addObserver(self, selector:
                    #selector(keyboardDidShow),
                                                       name: NSNotification.Name.UIKeyboardDidShow, object: nil)
    
                NotificationCenter.default.addObserver(self, selector:
                    #selector(keyboardWillHide), name:
                    NSNotification.Name.UIKeyboardWillHide, object: nil)
            }
    
            //remove keyBoard observers
            func deregisterFromKeyboardNotifications() {
                NotificationCenter.default.removeObserver(self, name: 
                    NSNotification.Name.UIKeyboardDidShow, object: nil)
    
                NotificationCenter.default.removeObserver(self, name: 
                    NSNotification.Name.UIKeyboardWillHide, object: nil)
            }
    
    
    
            deinit {
                //deregister keyboard notifications
                deregisterFromKeyboardNotifications()
            }
    } //end of class
    

    Now one more step. We have to handle the move up of the button. Instead of just killing this controller and putting more handling in it you can subclass the bottomconstraint to handle it. (Just make sure not to add a top constraint to the bottom.) Here is a constraint to drop in your project.

    import UIKit
    
    class AvoidingConstraint: NSLayoutConstraint {
    
        private var offset : CGFloat = 0
        private var keyboardVisibleHeight : CGFloat = 0
    
        override public func awakeFromNib() {
            super.awakeFromNib()
    
            offset = constant
    
            NotificationCenter.default.addObserver(self, selector: #selector(AvoidingConstraint.keyboardWillShowNotification(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(AvoidingConstraint.keyboardWillHideNotification(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
        }
    
        deinit {
            NotificationCenter.default.removeObserver(self)
        }
    
        // MARK: Notification
    
        func keyboardWillShowNotification(_ notification: Notification) {
            if let userInfo = notification.userInfo {
                if let frameValue = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue {
                    let frame = frameValue.cgRectValue
                    keyboardVisibleHeight = frame.size.height
                }
    
                self.updateConstant()
                switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber) {
                case let (.some(duration), .some(curve)):
    
                    let options = UIViewAnimationOptions(rawValue: curve.uintValue)
    
                    UIView.animate(
                        withDuration: TimeInterval(duration.doubleValue),
                        delay: 0,
                        options: options,
                        animations: {
                            UIApplication.shared.keyWindow?.layoutIfNeeded()
                            return
                    }, completion: { finished in
                    })
                default:
    
                    break
                }
    
            }
    
        }
    
        func keyboardWillHideNotification(_ notification: NSNotification) {
            keyboardVisibleHeight = 0
            self.updateConstant()
    
            if let userInfo = notification.userInfo {
    
                switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber) {
                case let (.some(duration), .some(curve)):
    
                    let options = UIViewAnimationOptions(rawValue: curve.uintValue)
    
                    UIView.animate(
                        withDuration: TimeInterval(duration.doubleValue),
                        delay: 0,
                        options: options,
                        animations: {
                            UIApplication.shared.keyWindow?.layoutIfNeeded()
                            return
                    }, completion: { finished in
                    })
                default:
                    break
                }
            }
        }
    
        func updateConstant() {
            self.constant = offset + keyboardVisibleHeight
        }
    
    }
    

    Add this to a file in your project. Then all you have to do is on the bottom constraint for your button in story board change it to this subclass. Just make sure there is no top constraint. The view holding the scrollview needs to have a bottom constraint to the main view and not the button with enough space for the button. Run the project and enjoy. See link for test project if this explanation is not enough. https://www.dropbox.com/s/ir5x324mvhhne64/ScrollView.zip?dl=0

    0 讨论(0)
提交回复
热议问题