2017-08-16 5 views
0

J'ai un UIScrollView et un UITextView, comme dans toute messagerie/application de chat, quand UIScrollView est défiler vers le bas, le clavier interactif traîné trop.Detect la hauteur du clavier pendant UIScrollView défile vers le bas et le clavier est d'être interactive traîné

J'ai besoin de détecter la hauteur du clavier tandis que UIScrollView est défilée, j'ai essayé UIKeyboardWillChangeFrame observateur, mais cet événement est appelé après le robinet de défilement est libéré. Sans connaître la hauteur du clavier, je ne parviens pas à mettre à jour la contrainte de bas UITextView, et j'ai un décalage entre le clavier et la vue du bas @screenshot.

enter image description here

également la fixation de capture d'écran Viber, qui aligne la barre inférieure lorsque le clavier étant tiré de la barre de défilement, peut également être vu dans WhatsApp aussi.

enter image description here

Répondre

1

Comme d'iOS 10, Apple ne fournit pas un observateur NSNotification pour détecter le changement d'image lorsque le clavier est entraîné de manière interactive par UIScrollView, UIKeyboardWillChangeFrame et UIKeyboardDidChangeFrame sont observés une seule fois la libération du robinet.

Quoi qu'il en soit, après avoir regardé autour DAKeyboardControl bibliothèque, j'ai eu l'idée d'attacher UIScrollView.UIPanGestureRecognizer dans le UIViewController, donc des événements de mouvement qui sont produits seront traitées dans UIViewController aussi bien. Après avoir vissé plusieurs heures, je l'ai eu pour travailler, voici tout le code qui est nécessaire pour cela:

class ViewController: UIViewController, UIGestureRecognizerDelegate { 

    fileprivate let collectionView = UICollectionView(frame: .zero) 
    private let bottomView = UIView() 
    fileprivate var bottomInset: NSLayoutConstraint! 

    // This holds height of keypad 
    private var maxKeypadHeight: CGFloat = 0 { 
     didSet { 
      self.updateCollectionViewInsets(maxKeypadHeight + self.bottomView.frame.height) 
      self.bottomInset.constant = -maxKeypadHeight 
     } 
    } 

    private var isListeningKeypadChange = false 

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

