我使用Swift与iOS编程,我使用这段代码来移动UITextField,但它不起作用。我正确地调用了函数keyboardWillShow,但是文本字段没有移动。我正在使用自动布局。

override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self);
}

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        //let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)

        var frame = self.ChatField.frame
        frame.origin.y = frame.origin.y - keyboardSize.height + 167
        self.chatField.frame = frame
        println("asdasd")
    }
}

当前回答

最好的方法是使用NotificationCenter来捕捉键盘动作。您可以按照这篇短文中的步骤操作 https://medium.com/@demirciy/keyboard-handling-deb1a96a8207

其他回答

下面是一个简单的解决方案,其中文本字段有一个约束将其绑定到底部布局指南。它只是将键盘高度添加到约束常数中。

// This constraint ties the text field to the bottom layout guide
@IBOutlet var textFieldToBottomLayoutGuideConstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name:UIKeyboardWillHideNotification, object: nil);
}

func keyboardWillShow(sender: NSNotification) {
    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.textFieldToBottomLayoutGuideConstraint?.constant += keyboardSize.height
    }
}

func keyboardWillHide(sender: NSNotification) {
    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.textFieldToBottomLayoutGuideConstraint?.constant -= keyboardSize.height
    }
}

我使用的Swift 4解决方案需要键盘大小。将serverStatusStackView替换为任何你关心的视图,例如:self.view:

deinit {
    NotificationCenter.default.removeObserver(self)
}

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        serverStatusStackView.frame.origin.y = keyboardSize.height * 2 - serverStatusStackView.frame.height
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        serverStatusStackView.frame.origin.y += keyboardSize.height
    }
}

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

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

如果你像我一样尝试了以上所有的解决方案,但你的问题仍然没有解决,我有一个很棒的解决方案,它就像一个魅力。首先我想澄清一下上面提到的一些解决方案。

In my case IQkeyboardmanager was working only when there is no auto layout applied on the elements, if it is applied then IQkeyboard manager will not work the way we think. Same thing with upward movement of self.view. i have wriiten a objective c header with a swift support for pushing UITexfield upward when user clicks on it, solving the problem of keyboard covering the UITextfield : https://github.com/coolvasanth/smart_keyboard. One who has An intermediate or higher level in iOS app development can easily understand the repository and implement it. All the best

Well, I think i might be too late but i found another simple version of Saqib's answer. I'm using Autolayout with constraints. I have a small view inside of another main view with username and password fields. Instead of changing the y coordinate of the view i'm saving the original constraint value in a variable and changing the constraint's constant to some value and again after the keyboard dismisses, i'm setting up the constraint to original one. This way it avoids the problem Saqib's answer has, (The view keeps on moving up and does not stop). Below is my code...

override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
    self.originalConstraint = self.centerYConstraint.constant
  }

  func keyboardWillShow(sender: NSNotification) {
    self.centerYConstraint.constant += 30
  }

  func keyboardWillHide(sender: NSNotification) {
    self.centerYConstraint.constant = self.originalConstraint
  }

斯威夫特4.1,

使用TPKeyBoardAvoiding类来实现这一点。 这与UIScrollView, UICollectionView, UITableView一起工作很好。

只需将这个类分配给storyboard中的scrollview, collectionview或tableview,或以编程方式创建它的对象。 tpkeyboardavoidscrollview内的所有文本字段或文本视图将在键盘出现和消失时自动调整。

这里是tpkeyboardavoidance的链接

tpkeyboardavoidance for Swift 4.1,

import Foundation
import UIKit

// MARK: - TableView
class TPKeyboardAvoidingTableView:UITableView,UITextFieldDelegate, UITextViewDelegate {

    override var frame:CGRect{
        willSet{
            super.frame = frame
        }

        didSet{
            if hasAutomaticKeyboardAvoidingBehaviour() {return}
            TPKeyboardAvoiding_updateContentInset()
        }
    }

    override var contentSize:CGSize{
        willSet(newValue){
            if hasAutomaticKeyboardAvoidingBehaviour() {
                super.contentSize = newValue
                return
            }

            if newValue.equalTo(self.contentSize)
            {
                return
            }

            super.contentSize = newValue
            self.TPKeyboardAvoiding_updateContentInset()
        }

        //        didSet{
        //            self.TPKeyboardAvoiding_updateContentInset()
        //        }
    }


