J'ai des problèmes avec mon code. J'essaie de déplacer la UIScrollView
lorsque j'édite une UITextField
qui devrait être masquée par le bruit du clavier.
Je déplace le cadre principal pour le moment car je ne sais pas comment faire défiler le code . J'ai donc fait un peu de code, cela fonctionne bien, mais lorsque je modifie un champ UItextfield, je passe à un autre UITextField
sans appuyer sur le bouton 'retour', la vue principale va très loin waaayyyyy.
J'ai fait un NSLog()
avec mes variables taille, distance et textFieldRect.Origin.y comme vous pouvez le voir ci-dessous. Quand je mets deux UITextField
au même endroit (y Origin) et que je fais ce 'commutateur' particulier (sans appuyer sur retour), je reçois les mêmes numéros, alors que mon code fonctionnait bien pour la première édition UITextField
mais pas pour la deuxième édition.
Regarde ça:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
{
int size;
CGRect textFieldRect = [self.view.window convertRect:textField.bounds fromView:textField];
size = textFieldRect.Origin.y + textFieldRect.size.height;
if (change == FALSE)
{
size = size - distance;
}
if (size < PORTRAIT_KEYBOARD_HEIGHT)
{
distance = 0;
}
else if (size > PORTRAIT_KEYBOARD_HEIGHT)
{
distance = size - PORTRAIT_KEYBOARD_HEIGHT + 5; // +5 px for more visibility
}
NSLog(@"Origin %f", textFieldRect.Origin.y);
NSLog(@"size %d", size);
NSLog(@"distance %d", distance);
CGRect viewFrame = self.view.frame;
viewFrame.Origin.y -= distance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
change = FALSE;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
change = TRUE;
CGRect viewFrame = self.view.frame;
viewFrame.Origin.y += distance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
Des idées ?
La méthode recommandée par Apple consiste à modifier la contentInset
de la UIScrollView
. C'est une solution très élégante, car vous n'avez pas à jouer avec la variable contentSize
. Le code suivant est copié à partir du Guide de programmation du clavier , où le traitement de ce problème est expliqué. Vous devriez y jeter un coup d'oeil.
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.Origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.Origin.y-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
Version Swift:
func registerForKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardAppear(_:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardDisappear(_:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}
// Don't forget to unregister when done
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}
@objc func onKeyboardAppear(_ notification: NSNotification) {
let info = notification.userInfo!
let rect: CGRect = info[UIKeyboardFrameBeginUserInfoKey] as! CGRect
let kbSize = rect.size
let insets = UIEdgeInsetsMake(0, 0, kbSize.height, 0)
scrollView.contentInset = insets
scrollView.scrollIndicatorInsets = insets
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
var aRect = self.view.frame;
aRect.size.height -= kbSize.height;
let activeField: UITextField? = [addressTextView, servicePathTextView, usernameTextView, passwordTextView].first { $0.isFirstResponder }
if let activeField = activeField {
if aRect.contains(activeField.frame.Origin) {
let scrollPoint = CGPoint(x: 0, y: activeField.frame.Origin.y-kbSize.height)
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
}
@objc func onKeyboardDisappear(_ notification: NSNotification) {
scrollView.contentInset = UIEdgeInsets.zero
scrollView.scrollIndicatorInsets = UIEdgeInsets.zero
}
Je viens d'implémenter cela avec Swift 2.0 pour iOS9 sur Xcode 7 (beta 6), ça fonctionne bien ici.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
registerKeyboardNotifications()
}
func registerKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
let userInfo: NSDictionary = notification.userInfo!
let keyboardSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue.size
let contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
var viewRect = view.frame
viewRect.size.height -= keyboardSize.height
if CGRectContainsPoint(viewRect, textField.frame.Origin) {
let scrollPoint = CGPointMake(0, textField.frame.Origin.y - keyboardSize.height)
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
func keyboardWillHide(notification: NSNotification) {
scrollView.contentInset = UIEdgeInsetsZero
scrollView.scrollIndicatorInsets = UIEdgeInsetsZero
}
Édité pour Swift 3
On dirait que vous n'avez besoin que de définir les contentInset
et scrollIndicatorInset
avec Swift 3, le défilement/contentOffset est fait automatiquement.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
registerKeyboardNotifications()
}
func registerKeyboardNotifications() {
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)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
let userInfo: NSDictionary = notification.userInfo! as NSDictionary
let keyboardInfo = userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue
let keyboardSize = keyboardInfo.cgRectValue.size
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
}
func keyboardWillHide(notification: NSNotification) {
scrollView.contentInset = .zero
scrollView.scrollIndicatorInsets = .zero
}
Toutes les réponses ici semblent oublier les possibilités du paysage. Si vous souhaitez que cela fonctionne lorsque vous faites pivoter le périphérique en mode paysage, vous rencontrerez des problèmes.
L'astuce ici est que, même si la vue est consciente de l'orientation, le clavier ne l'est pas. Cela signifie que dans Landscape, la largeur du clavier est en fait la hauteur et inversement.
Pour modifier la méthode recommandée par Apple pour modifier les éléments de contenu et obtenir une orientation en mode paysage, je vous recommande d'utiliser les éléments suivants:
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGSize keyboardSize = [[[notif userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight ) {
CGSize origKeySize = keyboardSize;
keyboardSize.height = origKeySize.width;
keyboardSize.width = origKeySize.height;
}
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
scroller.contentInset = contentInsets;
scroller.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect rect = scroller.frame;
rect.size.height -= keyboardSize.height;
NSLog(@"Rect Size Height: %f", rect.size.height);
if (!CGRectContainsPoint(rect, activeField.frame.Origin)) {
CGPoint point = CGPointMake(0, activeField.frame.Origin.y - keyboardSize.height);
NSLog(@"Point Height: %f", point.y);
[scroller setContentOffset:point animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
La partie à faire attention ici est la suivante:
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGSize keyboardSize = [[[notif userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight ) {
CGSize origKeySize = keyboardSize;
keyboardSize.height = origKeySize.width;
keyboardSize.width = origKeySize.height;
}
Ce qui permet de détecter l'orientation du périphérique. S'il s'agit d'un paysage, il permutera les valeurs de largeur et de hauteur de la variable keyboardSize pour s'assurer que les valeurs correctes sont utilisées dans chaque orientation.
Pour ce genre de choses, pas besoin de beaucoup de codage, c'est très facile comme ci-dessous: -
votre tout textfiled dans UIScrollview de nib comme cette image: -
YourViewController.h
@interface cntrInquiryViewController : UIViewController<UIScrollViewDelegate,UITextFieldDelegate>
{
IBOutlet UITextField *txtName;
IBOutlet UITextField *txtEmail;
IBOutlet UIScrollView *srcScrollView;
}
@end
connecte IBOutlet à partir de nib et connecte chaque délégué de UItextfiled et le délégué scrollview de NIB
-(void)viewWillAppear:(BOOL)animated
{
srcScrollView.contentSize = CGSizeMake(320, 500);
[super viewWillAppear:YES];
}
-(void)textFieldDidBeginEditing:(FMTextField *)textField
{
[srcScrollView setContentOffset:CGPointMake(0,textField.center.y-140) animated:YES];//you can set your y cordinate as your req also
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
[srcScrollView setContentOffset:CGPointMake(0,0) animated:YES];
return YES;
}
NOTEsi le délégué texte n'est pas connecté alors aucune méthode ne fonctionne, veuillez vous assurer que tout iBOulate et délégué sont connectés correctement
Swift 4 Solution:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
registerKeyboardNotifications()
}
func registerKeyboardNotifications() {
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)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
@objc func keyboardWillShow(notification: NSNotification) {
let userInfo: NSDictionary = notification.userInfo! as NSDictionary
let keyboardInfo = userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue
let keyboardSize = keyboardInfo.cgRectValue.size
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
}
@objc func keyboardWillHide(notification: NSNotification) {
scrollView.contentInset = .zero
scrollView.scrollIndicatorInsets = .zero
}
Recommandation d'Apple recodée dans Swift + Utilisation d'UIScrollView avec la disposition automatique dans iOS (en se basant sur les liens suivants: lien 1 , lien 2 , lien 3 ):
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet var t1: UITextField!
@IBOutlet var t2: UITextField!
@IBOutlet var t3: UITextField!
@IBOutlet var t4: UITextField!
@IBOutlet var srcScrollView: UIScrollView!
@IBOutlet var contentView: UIView!
var contentViewCoordinates: CGPoint!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
/* Constraints on content view */
let leftConstraint = NSLayoutConstraint(item:self.contentView,
attribute:NSLayoutAttribute.Leading,
relatedBy:NSLayoutRelation.Equal,
toItem:self.view,
attribute:NSLayoutAttribute.Left,
multiplier:1.0,
constant:0)
self.view.addConstraint(leftConstraint)
let rightConstraint = NSLayoutConstraint(item:self.contentView,
attribute:NSLayoutAttribute.Trailing,
relatedBy:NSLayoutRelation.Equal,
toItem:self.view,
attribute:NSLayoutAttribute.Right,
multiplier:1.0,
constant:0)
self.view.addConstraint(rightConstraint)
/* Tap gesture */
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "hideKeyboard")
// prevents the scroll view from swallowing up the touch event of child buttons
tapGesture.cancelsTouchesInView = false
srcScrollView.addGestureRecognizer(tapGesture)
/* Save content view coordinates */
contentViewCoordinates = contentView.frame.Origin
}
func hideKeyboard() {
t1.resignFirstResponder()
t2.resignFirstResponder()
t3.resignFirstResponder()
t4.resignFirstResponder()
}
var activeField: UITextField?
func textFieldDidBeginEditing(textField: UITextField) {
activeField = textField
}
func textFieldDidEndEditing(textField: UITextField) {
activeField = nil
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let center = NSNotificationCenter.defaultCenter()
center.addObserver(self, selector: "keyboardOnScreen:", name: UIKeyboardDidShowNotification, object: nil)
center.addObserver(self, selector: "keyboardOffScreen:", name: UIKeyboardDidHideNotification, object: nil)
}
func keyboardOnScreen(notification: NSNotification){
// Retrieve the size and top margin (inset is the fancy Word used by Apple)
// of the keyboard displayed.
let info: NSDictionary = notification.userInfo!
let kbSize = info.valueForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue().size
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize!.height, 0.0)
srcScrollView.contentInset = contentInsets
srcScrollView.scrollIndicatorInsets = contentInsets
var aRect: CGRect = self.view.frame
aRect.size.height -= kbSize!.height
//you may not need to scroll, see if the active field is already visible
if (CGRectContainsPoint(aRect, activeField!.frame.Origin) == false) {
let scrollPoint:CGPoint = CGPointMake(0.0, activeField!.frame.Origin.y - kbSize!.height)
srcScrollView.setContentOffset(scrollPoint, animated: true)
}
}
// func keyboardOnScreen(aNotification: NSNotification) {
// let info: NSDictionary = aNotification.userInfo!
// let kbSize = info.valueForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue().size
//
// var bkgndRect: CGRect! = activeField?.superview?.frame
//
// bkgndRect.size.height += kbSize!.height
//
// activeField?.superview?.frame = bkgndRect
//
// srcScrollView.setContentOffset(CGPointMake(0.0, activeField!.frame.Origin.y - kbSize!.height), animated: true)
// }
func keyboardOffScreen(notification: NSNotification){
let contentInsets:UIEdgeInsets = UIEdgeInsetsZero
srcScrollView.contentInset = contentInsets
srcScrollView.scrollIndicatorInsets = contentInsets
self.srcScrollView.setContentOffset(CGPointMake(0, -self.view.frame.Origin.y/2), animated: true)
}
}
La seule chose que je voudrais mettre à jour dans le code Apple est la méthode keyboardWillBeHidden:, pour assurer une transition en douceur.
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
[UIView animateWithDuration:0.4 animations:^{
self.scrollView.contentInset = contentInsets;
}];
self.scrollView.scrollIndicatorInsets = contentInsets;
}
Voici une réponse Swift 3 compatible, qui fonctionnera également avec les contrôleurs de vue dans un contrôleur de navigation - car ils modifieront la propriété contentInset.top
du défilement des vues.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.registerKeyboardNotifications()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.unregisterKeyboardNotifications()
}
func registerKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(LoginViewController.keyboardDidShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(LoginViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func unregisterKeyboardNotifications() {
NotificationCenter.default.removeObserver(self)
}
func keyboardDidShow(notification: NSNotification) {
let userInfo: NSDictionary = notification.userInfo! as NSDictionary
let keyboardInfo = userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue
let keyboardSize = keyboardInfo.cgRectValue.size
// Get the existing contentInset for the scrollView and set the bottom property to be the height of the keyboard
var contentInset = self.scrollView.contentInset
contentInset.bottom = keyboardSize.height
self.scrollView.contentInset = contentInset
self.scrollView.scrollIndicatorInsets = contentInset
}
func keyboardWillHide(notification: NSNotification) {
var contentInset = self.scrollView.contentInset
contentInset.bottom = 0
self.scrollView.contentInset = contentInset
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero
}
J'ai trouvé que les réponses ci-dessus étaient obsolètes. Aussi pas parfait quand défiler.
Voici une version de Swift.
Il défilera juste en dessous du champ textField, sans espace disponible. Et il va restaurer à la façon dont il était, comme sa première apparition.
//add observer
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ARVHttpPlayVC.keyboardDidShow(_:)), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ARVHttpPlayVC.keyboardDidHide(_:)), name: UIKeyboardDidHideNotification, object: nil)
}
func keyboardDidShow(notification: NSNotification) {
let userInfo: NSDictionary = notification.userInfo!
let keyboardSize = userInfo.objectForKey(UIKeyboardFrameEndUserInfoKey)!.CGRectValue.size
let difference = keyboardSize.height - (self.view.frame.height - inputTextField.frame.Origin.y - inputTextField.frame.size.height)
if difference > 0 {
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = difference
self.scrollView.contentInset = contentInset
let scrollPoint = CGPointMake(0, difference)
self.scrollView.setContentOffset(scrollPoint, animated: true)
}
}
func keyboardDidHide(notification: NSNotification) {
let contentInset:UIEdgeInsets = UIEdgeInsetsZero
self.scrollView.contentInset = contentInset
}
//remove observer
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Essayez ce code dans Swift 3:
override func viewDidAppear(_ animated: Bool) {
setupViewResizerOnKeyboardShown()
}
func setupViewResizerOnKeyboardShown() {
NotificationCenter.default.addObserver(self,
selector: #selector(self.keyboardWillShowForResizing),
name: Notification.Name.UIKeyboardWillShow,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(self.keyboardWillHideForResizing),
name: Notification.Name.UIKeyboardWillHide,
object: nil)
}
func keyboardWillShowForResizing(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
let window = self.view.window?.frame {
// We're not just minusing the kb height from the view height because
// the view could already have been resized for the keyboard before
self.view.frame = CGRect(x: self.view.frame.Origin.x,
y: self.view.frame.Origin.y,
width: self.view.frame.width,
height: window.Origin.y + window.height - keyboardSize.height)
} else {
debugPrint("We're showing the keyboard and either the keyboard size or window is nil: panic widely.")
}
}
func keyboardWillHideForResizing(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
let viewHeight = self.view.frame.height
self.view.frame = CGRect(x: self.view.frame.Origin.x,
y: self.view.frame.Origin.y,
width: self.view.frame.width,
height: viewHeight) //viewHeight + keyboardSize.height
} else {
debugPrint("We're about to hide the keyboard and the keyboard size is nil. Now is the rapture.")
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
C'est ce que j'ai utilisé. C'est simple et ça marche bien.
#pragma mark - Scrolling
-(void)scrollElement:(UIView *)view toPoint:(float)y
{
CGRect theFrame = view.frame;
float orig_y = theFrame.Origin.y;
float diff = y - orig_y;
if (diff < 0)
[self scrollToY:diff];
else
[self scrollToY:0];
}
-(void)scrollToY:(float)y
{
[UIView animateWithDuration:0.3f animations:^{
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
self.view.transform = CGAffineTransformMakeTranslation(0, y);
}];
}
Utilisez l'appel de délégué UITextField
textFieldDidBeginEditing:
pour déplacer votre vue vers le haut et ajoutez également un observateur de notification pour rétablir l'affichage normal lorsque le clavier se cache:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
if (self.view.frame.Origin.y == 0)
[self scrollToY:-90.0]; // y can be changed to your liking
}
-(void)keyboardWillHide:(NSNotification*)note
{
[self scrollToY:0];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
Ceci est le code final avec des améliorations dans Swift
//MARK: UITextFieldDelegate
func textFieldDidBeginEditing(textField: UITextField!) { //delegate method
self.textField = textField
}
func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method
textField.resignFirstResponder()
return true
}
//MARK: Keyboard handling
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
unregisterKeyboardNotifications()
}
func registerKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UCProfileSettingsViewController.keyboardDidShow(_:)), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UCProfileSettingsViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
}
func unregisterKeyboardNotifications() {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardDidShow(notification: NSNotification) {
let userInfo: NSDictionary = notification.userInfo!
let keyboardSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue.size
let contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
var viewRect = self.view.frame
viewRect.size.height -= keyboardSize.height
let relativeFieldFrame: CGRect = textField.convertRect(textField.frame, toView: self.view)
if CGRectContainsPoint(viewRect, relativeFieldFrame.Origin) {
let scrollPoint = CGPointMake(0, relativeFieldFrame.Origin.y - keyboardSize.height)
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
func keyboardWillHide(notification: NSNotification) {
scrollView.contentInset = UIEdgeInsetsZero
scrollView.scrollIndicatorInsets = UIEdgeInsetsZero
}
Je ferais ça comme ça. C'est beaucoup de code, mais cela garantit que le champ textField actuellement ciblé est centré verticalement dans l'espace disponible:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSValue *keyBoardEndFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGSize keyboardSize = [keyBoardEndFrame CGRectValue].size;
self.keyboardSize = keyboardSize;
[self adjustScrollViewOffsetToCenterTextField:self.currentTextField];
}
- (void)keyboardWillHide:(NSNotification *)notification {
self.keyboardSize = CGSizeZero;
}
- (IBAction)textFieldGotFocus:(UITextField *)sender {
sender.inputAccessoryView = self.keyboardAccessoryView;
self.currentTextField = sender;
[self adjustScrollViewOffsetToCenterTextField:sender];
}
- (void)adjustScrollViewOffsetToCenterTextField:(UITextField *)textField
{
CGRect textFieldFrame = textField.frame;
float keyboardHeight = MIN(self.keyboardSize.width, self.keyboardSize.height);
float visibleScrollViewHeight = self.scrollView.frame.size.height - keyboardHeight;
float offsetInScrollViewCoords = (visibleScrollViewHeight / 2) - (textFieldFrame.size.height / 2);
float scrollViewOffset = textFieldFrame.Origin.y - offsetInScrollViewCoords;
[UIView animateWithDuration:.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.scrollView.contentOffset = CGPointMake(self.scrollView.contentOffset.x, scrollViewOffset);
}completion:NULL];
}
you'll need these two properties in your @interface...
@property (nonatomic, assign) CGSize keyboardSize;
@property (nonatomic, strong) UITextField *currentTextField;
Notez que l'action - (IBAction)textFieldGotFocus:
est liée à l'état DidBeginEditing
de chaque textField.
En outre, il serait un peu préférable de récupérer la durée de l'animation à partir de la notification au clavier et de l'utiliser pour l'animation à défilement au lieu d'une valeur fixe, mais poursuivez-moi en justice, cela me suffisait;)
Utilisez l'extension suivante si vous ne voulez pas trop calculer:
func scrollSubviewToBeVisible(subview: UIView, animated: Bool) {
let visibleFrame = UIEdgeInsetsInsetRect(self.bounds, self.contentInset)
let subviewFrame = subview.convertRect(subview.bounds, toView: self)
if (!CGRectContainsRect(visibleFrame, subviewFrame)) {
self.scrollRectToVisible(subviewFrame, animated: animated)
}
}
Et peut-être voulez-vous que votre UITextField soit toujours visible:
func textViewDidChange(textView: UITextView) {
self.scrollView?.scrollSubviewToBeVisible(textView, animated: false)
}
Vous pouvez faire défiler en utilisant la propriété contentOffset
dans UIScrollView
, par exemple,
CGPoint offset = scrollview.contentOffset;
offset.y -= KEYBOARD_HEIGHT + 5;
scrollview.contentOffset = offset;
Il existe également une méthode pour faire du défilement animé.
La raison pour laquelle votre deuxième édition ne défile pas correctement peut-être parce que vous semblez supposer qu’un nouveau clavier apparaîtra à chaque démarrage de la modification. Vous pouvez essayer de vérifier si vous avez déjà ajusté la position visible du "clavier" (et de même vérifier la visibilité du clavier avant de la rétablir).
Une meilleure solution pourrait être d'écouter la notification au clavier, par exemple:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
Je sais que c'est une vieille question maintenant mais je pensais que cela pourrait aider les autres. Je voulais quelque chose d'un peu plus facile à mettre en œuvre pour quelques applications que j'avais, alors j'ai fait un cours pour cela. Vous pouvez le télécharger ici si vous voulez: https://github.com/sdernley/iOSTextFieldHandler
C'est aussi simple que de configurer tous les UITextFields pour avoir un délégué de soi
textfieldname.delegate = self;
Et en ajoutant ceci à votre contrôleur de vue avec le nom de votre bouton scrollView et submit
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[iOSTextFieldHandler TextboxKeyboardMover:containingScrollView tf:textField btn:btnSubmit];
}
Ma solution a 4 étapes:
- Étape 1: la fonction écoute lorsque le clavier apparaît
- (void)keyboardWasShown:(NSNotification *)notification {
// Get the size of the keyboard.
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
//top: 64 for navigation bar, 0 for without navigation
UIEdgeInsets contentInsets = UIEdgeInsetsMake(64, 0, keyboardSize.height, 0);
_scrollView.contentInset = contentInsets;
_scrollView.scrollIndicatorInsets = contentInsets;
}
- Étape 2: la fonction écoute lorsque le clavier disparaît
- (void)keyboardWillHide:(NSNotification *)notification {
//top: 64 for navigatiob bar
UIEdgeInsets contentInsets = UIEdgeInsetsMake(64, 0, 0, 0);
[_editScrollView setContentInset: contentInsets];
[_editScrollView setScrollIndicatorInsets: contentInsets];
}
- Étape 3: ajoutez ces fonctions au centre de notification:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- Étape 4: supprimez l'écoute lorsque le contrôleur de vue disparaît
- (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
Swift 4.2 solution qui prend en compte les hauteurs possibles de UIToolbar et UITabBar.
private func setupKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIControl.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIControl.keyboardWillHideNotification, object: nil)
}
@objc func keyboardWillShow(_ notification: Notification) {
let userInfo: NSDictionary = notification.userInfo! as NSDictionary
let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size
let tabbarHeight = tabBarController?.tabBar.frame.size.height ?? 0
let toolbarHeight = navigationController?.toolbar.frame.size.height ?? 0
let bottomInset = keyboardSize.height - tabbarHeight - toolbarHeight
scrollView.contentInset.bottom = bottomInset
scrollView.scrollIndicatorInsets.bottom = bottomInset
}
@objc func keyboardWillHide(_ notification: Notification) {
scrollView.contentInset = .zero
scrollView.scrollIndicatorInsets = .zero
}
Et si vous ciblez <iOS 9, vous devez désenregistrer l’observateur à un moment donné (merci Joe )
En réalité, vous n'avez pas besoin d'un UIScrollView pour le faire. J'ai utilisé ce code et cela fonctionne pour moi:
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if (textField==_myTextField)
{
[self keyBoardAppeared];
}
return true;
}
-(void)textFieldDidEndEditing:(UITextField *)textField {
if (textField==_myTextField)
{
[self keyBoardDisappeared];
}
}
-(void) keyBoardAppeared
{
CGRect frame = self.view.frame;
[UIView animateWithDuration:0.3
delay:0
options: UIViewAnimationCurveEaseOut
animations:^{
self.view.frame = CGRectMake(frame.Origin.x, frame.Origin.y-215, frame.size.width, frame.size.height);
}
completion:^(BOOL finished){
}];
}
-(void) keyBoardDisappeared
{
CGRect frame = self.view.frame;
[UIView animateWithDuration:0.3
delay:0
options: UIViewAnimationCurveEaseOut
animations:^{
self.view.frame = CGRectMake(frame.Origin.x, frame.Origin.y+215, frame.size.width, frame.size.height);
}
completion:^(BOOL finished){
}];
}
L’une des solutions les plus simples consiste à utiliser le protocole suivant:
protocol ScrollViewKeyboardDelegate: class {
var scrollView: UIScrollView? { get set }
func registerKeyboardNotifications()
func unregisterKeyboardNotifications()
}
extension ScrollViewKeyboardDelegate where Self: UIViewController {
func registerKeyboardNotifications() {
NotificationCenter.default.addObserver(
forName: UIResponder.keyboardWillChangeFrameNotification,
object: nil,
queue: nil) { [weak self] notification in
self?.keyboardWillBeShown(notification)
}
NotificationCenter.default.addObserver(
forName: UIResponder.keyboardWillHideNotification,
object: nil,
queue: nil) { [weak self] notification in
self?.keyboardWillBeHidden(notification)
}
}
func unregisterKeyboardNotifications() {
NotificationCenter.default.removeObserver(
self,
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil
)
NotificationCenter.default.removeObserver(
self,
name: UIResponder.keyboardWillHideNotification,
object: nil
)
}
func keyboardWillBeShown(_ notification: Notification) {
let info = notification.userInfo
let key = (info?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)
let aKeyboardSize = key?.cgRectValue
guard let keyboardSize = aKeyboardSize,
let scrollView = self.scrollView else {
return
}
let bottomInset = keyboardSize.height
scrollView.contentInset.bottom = bottomInset
scrollView.scrollIndicatorInsets.bottom = bottomInset
if let activeField = self.view.firstResponder {
let yPosition = activeField.frame.Origin.y - bottomInset
if yPosition > 0 {
let scrollPoint = CGPoint(x: 0, y: yPosition)
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
}
func keyboardWillBeHidden(_ notification: Notification) {
self.scrollView?.contentInset = .zero
self.scrollView?.scrollIndicatorInsets = .zero
}
}
extension UIView {
var firstResponder: UIView? {
guard !isFirstResponder else { return self }
return subviews.first(where: {$0.firstResponder != nil })
}
}
Lorsque vous souhaitez utiliser ce protocole, il vous suffit de vous y conformer et d'affecter votre vue de défilement dans votre contrôleur comme suit:
class MyViewController: UIViewController {
@IBOutlet var scrollViewOutlet: UIScrollView?
var scrollView: UIScrollView?
public override func viewDidLoad() {
super.viewDidLoad()
self.scrollView = self.scrollViewOutlet
self.scrollView?.isScrollEnabled = true
self.registerKeyboardNotifications()
}
extension MyViewController: ScrollViewKeyboardDelegate {}
deinit {
self.unregisterKeyboardNotifications()
}
}
Ce qui suit est ma solution qui fonctionne (5 étapes)
Étape 1: Ajoutez un observateur pour identifier quel UITEXTFIELD ou UITEXTVIEW ShoudBeginEditing (où l’objet est inité ou ViewDidLoad.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updateActiveField:)
name:@"UPDATE_ACTIVE_FIELD" object:nil];
Étape 2: publiez une notification lorsque ..ShouldBeginEditing avec OBJECT de UITEXTFIELD ou UITEXTVIEW
-(BOOL)textViewShouldBeginEditing:(UITextView *)textView {
[[NSNotificationCenter defaultCenter] postNotificationName:@"UPDATE_ACTIVE_FIELD"
object:textView];
return YES;
}
Step3: La méthode qui (Step1 appelle) assigne le UITEXTFIELD ou le UITEXTVIEW actuel
-(void) updateActiveField: (id) sender {
activeField = [sender object];
}
Étape 4: Ajouter l'observateur de clavier UIKeyboardWillShowNotification (même endroit que l'étape 1)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
et méthode:
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
_currentEdgeInsets = self.layoutPanel.contentInset; // store current insets to restore them later
self.layoutPanel.contentInset = contentInsets;
self.layoutPanel.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
CGPoint p = [activeField convertPoint:activeField.bounds.Origin toView:window];
if (!CGRectContainsPoint(aRect, p) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.Origin.y +kbSize.height);
[self.layoutPanel setContentOffset:scrollPoint animated:YES];
self.layoutPanel.scrollEnabled = NO;
}
}
Étape 5: Ajouter l'observateur de clavier UIKeyboardWillHideNotification (même endroit que l'étape 1)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
et méthode:
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
self.layoutPanel.contentInset = _currentEdgeInsets;
self.layoutPanel.scrollIndicatorInsets = _currentEdgeInsets;
self.layoutPanel.scrollEnabled = YES;
}
N'oubliez pas de supprimer les observateurs!
J'ai utilisé cette réponse fournie par Sudheer Palchuri https://stackoverflow.com/users/2873919/sudheer-palchurihttps://stackoverflow.com/a/32583809/6193496
Dans ViewDidLoad, enregistrez les notifications:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DetailsViewController.keyboardWillShow(_:)), name:UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DetailsViewController.keyboardWillHide(_:)), name:UIKeyboardWillHideNotification, object: nil)
Ajoutez ci-dessous les méthodes de l'observateur qui effectuent le défilement automatique lorsque le clavier apparaît.
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func keyboardWillShow(notification:NSNotification){
var userInfo = notification.userInfo!
var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue()
keyboardFrame = self.view.convertRect(keyboardFrame, fromView: nil)
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = keyboardFrame.size.height
self.scrollView.contentInset = contentInset
}
func keyboardWillHide(notification:NSNotification){
var contentInset:UIEdgeInsets = UIEdgeInsetsZero
self.scrollView.contentInset = contentInset
}