     NotificationCenter.default.addObserver(self, selector: #selector(keypadWillChange(_:)), name: .UIKeyboardWillChangeFrame, object: nil) 
     NotificationCenter.default.addObserver(self, selector: #selector(keypadWillShow(_:)), name: .UIKeyboardWillShow, object: nil) 
     NotificationCenter.default.addObserver(self, selector: #selector(keypadWillHide(_:)), name: .UIKeyboardWillHide, object: nil) 
     NotificationCenter.default.addObserver(self, selector: #selector(keypadDidHide), name: .UIKeyboardDidHide, object: nil) 
    } 

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

     NotificationCenter.default.removeObserver(self) 
    } 

    func keypadWillShow(_ notification: Notification) { 
     guard !self.isListeningKeypadChange, let userInfo = notification.userInfo as? [String : Any], 
      let animationDuration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? TimeInterval, 
      let animationCurve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt, 
      let value = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue 
      else { 
       return 
     } 

     self.maxKeypadHeight = value.cgRectValue.height 

     let options = UIViewAnimationOptions.beginFromCurrentState.union(UIViewAnimationOptions(rawValue: animationCurve)) 
     UIView.animate(withDuration: animationDuration, delay: 0, options: options, animations: { [weak self] in 
      self?.view.layoutIfNeeded() 
      }, completion: { finished in 
       guard finished else { return } 

       // Some delay of about 500MS, before ready to listen other keypad events 
       DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in 
        self?.beginListeningKeypadChange() 
       } 
     }) 
    } 

    func handlePanGestureRecognizer(_ pan: UIPanGestureRecognizer) { 
     guard self.isListeningKeypadChange, let windowHeight = self.view.window?.frame.height else { return } 

     let barHeight = self.bottomView.frame.height 
     let keypadHeight = abs(self.bottomInset.constant) 
     let usedHeight = keypadHeight + barHeight 

     let dragY = windowHeight - pan.location(in: self.view.window).y 
     let newValue = min(dragY < usedHeight ? max(dragY, 0) : dragY, self.maxKeypadHeight) 

     print("Old: \(keypadHeight)  New: \(newValue)  Drag: \(dragY)  Used: \(usedHeight)") 
     guard keypadHeight != newValue else { return } 
     self.updateCollectionViewInsets(newValue + barHeight) 
     self.bottomInset.constant = -newValue 
    } 

    func keypadWillChange(_ notification: Notification) { 
     if self.isListeningKeypadChange, let value = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue { 
      self.maxKeypadHeight = value.cgRectValue.height 
     } 
    } 

    func keypadWillHide(_ notification: Notification) { 
     guard let userInfo = notification.userInfo as? [String : Any] else { return } 

     self.maxKeypadHeight = 0 

     var options = UIViewAnimationOptions.beginFromCurrentState 
     if let animationCurve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt { 
      options = options.union(UIViewAnimationOptions(rawValue: animationCurve)) 
     } 

     let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? TimeInterval 
     UIView.animate(withDuration: duration ?? 0, delay: 0, options: options, animations: { 
      self.view.layoutIfNeeded() 
     }, completion: nil) 
    } 

    func keypadDidHide() { 
     self.collectionView.panGestureRecognizer.removeTarget(self, action: nil) 
     self.isListeningKeypadChange = false 
     if (self.maxKeypadHeight != 0 || self.bottomInset.constant != 0) { 
      self.maxKeypadHeight = 0 
     } 
    } 

    private func beginListeningKeypadChange() { 
     self.isListeningKeypadChange = true 
     self.collectionView.panGestureRecognizer.addTarget(self, action: #selector(self.handlePanGestureRecognizer(_:))) 
    } 

    fileprivate func updateCollectionViewInsets(_ value: CGFloat) { 
     let insets = UIEdgeInsets(top: 0, left: 0, bottom: value + 8, right: 0) 
     self.collectionView.contentInset = insets 
     self.collectionView.scrollIndicatorInsets = insets 
    }   
} 
0

rapide 3.0

Vous pouvez essayer cette façon, je l'ai mis en œuvre dans mon projet. J'espère que cela vous aidera.

@IBOutlet weak var constant_ViewBottom: NSLayoutConstraint! // 0 

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

    override func viewWillDisappear(_ animated: Bool) { 
     super.viewWillDisappear(animated) 
     NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) 
     NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) 
    } 

func keyboardWillShow(_ notification: NSNotification){ 
     if let keyboardRectValue = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size { 
      let keyboardHeight = keyboardRectValue.height 
      print("keyboardHeight:=\(keyboardHeight)") 
      constant_ViewBottom.constant = keyboardHeight 
      self.view.layoutIfNeeded() 
     } 
    } 
func keyboardWillHide(_ notification: NSNotification){ 
     constant_ViewBottom.constant = 0.0 
     self.view.layoutIfNeeded() 
    } 
0

Il semble que vous ayez une mauvaise constante de contrainte de bas. Essayez de réinitialiser à chaque fois que la contrainte de fond et définir une nouvelle valeur de hauteur

func keyboardDidChangeFrame(notification: Notification) { 
     if let userInfo = notification.userInfo { 
      if let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { 
       let duration: TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0 
       let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber 
       let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions().rawValue 
       let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw) 
       if endFrame.origin.y >= UIScreen.main.bounds.size.height { 
        self.inputBarBottomSpacing.constant = 0 
       } else { 
        //the most important logic branch, reset current bottom constant constraint value 
        if self.inputBarBottomSpacing.constant != 0 { 
         self.inputBarBottomSpacing.constant = 0 
        } 
        self.inputBarBottomSpacing.constant = -endFrame.size.height 
       } 
       UIView.animate(withDuration: duration, delay: TimeInterval(0), options: animationCurve, animations: { 
        self.view.layoutIfNeeded() 
       }, completion: nil) 
      } 
     } 
    } 
0

Vous pouvez simplement ajouter ce pod:

pod 'IQKeyboardManagerSwift' 

Puis dans votre AppDelegate.swift ajouter:

import IQKeyboardManagerSwift 

Et un dans la fonction didFinishLaunchingWithOptions

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 
    // Override point for customization after application launch. 

    IQKeyboardManager.sharedManager().enable = true // ADD THIS !!! 

    return true 
} 

Cela est simple.