    override init(frame: CGRect, style: UITableViewStyle) {
        super.init(frame: frame, style: style)
        self.setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setup()
    }

    override func awakeFromNib() {
        setup()
    }

    deinit{
        NotificationCenter.default.removeObserver(self)
    }

    func hasAutomaticKeyboardAvoidingBehaviour()->Bool
    {
        if #available(iOS 8.3, *) {
            if self.delegate is UITableViewController
            {
                return true
            }
        }

        return false
    }

    func focusNextTextField()->Bool
    {
        return self.TPKeyboardAvoiding_focusNextTextField()
    }

    @objc func scrollToActiveTextField()
    {
        return self.TPKeyboardAvoiding_scrollToActiveTextField()
    }

    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        if newSuperview != nil {
            NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
        super.touchesEnded(touches, with: event)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if !self.focusNextTextField()
        {
            textField.resignFirstResponder()
        }
        return true
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)

        Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
    }
}

private extension TPKeyboardAvoidingTableView
{
    func setup()
    {
        if self.hasAutomaticKeyboardAvoidingBehaviour() { return }

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
                                               name: NSNotification.Name.UIKeyboardWillChangeFrame,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
                                               name: NSNotification.Name.UIKeyboardWillHide,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextViewTextDidBeginEditing,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextFieldTextDidBeginEditing,
                                               object: nil)
    }
}

// MARK: - CollectionView
class TPKeyboardAvoidingCollectionView:UICollectionView,UITextViewDelegate {

    override var contentSize:CGSize{
        willSet(newValue){
            if newValue.equalTo(self.contentSize)
            {
                return
            }

            super.contentSize = newValue
            self.TPKeyboardAvoiding_updateContentInset()
        }

        //        didSet{
        //            self.TPKeyboardAvoiding_updateContentInset()
        //        }
    }


    override var frame:CGRect{
        willSet{
            super.frame = frame
        }

        didSet{
            self.TPKeyboardAvoiding_updateContentInset()
        }
    }

    //    override init(frame: CGRect) {
    //        super.init(frame: frame)
    //    }

    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        //        fatalError("init(coder:) has not been implemented")
        super.init(coder: aDecoder)
        self.setup()

    }

    override func awakeFromNib() {
        setup()
    }

    deinit{
        NotificationCenter.default.removeObserver(self)
    }

    func focusNextTextField()->Bool
    {
        return self.TPKeyboardAvoiding_focusNextTextField()
    }

    @objc func scrollToActiveTextField()
    {
        return self.TPKeyboardAvoiding_scrollToActiveTextField()
    }

    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        if newSuperview != nil {
            NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
        super.touchesEnded(touches, with: event)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if !self.focusNextTextField()
        {
            textField.resignFirstResponder()
        }
        return true
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)

        Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
    }
}

private extension TPKeyboardAvoidingCollectionView
{
    func setup()
    {
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
                                               name: NSNotification.Name.UIKeyboardWillChangeFrame,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
                                               name: NSNotification.Name.UIKeyboardWillHide,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextViewTextDidBeginEditing,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextFieldTextDidBeginEditing,
                                               object: nil)
    }
}

// MARK: - ScrollView
class TPKeyboardAvoidingScrollView:UIScrollView,UITextFieldDelegate,UITextViewDelegate
{
    override var contentSize:CGSize{
        didSet{
            self.TPKeyboardAvoiding_updateFromContentSizeChange()
        }
    }


    override var frame:CGRect{
        didSet{
            self.TPKeyboardAvoiding_updateContentInset()
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setup()
    }

    override func awakeFromNib() {
        setup()
    }

    func contentSizeToFit()
    {
        self.contentSize = self.TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames()
    }

    func focusNextTextField() ->Bool
    {
        return self.TPKeyboardAvoiding_focusNextTextField()
    }

    @objc func scrollToActiveTextField()
    {
        return self.TPKeyboardAvoiding_scrollToActiveTextField()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setup()
    }

    deinit{
        NotificationCenter.default.removeObserver(self)
    }

    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        if newSuperview != nil {
            NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
        super.touchesEnded(touches, with: event)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if !self.focusNextTextField()
        {
            textField.resignFirstResponder()
        }
        return true
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)

        Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
    }
}

