Как я могу прокрутить UIScrollView, когда появляется клавиатура?
у меня возникли проблемы с моим код. Я пытаюсь переместить UIScrollView
когда я редактирую UITextField
это должно быть скрыто с помощью клавиатуры поп.
я перемещаю основной кадр прямо сейчас, потому что я не знаю, как "прокрутить" в коде.
Итак, я сделал немного кода, он работает нормально, но когда я редактирую UItextfield и переключаюсь на другой UITextField
без нажатия на кнопку "возврат" основной вид идет waaayyyyy далеко вверх.
Я NSLog()
С моей переменные размер, расстояние и textFieldRect.происхождение.г Как вы можете видеть ниже. Когда я ставлю два UITextField
в том же месте (y origin), и я делаю этот конкретный "переключатель" (без нажатия return), я получаю те же номера, тогда как мой код отлично работал для первого UITextField
редактирования, но не для второго редактирования.
зацени вот это:
- (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];
}
какие идеи ?
21 ответ:
рекомендуемый способ от Apple, чтобы изменить
contentInset
наUIScrollView
. Это очень элегантное решение, потому что вам не придется возиться сcontentSize
. Следующий код копируется из Руководство По Программированию Клавиатуры, где объясняется обработка этой проблемы. Вы должны взглянуть на это.// 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; }
Я только что реализовал это с Swift 2.0 для iOS9 на Xcode 7 (beta 6), отлично работает здесь.
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 }
отредактировано для Swift 3
Похоже, вам нужно только установить
contentInset
иscrollIndicatorInset
С помощью Swift 3 прокрутка / contentOffset выполняется автоматически..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 }
все ответы здесь, кажется, забывают о ландшафтном возможностей. Если вы хотите, чтобы это работало, когда устройство повернуто к ландшафтному виду, то вы столкнетесь с проблемами.
трюк здесь заключается в том, что, хотя вид знает об ориентации, клавиатура не является. Это означает, что в ландшафте ширина клавиатуры на самом деле является ее высотой и наоборот.
для изменения яблок рекомендуется способ изменения содержимого вставки и получить его поддержку ландшафта ориентация, я бы рекомендовал использовать следующее:
// 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; }
часть, на которую следует обратить внимание, заключается в следующем:
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; }
что делает, это определяет, в какой ориентации находится устройство. Если это пейзаж, он будет "менять местами" значения ширины и высоты переменной keyboardSize, чтобы гарантировать, что правильные значения используются в каждой ориентации.
для этого материала не нужно много кодирования очень легко, как показано ниже код:-
все ваши текстовые файлы в UIScrollview из nib, как это изображение: -
YourViewController.h
@interface cntrInquiryViewController : UIViewController<UIScrollViewDelegate,UITextFieldDelegate> { IBOutlet UITextField *txtName; IBOutlet UITextField *txtEmail; IBOutlet UIScrollView *srcScrollView; } @end
подключите IBOutlet от nib, а также подключите каждый делегат uitextfiled и ScrollView делегата от 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; }
Примечание если текстовый делегат не подключен, то ни один метод не работает, пожалуйста, убедитесь, что все iBOulate и делегат подключены правильно
рекомендация Apple перекодирована в Свифт + использование UIScrollView с автоматической компоновкой в iOS (на основе следующих ссылок:ссылка 1,Ссылка 2,Ссылка 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) } }
Swift 4 устранение:
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 }
единственное, что я хотел бы обновить в коде Apple, это keyboardWillBeHidden: метод, чтобы обеспечить плавный переход.
// 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; }
здесь Swift 3 совместимый ответ, который также будет работать с контроллерами вида в навигационном контроллере-поскольку они изменят виды прокрутки
contentInset.top
собственность.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 }
Я обнаружил, что приведенные выше ответы устарели. Также не идеально, когда прокрутка.
вот быстрая версия.
он будет прокручиваться прямо под текстовым полем, без свободного места. И он вернется к тому, как это было, как его первое появление.
//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) }
это то, что я использую. Это просто и хорошо работает.
#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); }]; }
использовать
UITextField
вызов делегатаtextFieldDidBeginEditing:
чтобы переместить вид вверх, а также добавить уведомление наблюдателя, чтобы вернуть вид в нормальное состояние, когда клавиатура скрывает:-(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]; }
Это окончательный код с улучшениями в Свифт
//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 }
попробуйте этот код в 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) }
Я бы сделал это вот так. Это много кода, но он гарантирует, что текстовое поле, находящееся в фокусе, вертикально центрировано в "доступном пространстве":
- (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;
отметим, что
- (IBAction)textFieldGotFocus:
действие подключается к каждому текстовому полюDidBeginEditing
государство.также было бы немного лучше, чтобы получить продолжительность анимации из уведомления клавиатуры и использовать это для анимации scrollview вместо фиксированного значения, но судите меня, это было достаточно хорошо для меня ;)
на самом деле вам не нужен UIScrollView для этого. Я использовал этот код и он работает для меня:
-(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){ }]; }
вы можете прокручивать с помощью свойства
contentOffset
наUIScrollView
, например,CGPoint offset = scrollview.contentOffset; offset.y -= KEYBOARD_HEIGHT + 5; scrollview.contentOffset = offset;
есть также способ сделать анимированную прокрутку.
что касается причины, по которой ваше второе редактирование не прокручивается правильно, это может быть потому, что вы, похоже, предполагаете, что новая клавиатура будет появляться каждый раз, когда начинается редактирование. Вы можете попробовать проверить, если вы уже настроили для видимого положения "клавиатуры" (а также проверить видимость клавиатуры в данный момент перед возвратом он.)
лучшим решением может быть прослушивание уведомлений клавиатуры, например:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
Я знаю, это старый вопрос, но я думал, что это может помочь другим. Я хотел что-то немного проще реализовать для нескольких приложений, которые у меня были, поэтому я сделал класс для этого. Вы можете скачать его здесь, если хотите:https://github.com/sdernley/iOSTextFieldHandler
это так же просто, как установить все UITextFields, чтобы иметь делегат self
textfieldname.delegate = self;
а затем добавить это в контроллер вида с именем вашего scrollView и отправить кнопка
- (void)textFieldDidBeginEditing:(UITextField *)textField { [iOSTextFieldHandler TextboxKeyboardMover:containingScrollView tf:textField btn:btnSubmit]; }
ниже приведены мои решения, которые работают ( 5 шагов)
Шаг 1: добавьте наблюдателя, чтобы поймать который UITEXTFIELD или UITEXTVIEW ShoudBeginEditing (где объект inited или ViewDidLoad.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateActiveField:) name:@"UPDATE_ACTIVE_FIELD" object:nil];
Шаг 2: отправить уведомление, когда ..ShouldBeginEditing с объектом UITEXTFIELD или UITEXTVIEW
-(BOOL)textViewShouldBeginEditing:(UITextView *)textView { [[NSNotificationCenter defaultCenter] postNotificationName:@"UPDATE_ACTIVE_FIELD" object:textView]; return YES; }
Step3: метод, который (Step1 вызывает) назначает текущий UITEXTFIELD или UITEXTVIEW
-(void) updateActiveField: (id) sender { activeField = [sender object]; }
Шаг 4: Добавить наблюдатель клавиатуры UIKeyboardWillShowNotification (то же место, что и Шаг 1 )
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification 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); _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; } }
Step5: добавьте клавиатурный наблюдатель UIKeyboardWillHideNotification (там же, где и Шаг 1 )
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
и способ:
// Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillBeHidden:(NSNotification*)aNotification { self.layoutPanel.contentInset = _currentEdgeInsets; self.layoutPanel.scrollIndicatorInsets = _currentEdgeInsets; self.layoutPanel.scrollEnabled = YES; }
Не забудьте удалить наблюдателей!
используйте следующее расширение, если вы не хотите вычислять слишком много:
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) } }
и, возможно, вы хотите, чтобы ваш UITextField всегда был виден:
func textViewDidChange(textView: UITextView) { self.scrollView?.scrollSubviewToBeVisible(textView, animated: false) }
я использовал этот ответ, предоставленный Sudheer Palchuri https://stackoverflow.com/users/2873919/sudheer-palchuri https://stackoverflow.com/a/32583809/6193496
в ViewDidLoad зарегистрируйте уведомления:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DetailsViewController.keyboardWillShow(_:)), name:UIKeyboardWillShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DetailsViewController.keyboardWillHide(_:)), name:UIKeyboardWillHideNotification, object: nil)
добавить ниже методы наблюдателя, который делает автоматическую прокрутку при появлении клавиатуры.
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 }
мое решение имеет 4 шаг:
- Шаг 1: функция слушает, когда появляется клавиатура- (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; }
Шаг 2: Функция слушает, когда клавиатура исчезает
- (void)keyboardWillHide:(NSNotification *)notification { //top: 64 for navigatiob bar UIEdgeInsets contentInsets = UIEdgeInsetsMake(64, 0, 0, 0); [_editScrollView setContentInset: contentInsets]; [_editScrollView setScrollIndicatorInsets: contentInsets]; }
- Шаг 3: Добавьте эти функции в Центр уведомлений:
- (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]; }
- Шаг 4: Удалите listen, когда контроллер просмотра отменяет
- (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 решение, которое учитывает возможные высоты UIToolbar и 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 }
и конечно, вы должны отменить регистрацию наблюдателя в какой-то момент.