private extension TPKeyboardAvoidingScrollView
{
    func setup()
    {
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
                                               name: NSNotification.Name.UIKeyboardWillChangeFrame,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
                                               name: NSNotification.Name.UIKeyboardWillHide,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextViewTextDidBeginEditing,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextFieldTextDidBeginEditing,
                                               object: nil)
    }
}

// MARK: - Process Event
let kCalculatedContentPadding:CGFloat = 10;
let kMinimumScrollOffsetPadding:CGFloat = 20;

extension UIScrollView
{
    @objc func TPKeyboardAvoiding_keyboardWillShow(_ notification:Notification)
    {
        guard let userInfo = notification.userInfo else { return }
        guard let rectNotification = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else
        {
            return
        }

        let keyboardRect = self.convert(rectNotification.cgRectValue , from: nil)
        if keyboardRect.isEmpty
        {
            return
        }

        let state = self.keyboardAvoidingState()

        guard let firstResponder = self.TPKeyboardAvoiding_findFirstResponderBeneathView(self) else { return}
        state.keyboardRect = keyboardRect
        if !state.keyboardVisible
        {
            state.priorInset = self.contentInset
            state.priorScrollIndicatorInsets = self.scrollIndicatorInsets
            state.priorPagingEnabled = self.isPagingEnabled
        }

        state.keyboardVisible = true
        self.isPagingEnabled = false

        if self is TPKeyboardAvoidingScrollView
        {
            state.priorContentSize = self.contentSize
            if self.contentSize.equalTo(CGSize.zero)
            {
                self.contentSize = self.TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames()
            }
        }

        let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Float ?? 0.0
        let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int ?? 0
        let options = UIViewAnimationOptions(rawValue: UInt(curve))

        UIView.animate(withDuration: TimeInterval(duration),
                       delay: 0,
                       options: options,
                       animations: { [weak self]() -> Void in
                        if let actualSelf = self
                        {
                            actualSelf.contentInset = actualSelf.TPKeyboardAvoiding_contentInsetForKeyboard()
                            let viewableHeight = actualSelf.bounds.size.height - actualSelf.contentInset.top - actualSelf.contentInset.bottom
                            let point = CGPoint(x: actualSelf.contentOffset.x, y: actualSelf.TPKeyboardAvoiding_idealOffsetForView(firstResponder, viewAreaHeight: viewableHeight))
                            actualSelf.setContentOffset(point, animated: false)

                            actualSelf.scrollIndicatorInsets = actualSelf.contentInset
                            actualSelf.layoutIfNeeded()
                        }

        }) { (finished) -> Void in

        }
    }

    @objc func TPKeyboardAvoiding_keyboardWillHide(_ notification:Notification)
    {
        guard let userInfo = notification.userInfo else { return }

        guard let rectNotification = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else
        {
            return
        }
        let keyboardRect = self.convert(rectNotification.cgRectValue , from: nil)
        if keyboardRect.isEmpty
        {
            return
        }
        let state = self.keyboardAvoidingState()

        if !state.keyboardVisible
        {
            return
        }
        state.keyboardRect = CGRect.zero
        state.keyboardVisible = false

        let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Float ?? 0.0
        let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int ?? 0
        let options = UIViewAnimationOptions(rawValue: UInt(curve))

        UIView.animate(withDuration: TimeInterval(duration),
                       delay: 0,
                       options: options,
                       animations: { [weak self]() -> Void in
                        if let actualSelf = self
                        {
                            if actualSelf is TPKeyboardAvoidingScrollView {
                                actualSelf.contentSize = state.priorContentSize
                                actualSelf.contentInset = state.priorInset
                                actualSelf.scrollIndicatorInsets = state.priorScrollIndicatorInsets
                                actualSelf.isPagingEnabled = state.priorPagingEnabled
                                actualSelf.layoutIfNeeded()
                            }
                        }

        }) { (finished) -> Void in

        }
    }

    func TPKeyboardAvoiding_updateFromContentSizeChange()
    {
        let state = self.keyboardAvoidingState()
        if state.keyboardVisible
        {
            state.priorContentSize = self.contentSize
        }
    }

    func TPKeyboardAvoiding_focusNextTextField() ->Bool
    {
        guard let firstResponder = self.TPKeyboardAvoiding_findFirstResponderBeneathView(self) else { return false}
        guard let view = self.TPKeyboardAvoiding_findNextInputViewAfterView(firstResponder, beneathView: self) else { return false}
        Timer.scheduledTimer(timeInterval: 0.1, target: view, selector: #selector(becomeFirstResponder), userInfo: nil, repeats: false)

        return true

    }

    func TPKeyboardAvoiding_scrollToActiveTextField()
    {
        let state = self.keyboardAvoidingState()

        if !state.keyboardVisible { return }

        let visibleSpace = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom

        let idealOffset = CGPoint(x: 0,
                                  y: self.TPKeyboardAvoiding_idealOffsetForView(self.TPKeyboardAvoiding_findFirstResponderBeneathView(self),
                                                                                viewAreaHeight: visibleSpace))

        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double((Int64)(0 * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)) {[weak self] () -> Void in
            self?.setContentOffset(idealOffset, animated: true)
        }
    }

    //Helper
    func TPKeyboardAvoiding_findFirstResponderBeneathView(_ view:UIView) -> UIView?
    {
        for childView in view.subviews
        {
            if childView.responds(to: #selector(getter: isFirstResponder)) && childView.isFirstResponder
            {
                return childView
            }
            let result = TPKeyboardAvoiding_findFirstResponderBeneathView(childView)
            if result != nil
            {
                return result
            }
        }
        return nil
    }

    func TPKeyboardAvoiding_updateContentInset()
    {
        let state = self.keyboardAvoidingState()
        if state.keyboardVisible
        {
            self.contentInset = self.TPKeyboardAvoiding_contentInsetForKeyboard()
        }
    }

    func TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames() ->CGSize
    {
        let wasShowingVerticalScrollIndicator = self.showsVerticalScrollIndicator
        let wasShowingHorizontalScrollIndicator = self.showsHorizontalScrollIndicator

        self.showsVerticalScrollIndicator = false
        self.showsHorizontalScrollIndicator = false

        var rect = CGRect.zero

        for view in self.subviews
        {
            rect = rect.union(view.frame)
        }

        rect.size.height += kCalculatedContentPadding
        self.showsVerticalScrollIndicator = wasShowingVerticalScrollIndicator
        self.showsHorizontalScrollIndicator = wasShowingHorizontalScrollIndicator

        return rect.size
    }

    func TPKeyboardAvoiding_idealOffsetForView(_ view:UIView?,viewAreaHeight:CGFloat) -> CGFloat
    {
        let contentSize = self.contentSize

        var offset:CGFloat = 0.0
        let subviewRect =  view != nil ? view!.convert(view!.bounds, to: self) : CGRect.zero

        var padding = (viewAreaHeight - subviewRect.height)/2
        if padding < kMinimumScrollOffsetPadding
        {
            padding = kMinimumScrollOffsetPadding
        }

        offset = subviewRect.origin.y - padding - self.contentInset.top

        if offset > (contentSize.height - viewAreaHeight)
        {
            offset = contentSize.height - viewAreaHeight
        }

        if offset < -self.contentInset.top
        {
            offset = -self.contentInset.top
        }

        return offset
    }

    func TPKeyboardAvoiding_contentInsetForKeyboard() -> UIEdgeInsets
    {
        let state = self.keyboardAvoidingState()
        var newInset = self.contentInset;

        let keyboardRect = state.keyboardRect
        newInset.bottom = keyboardRect.size.height - max(keyboardRect.maxY - self.bounds.maxY, 0)

        return newInset

    }

    func TPKeyboardAvoiding_viewIsValidKeyViewCandidate(_ view:UIView)->Bool
    {
        if view.isHidden || !view.isUserInteractionEnabled {return false}

        if view is UITextField
        {
            if (view as! UITextField).isEnabled {return true}
        }

        if view is UITextView
        {
            if (view as! UITextView).isEditable {return true}
        }

        return false
    }

    func TPKeyboardAvoiding_findNextInputViewAfterView(_ priorView:UIView,beneathView view:UIView, candidateView bestCandidate: inout UIView?)
    {
        let priorFrame = self.convert(priorView.frame, to: priorView.superview)
        let candidateFrame = bestCandidate == nil ? CGRect.zero : self.convert(bestCandidate!.frame, to: bestCandidate!.superview)

        var bestCandidateHeuristic = -sqrt(candidateFrame.origin.x*candidateFrame.origin.x + candidateFrame.origin.y*candidateFrame.origin.y) + ( Float(fabs(candidateFrame.minY - priorFrame.minY))<Float.ulpOfOne ? 1e6 : 0)

        for childView in view.subviews
        {
            if TPKeyboardAvoiding_viewIsValidKeyViewCandidate(childView)
            {
                let frame = self.convert(childView.frame, to: view)
                let heuristic = -sqrt(frame.origin.x*frame.origin.x + frame.origin.y*frame.origin.y)
                    + (Float(fabs(frame.minY - priorFrame.minY)) < Float.ulpOfOne ? 1e6 : 0)

                if childView != priorView && (Float(fabs(frame.minY - priorFrame.minY)) < Float.ulpOfOne
                    && frame.minX > priorFrame.minX
                    || frame.minY > priorFrame.minY)
                    && (bestCandidate == nil || heuristic > bestCandidateHeuristic)
                {
                    bestCandidate = childView
                    bestCandidateHeuristic = heuristic
                }
            }else
            {
                self.TPKeyboardAvoiding_findNextInputViewAfterView(priorView, beneathView: view, candidateView: &bestCandidate)
            }
        }
    }

    func TPKeyboardAvoiding_findNextInputViewAfterView(_ priorView:UIView,beneathView view:UIView) ->UIView?
    {
        var candidate:UIView?
        self.TPKeyboardAvoiding_findNextInputViewAfterView(priorView, beneathView: view, candidateView: &candidate)
        return candidate
    }


    @objc func TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_ obj: AnyObject)
    {
        func processWithView(_ view: UIView) {
            for childView in view.subviews
            {
                if childView is UITextField || childView is UITextView
                {
                    self.TPKeyboardAvoiding_initializeView(childView)
                }else
                {
                    self.TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(childView)
                }
            }
        }

        if let timer = obj as? Timer, let view = timer.userInfo as? UIView {
            processWithView(view)
        }
        else if let view = obj as? UIView {
            processWithView(view)
        }
    }

    func TPKeyboardAvoiding_initializeView(_ view:UIView)
    {
        if let textField = view as? UITextField,
            let delegate = self as? UITextFieldDelegate, textField.returnKeyType == UIReturnKeyType.default &&
            textField.delegate !== delegate
        {
            textField.delegate = delegate
            let otherView = self.TPKeyboardAvoiding_findNextInputViewAfterView(view, beneathView: self)
            textField.returnKeyType = otherView != nil ? .next : .done

        }
    }

    func keyboardAvoidingState()->TPKeyboardAvoidingState
    {
        var state = objc_getAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName) as? TPKeyboardAvoidingState
        if state == nil
        {
            state = TPKeyboardAvoidingState()
            self.state = state
        }

        return self.state!
    }

}

// MARK: - Internal object observer
internal class TPKeyboardAvoidingState:NSObject
{
    var priorInset = UIEdgeInsets.zero
    var priorScrollIndicatorInsets = UIEdgeInsets.zero

    var keyboardVisible = false
    var keyboardRect = CGRect.zero
    var priorContentSize = CGSize.zero

    var priorPagingEnabled = false
}

internal extension UIScrollView
{
    fileprivate struct AssociatedKeysKeyboard {
        static var DescriptiveName = "KeyBoard_DescriptiveName"
    }

    var state:TPKeyboardAvoidingState?{
        get{
            let optionalObject:AnyObject? = objc_getAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName) as AnyObject?
            if let object:AnyObject = optionalObject {
                return object as? TPKeyboardAvoidingState
            } else {
                return nil
            }
        }
        set{
            objc_setAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}