Як я можу зробити так, щоб UITextField рухався вгору, коли клавіатура присутня - коли я починаю редагувати?


1691

За допомогою iOS SDK:

У мене є UIViewз UITextFieldз , що виховує клавіатурою. Мені це потрібно, щоб мати можливість:

  1. Дозвольте прокручувати вміст, UIScrollViewщоб побачити інші текстові поля після появи клавіатури

  2. Автоматично «стрибати» (прокручуючи вгору) або скорочуючи

Я знаю, що мені потрібно UIScrollView. Я спробував змінити клас мого UIViewна а, UIScrollViewале я все ще не можу прокручувати текстові поля вгору або вниз.

Чи потрібні мені і a, UIViewі a UIScrollView? Один заходить всередині іншого?

Що потрібно здійснити для автоматичного прокручування до активного текстового поля?

В ідеалі якнайбільше налаштування компонентів буде виконано в Interface Builder. Я хотів би написати код лише для того, що йому потрібно.

Примітка: UIView(або UIScrollView), з яким я працюю, виводиться на вкладку ( UITabBar), яка повинна функціонувати як звичайна.


Редагувати: я додаю смугу прокрутки саме тоді, коли з’являється клавіатура. Незважаючи на те, що це не потрібно, я вважаю, що він забезпечує кращий інтерфейс, оскільки тоді користувач може прокручувати та змінювати текстові поля, наприклад.

У мене це працює там, де я змінюю розмір кадру, UIScrollViewколи клавіатура піднімається вгору і вниз. Я просто використовую:

-(void)textFieldDidBeginEditing:(UITextField *)textField { 
    //Keyboard becomes visible
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
                     scrollView.frame.origin.y, 
scrollView.frame.size.width,
scrollView.frame.size.height - 215 + 50);   //resize
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
   //keyboard will hide
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
       scrollView.frame.origin.y, 
     scrollView.frame.size.width,
      scrollView.frame.size.height + 215 - 50); //resize
}

Однак це не автоматично "переміщується вгору" або не центрує нижні текстові поля у видимій області, що саме мені б хотілося.


6
Заціни. Жодних клопотів для вас. TPKeyboardAvoiding
Aruna

21
Це задокументовано Apple, я думаю, що це найкращий спосіб: developer.apple.com/library/ios/#documentation/StringsTextFonts/…
Maik639

58
Використовуйте цей код. Вам просто потрібен 1 рядок у файлі appdelegate.m, і він працює. github.com/hackiftekhar/IQKeyboardManager
Pradeep Mittal

9
Найкращий спосіб, який я знайшов поки що, - це відкритий код TPKeyboardAvoiding
Монжу Зайди

2
Інший спосіб - додати текстові поля такого вмісту та всі в TableViewController, і дозволити цьому переглянути tableview.
Вікі Дас

Відповіді:


1036
  1. Вам знадобиться лише те, ScrollViewякщо вміст, який у вас зараз, не вміщується на екрані iPhone. (Якщо ви додаєте ScrollViewяк нагляд компонентів просто для створенняTextField прокрутку вгору, коли з'являється клавіатура, це не потрібно.)

  2. Стандартний спосіб запобігання TextField покриттю s клавіатурою - це переміщення зображення вгору / вниз, коли клавіатура відображається.

Ось приклад коду:

#define kOFFSET_FOR_KEYBOARD 80.0

-(void)keyboardWillShow {
    // Animate the current view out of the way
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)keyboardWillHide {
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:mailTf])
    {
        //move the main view, so that the keyboard does not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [self setViewMovedUp:YES];
        }
    }
}

//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3]; // if you want to slide up the view

    CGRect rect = self.view.frame;
    if (movedUp)
    {
        // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
        // 2. increase the size of the view so that the area behind the keyboard is covered up.
        rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
    }
    else
    {
        // revert back to the normal state.
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
    }
    self.view.frame = rect;

    [UIView commitAnimations];
}


- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    // register for keyboard notifications
    [[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];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}

3
Що означає _textField? Я скопіював його у свій код, він говорить, що _textField є незадекларованим.
Какао Дев

Це поле, яке ви використовуєте для того, щоб сказати, "коли користувач редагує тут подання, слід зісковзнути вгору" або щось таке ... Однак ви можете видалити це, якщо у вас більше полів.
патрік

чи це не тісто для виклику - (недійсне) setViewMovedUp: (BOOL) переміщене в keyBoardWillSHow та KeyBoardWillHide події !!
Абдуліам Рехманіус

4
Не особливо корисно, якщо ви підтримуєте обертання головного виду.
FractalDoctor

2
Щоб зробити цю роботу, мені довелося прокоментувати textFieldDidBeginEditingрозділ.
аванс

445

У мене також було багато проблем із UIScrollViewскладанням множинUITextFields , з яких одна чи більше з них затуляються клавіатурою під час редагування.

Ось кілька речей, які слід врахувати, якщо ваш UIScrollView не правильно прокручуєте.

1) Переконайтеся, що UIScrollViewрозмір contentSize перевищує розмір кадру. Спосіб зрозуміти UIScrollViews, що UIScrollViewце схоже на вікно перегляду вмісту, визначеного в contentSize. Тож, коли для UIScrollviewпрокрутки кудись потрібно, розмір contentSize повинен бути більшим за UIScrollView. В іншому випадку не потрібно прокручування, оскільки все, що визначено в contentSize, вже видно. BTW, за замовчуванням contentSize = CGSizeZero.

2) Тепер, коли ви розумієте, що UIScrollViewсправді це вікно у ваш "вміст", спосіб переконатися, що клавіатура не затьмарює ваше UIScrollView's"вікно" перегляду, було б змінити розмір, UIScrollViewщоб при наявності клавіатури у вас було UIScrollViewвікно розміром до оригіналуUIScrollView frame.size.height мінус висота клавіатури. Це забезпечить, що у вашому вікні є лише ця невелика область видимості.

3) Ось увага: Коли я вперше реалізував це, я зрозумів, що мені доведеться отримати CGRectвідредаговане текстове поле та викликати UIScrollView'sметод scrollRecToVisible. Я реалізував UITextFieldDelegateметод textFieldDidBeginEditingіз викликом до scrollRecToVisibleметоду. Це на самому справі працювали з дивною побічним ефектом , що скролінг буде оснащеннямUITextField в положення. Найдовше я не міг зрозуміти, що це таке. Тоді я прокоментував textFieldDidBeginEditingметод Delegate і все це працює !! (???). Як виявилося, я вважаю, що UIScrollViewнасправді неявно перенесене редагуване UITextFieldв вікно відображається неявно. Моя реалізація UITextFieldDelegateметоду та подальший заклик до програми scrollRecToVisibleбули зайвими і стали причиною дивного побічного ефекту.

Так ось кроки , щоб правильно прокручує UITextFieldв UIScrollViewна місце , коли з'являється значок клавіатури.

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardWillShowNotification 
                                               object:self.view.window];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillHide:) 
                                                 name:UIKeyboardWillHideNotification 
                                               object:self.view.window];
    keyboardIsShown = NO;
    //make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
    CGSize scrollContentSize = CGSizeMake(320, 345);
    self.scrollView.contentSize = scrollContentSize;
}

- (void)keyboardWillHide:(NSNotification *)n
{
    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;


    // resize the scrollview
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height += (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];

    keyboardIsShown = NO;
}

- (void)keyboardWillShow:(NSNotification *)n
{
    // This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown.  This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`.  If we were to resize the `UIScrollView` again, it would be disastrous.  NOTE: The keyboard notification will fire even when the keyboard is already shown.
    if (keyboardIsShown) {
        return;
    }

    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;

    // resize the noteView
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];
    keyboardIsShown = YES;
}
  1. Зареєструйтесь для сповіщень на клавіатурі за адресою viewDidLoad
  2. Скасуйте реєстрацію для невідповідностей про клавіатуру на viewDidUnload
  3. Переконайтеся , що contentSizeвстановлена і більше , ніж ваш UIScrollViewATviewDidLoad
  4. ПлівкиUIScrollView , коли клавіатура присутня
  5. Відкат назадUIScrollView коли клавіатура йде.
  6. Використовуйте Івар , щоб виявити , якщо клавіатура вже відображається на екрані , так як повідомлення клавіатури надсилаються кожен раз , коли UITextFieldце закладками , навіть якщо клавіатура вже присутня , щоб уникнути усадкиUIScrollView , коли він вже усадки

Варто зауважити, що UIKeyboardWillShowNotificationзапускається навіть тоді, коли клавіатура вже на екрані, коли ви клацнете іншу UITextField. Я подбав про це, використовуючи ivar, щоб уникнути зміни розміру, UIScrollViewколи клавіатура вже на екрані. Мимоволі зміни розміру, UIScrollViewколи клавіатура вже є, було б катастрофічно!

Сподіваюся, що цей код рятує від вас багато головного болю.


3
Велика, але дві проблеми: 1. UIKeyboardBoundsUserInfoKeyзастаріла. 2. Розмір клавіатури знаходиться в "координатах екрана", тому обчислення вашого viewFrame не вдасться, якщо кадр повернути або змінити масштаб.
Мартін Вікман

21
@Martin Wickman - Використовуйте CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;замість застарілогоUIKeyboardBoundsUserInfoKey
sottenad

1
Здравствуйте, я зробив те саме, але перегляд тексту рухається вгору лише тоді, коли користувач почне вводити текст? Це очікувана поведінка чи я щось пропускаю?

3
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].sizeповинно бути [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size. Хоча чудове рішення!
j7nn7k

1
Мені подобається ваше рішення, але я думаю, що я можу зробити його ще простішим: не турбуйтеся з повідомленнями спостерігача сповіщення; замість цього викликайте правильні процедури анімації у відповідних методах делегування - для UITextView вони textViewDidBeginEditing та textViewDidEndEditing.
AlexChaffee

270

Насправді найкраще просто використовувати реалізацію Apple, як це передбачено в документах . Однак код, який вони надають, є несправним. Замініть порцію, яку ви знайдете keyboardWasShown:трохи під коментарями, на наступну:

NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);

CGPoint origin =  activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
    CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
    [backScrollView setContentOffset:scrollPoint animated:YES];
}

Проблеми з кодом Apple полягають у наступному: (1) Вони завжди підраховують, чи точка входить у рамку перегляду, але це a ScrollView, тому вона, можливо, вже прокручується, і вам потрібно врахувати це зміщення:

origin.y -= scrollView.contentOffset.y

(2) Вони зміщують contentOffset на висоту клавіатури, але ми хочемо навпаки (ми хочемо змістити значення contentOffsetна висоту, яка видно на екрані, а не на те, що немає):

activeField.frame.origin.y-(aRect.size.height)

1
У ситуаціях, коли подання прокрутки не заповнює екран, aRect слід встановити на кадр подання прокрутки
mblackwell8

2
Якщо ви не хочете, щоб джерело CGPoint = activeField.frame.origin + activeField.frame.size.height?, Тому що ви хочете відображати все текстове поле, і якщо у нього трапляються лише деякі пікселі, код не введеться в хвороба.
хтафоя

1
Це рішення не працює в альбомній орієнтації - текстове поле відлітає вгорі порту перегляду. iPad з iOS 7.1.
Андрій

4
Для кращої підтримки iOS 8 я б рекомендував використовувати UIKeyboardFrameEndUserInfoKeyзамість того, UIKeyboardFrameBeginUserInfoKeyщоб отримувати розмір клавіатури, оскільки це дозволить вибрати такі речі, як користувацькі зміни клавіатури та включення / вимкнення прогнозного тексту.
Ендарет

1
@Egor: Ваш виправлення покращує роботу, але останній рядок повинен бути зворотним:self.scrollView.contentOffset = self.currentSVoffset;
Morten Holmgaard

244

В textFieldDidBeginEdittingі в textFieldDidEndEditingвиконанні функції [self animateTextField:textField up:YES]наступним чином:

-(void)textFieldDidBeginEditing:(UITextField *)textField 
{ 
    [self animateTextField:textField up:YES]; 
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField:textField up:NO];
}

-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
    const int movementDistance = -130; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? movementDistance : -movementDistance); 

    [UIView beginAnimations: @"animateTextField" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

Я сподіваюся, що цей код вам допоможе.

У Swift 2

func animateTextField(textField: UITextField, up: Bool) 
{
     let movementDistance:CGFloat = -130
     let movementDuration: Double = 0.3

     var movement:CGFloat = 0
     if up 
     {
         movement = movementDistance
     }
     else 
     {
         movement = -movementDistance
     }
     UIView.beginAnimations("animateTextField", context: nil)
     UIView.setAnimationBeginsFromCurrentState(true)
     UIView.setAnimationDuration(movementDuration)
     self.view.frame = CGRectOffset(self.view.frame, 0, movement)
     UIView.commitAnimations()
}


func textFieldDidBeginEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:true)
}

func textFieldDidEndEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:false)
}

SWIFT 3

 func animateTextField(textField: UITextField, up: Bool)
    {
        let movementDistance:CGFloat = -130
        let movementDuration: Double = 0.3

        var movement:CGFloat = 0
        if up
        {
            movement = movementDistance
        }
        else
        {
            movement = -movementDistance
        }
        UIView.beginAnimations("animateTextField", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration)
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }


    func textFieldDidBeginEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:true)
    }

    func textFieldDidEndEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:false)
    }

1
чому б не використовувати [UIView animateWithDuration: animations:^{ }];?
Андре Цитрин

2
це добре, хоча const int motionDistance = -130; // Налаштування за потребою потрібно змінити на більш гнучку
Hammer

7
Неймовірно простий у невеликих реалізаціях. Немає стосунків із ScrollViews та неоднозначними проблемами автоматичного розміщення.
Джеймс Перих

4
Ем ... ви взагалі не використовуєте параметр textField. Чому тоді це функція як параметр функції? Крім того, ви можете використовувати потрійний оператор також у Swift. Робить код менш розмовними.
stk

1
Якщо колір тла Перегляду є чимось іншим, ніж чорний, переконайтеся, що ви встановили колір Вікна відповідно до вашого перегляду, щоб користувач не бачив за ним. тобто self.window.backgroundColor = [UIColor whiteColor];
bvmobileapps

134

Просто за допомогою TextFields:

1a) Використання Interface Builder: Виберіть Усі текстові поля => Редагувати => Вбудувати => ScrollView

1b) Вручну вставити текстові поля в UIScrollView під назвою scrollView

2) Встановити UITextFieldDelegate

3) Встановити кожен textField.delegate = self;(або встановити з'єднання Interface Builder)

4) Копіювати / вставляти:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
    [scrollView setContentOffset:scrollPoint animated:YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    [scrollView setContentOffset:CGPointZero animated:YES];
}

8
Але воно також рухається вгору погляду, коли textFieldвоно вже видно.
TheTiger

1
Потрібно змінитись CGPointMake(0, textField.frame.origin.y);наCGPointMake(0, textField.frame.origin.y + scrollView.contentInset.top);
Fury

@Egor Навіть після вашого коментаря це не працює. Як і згадуваний "TheTiger", він рухається вгору погляду навіть після того, як видно текстове поле.
rak appdev

Зміна для XCode 10: "Виберіть усі текстові поля => Редактор => Вставити = = Переглянути прокрутку"
tibalt

116

Щодо універсального рішення , тут був мій підхід до впровадження IQKeyboardManager .

введіть тут опис зображення

Шаг1: - Я Додані глобальні повідомлення про UITextField, UITextViewі UIKeyboardв класі одноплідних. Я називаю це IQKeyboardManager .

КРОК 2: - Якщо буде встановлено UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationчи UITextViewTextDidBeginEditingNotificationповідомлення, я намагаюся отримати topMostViewControllerекземпляр з UIWindow.rootViewControllerієрархії. Для правильного розкриття UITextField/ UITextViewна ньому topMostViewController.viewкадр потрібно відрегулювати.

Крок 3: - Я обчислював очікувану відстань переміщення topMostViewController.viewвідносно першої відповіді UITextField/ UITextView.

Крок 4: - Я рухався topMostViewController.view.frameвгору / вниз відповідно до очікуваної відстані переміщення.

Крок 5: - Якщо буде встановлено UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotificationчи UITextViewTextDidEndEditingNotificationповідомлення, я знову спробувати отримати topMostViewControllerекземпляр з UIWindow.rootViewControllerієрархії.

Крок 6: - Я обчислив порушену відстань, topMostViewController.viewяку потрібно відновити у вихідне положення.

Крок 7: - Я відновився topMostViewController.view.frameвниз відповідно до порушеної відстані.

Крок 8: - Я інстанціював однофакторний екземпляр класу IQKeyboardManager під час завантаження програми, тому кожен UITextField/ UITextViewу додатку автоматично налаштовується відповідно до очікуваної відстані переміщення.

Це все, що для вас робить IQKeyboardManager з НЕ РІДНОМИ КОДУ ! потрібно лише перетягнути відповідний вихідний файл до проекту. IQKeyboardManager також підтримує орієнтацію на пристрої , автоматичне управління панеллю UITool , KeybkeyboardDistanceFromTextField та багато іншого, ніж ви думаєте.


Додайте каталог IQKeyBoardManagerSwift до мого проекту і не працюйте. Неможливо включити, тому що його не розпізнають у AppDelegate ...
user3722523

2
Вам здається, що фішинг фактичне рішення не відображається, але натомість ми бачимо рекламу цього облікового запису GitHub для хлопців.
Брайан

101

Я склав універсальний, що випадає UIScrollView , UITableViewі навіть UICollectionViewпідклас , який піклується про переміщення всіх текстових полів всередині нього в сторону клавіатури.

Коли клавіатура ось-ось з'явиться, підклас знайде підперегляд, який збирається редагувати, і відрегулює зміщення кадру та змісту, щоб переконатися у тому, що подання видно, з анімацією, що відповідає спливаючому клавіатурі. Коли клавіатура зникає, вона відновлює свій попередній розмір.

Він повинен працювати в основному з будь-якою установкою, або з UITableViewінтерфейсом на базі , або з інтер'єром, що складається з подань, розміщених вручну.

Ось tis: рішення для переміщення текстових полів з шляху клавіатури


Ось воно! Це найкраще, найефективніше та ідеальне рішення! Він також обробляє обертання належним чином для подання прокрутки. Якщо ви обертаєтесь, обов'язково зробіть автоматичний розмір вертикально, але не закріплюйте його внизу. Я додав UITextView до подання прокрутки у своєму випадку. Дякую пучки!
Крістофер

Дуже приємна робота! Звичайно, я лінуюся використовувати ваше рішення замість того, щоб зробити сам, але мій начальник щасливіший, так, так! Навіть якщо хтось хоче зробити це сам, мені подобається ваш підклас, а не додавання коду до кожного контролера. Я був шокований, що iOS цього не робив за замовчуванням, як це робив Android - тоді я знову знаходжу багато речей, у яких не вистачає iOS та MacOS :(
eselk

Чи всі дивні речі, такі як мій прокрутка, вміщуються на екрані, тому його неможливо прокрутити. Після відкриття та закриття клавіатури вміст тепер стає більшим (виглядає так, що щось невидиме було додано, а не видалено внизу сторінки), і його можна прокручувати.
Альмо

90

Для швидких програмістів:

Це зробить все для вас, просто покладіть їх у свій клас контролера подання та вкажіть у свій контролер UITextFieldDelegateперегляду та встановіть делегат textField уself

textField.delegate = self // Setting delegate of your UITextField to self

Реалізуйте методи зворотного виклику делегата:

func textFieldDidBeginEditing(textField: UITextField) {
    animateViewMoving(true, moveValue: 100)
}

func textFieldDidEndEditing(textField: UITextField) {
    animateViewMoving(false, moveValue: 100)
}

// Lifting the view up
func animateViewMoving (up:Bool, moveValue :CGFloat){
    let movementDuration:NSTimeInterval = 0.3
    let movement:CGFloat = ( up ? -moveValue : moveValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration )
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}

Для Swift 4, 4.2, 5: Зміна

self.view.frame = CGRectOffset(self.view.frame, 0,  movement)

до

self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)

Остання примітка про цю реалізацію: Якщо натиснути інший контролер перегляду на стек, поки клавіатура відображається, це створить помилку, коли подання повертається назад до його центрального кадру, але зміщення клавіатури не скидається. Наприклад, ваша клавіатура є першим відгуком для nameField, але потім ви натискаєте кнопку, яка натискає ваш контролер перегляду довідки на ваш стек. Щоб виправити помилку зсуву, переконайтесь, щоб зателефонувати до nameField.resignFirstResponder (), перш ніж виходити з контролера перегляду, переконуючись, що також викликається метод делегата textFieldDidEndEditing. Я роблю це методом viewWillDisappear.


3
SwiftLint не сподобалось, self.view.frame = CGRectOffset(self.view.frame, 0, movement)тому я змінив цю лінію наself.view.frame.offsetInPlace(dx: 0, dy: movement)
levibostian

2
Swift 4 змінить self.view.frame = CGRectOffset (self.view.frame, 0, рух) на self.view.frame.offsetBy (dx: 0, dy: рух)
Asinox

FYI, щоб це працювало, ти повинен поставити. self.view.frame = self.view.frame.offsetBy (dx: 0, dy: рух)
Josh Wolff

64

Відповідей уже багато, але все ще в жодному з вищевказаних рішень не було всіх фантазійних позиціонерів, необхідних для «ідеальної» анімації без помилок, сумісної назад і без мерехтіння. (помилка при анімації кадру / меж та вмістуOffset разом, різні орієнтації інтерфейсу, розділена клавіатура iPad, ...)
Дозвольте мені поділитися своїм рішенням:
(якщо припустити, що ви налаштували UIKeyboardWill(Show|Hide)Notification)

// Called when UIKeyboardWillShowNotification is sent
- (void)keyboardWillShow:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = [notification userInfo];

    CGRect keyboardFrameInWindow;
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];

    // the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
    CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil];

    CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView);
    UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);

    // this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    _scrollView.contentInset = newContentInsets;
    _scrollView.scrollIndicatorInsets = newContentInsets;

    /*
     * Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element
     * that should be visible, e.g. a purchase button below an amount text field
     * it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
     */
    if (_focusedControl) {
        CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
        controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.

        CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y;
        CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;

        // this is the visible part of the scroll view that is not hidden by the keyboard
        CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;

        if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
            // scroll up until the control is in place
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);

            // make sure we don't set an impossible offset caused by the "nice visual offset"
            // if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
            newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight);

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        } else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) {
            // if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y = controlFrameInScrollView.origin.y;

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        }
    }

    [UIView commitAnimations];
}


// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillHide:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = notification.userInfo;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    // undo all that keyboardWillShow-magic
    // the scroll view will adjust its contentOffset apropriately
    _scrollView.contentInset = UIEdgeInsetsZero;
    _scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;

    [UIView commitAnimations];
}

Великі вдосконалення відповіді @Shiun. Але після відсутності клавіатури, вигляд не повертається на 1-у позицію. Це ще чудова робота :)
Люсьєн

2
Дякую, це найкраще рішення для мене у 2017 році. Зверніть увагу, що вам не потрібно самостійно відстежувати focusControl, це можна визначити за допомогою UIApplication.shared.sendAction(...). Ось версія Вашої відповіді Swift 3 (мінус willHide порція) із sendActionреалізованою: gist.github.com/xaphod/7aab1302004f6e933593a11ad8f5a72d
xaphod

@xaphod у моєму випадку мені потрібно було зосередити більше елементів керування - наприклад, кнопку під полем введення. але так, цьому коду зараз 4 роки, і він може скористатися вдосконаленнями.
Мартін Улріх

Це, мабуть, правильне рішення. Повідомлення на клавіатурі містить дані про анімацію, делегації текстового поля не знають про тривалість анімації, це було б просто здогадкою.
XY

62

Шиун сказав: "Як виявилося, я вважаю, що UIScrollView насправді неявно приносить редактоване UITextField, що редагується в даний момент, у вікно, що відображається неявно". Це здається, що це стосується iOS 3.1.3, але не 3.2, 4.0 або 4.1. Мені довелося додати явний scrollRectToVisible, щоб зробити iITextField видимим на iOS> = 3.2.


Не UIScrollView прокручує неявно відредагований UITextField, а його UITextField, який викликає приватний [UITextField scrollTextFieldToVisibleIfNecessary]метод, який у свою чергу викликає, [UIScrollView scrollRectToVisible]коли [UITextField becomeFirstResponder]викликається. Дивіться сторінку github.com/leopatras/ios_textfields_on_scrollview . Якщо обмеження та контролери перегляду встановлені належним чином, насправді не потрібно scrollRectToVisibleчітко телефонувати (принаймні з моменту IOS 11).
Лев

48

Варто враховувати, чи хочете ви коли-небудь використовувати UITextFieldсамостійно. Я не стикався з жодними добре розробленими додатками для iPhone, які фактично використовуються UITextFieldsза межами UITableViewCells.

Це буде додатковою роботою, але я рекомендую вам реалізувати всі представлення даних в таблиці. Додайте UITextViewсвій UITableViewCells.


1
Один із моїх додатків повинен дозволити користувачам додавати нотатки вільної форми, так що так, іноді корисно використовувати поле UITextField.
Пітер Джонсон

1
Я згоден з цим методом. Нульова робота або кодування таким чином. Навіть якщо вам потрібна вільна форма, ви все одно можете скористатися коміркою таблиці
RJH

UITableViewна жаль єдиний шлях. Повідомлення на клавіатурі є крихкими та змінили понаднормово. Приклад коду на переповнення стека: stackoverflow.com/a/32390936/218152
SwiftArchitect

Ця відповідь застаріла через п’ять років . Єдине сучасне рішення - щось подібне ... stackoverflow.com/a/41808338/294884
Fattie

47

У цьому документі детально описано рішення цієї проблеми. Подивіться вихідний код у розділі "Переміщення вмісту, розташованого під клавіатурою". Це досить просто.

EDIT: Помічено, що в цьому прикладі є невдача. Ви, ймовірно, захочете слухати UIKeyboardWillHideNotificationзамість цього UIKeyboardDidHideNotification. В іншому випадку подання прокрутки позаду клавіатури буде вирізане протягом тривалості анімації закриття клавіатури.


32

Найпростіше знайдене рішення

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}


- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = 80; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

Екран рухається вгору, навіть якщо він не знаходиться внизу. тобто, якщо текстове поле знаходиться вгорі, воно переміщується з екрана. Як контролювати цей випадок?
MELWIN

@MELWIN Просто додайте після цього рядка: int movement = (up ? -movementDistance : movementDistance); if (textField.frame.origin.y < self.view.frame.size.height - keyboard.height) { movementDistance = 0 } Будь ласка, не keyboardвраховуйте, що змінна - це CGRect клавіатури, що спливає, яку ви отримуєте, роблячи:let keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
CapturedTree

31

Невелике виправлення, яке працює для багатьох UITextFields

#pragma mark UIKeyboard handling

#define kMin 150

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
   if (currTextField) {
      [currTextField release];
   }
   currTextField = [sender retain];
   //move the main view, so that the keyboard does not hide it.
   if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) {
        [self setViewMovedUp:YES]; 
   }
}



//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
   [UIView beginAnimations:nil context:NULL];
   [UIView setAnimationDuration:0.3]; // if you want to slide up the view

   CGRect rect = self.view.frame;
   if (movedUp)
   {
      // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
      // 2. increase the size of the view so that the area behind the keyboard is covered up.
      rect.origin.y = kMin - currTextField.frame.origin.y ;
   }
   else
   {
      // revert back to the normal state.
      rect.origin.y = 0;
   }
   self.view.frame = rect;

   [UIView commitAnimations];
}


- (void)keyboardWillShow:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately

   if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin)
   {
      [self setViewMovedUp:YES];
   }
   else if (![currTextField isFirstResponder] && currTextField.frame.origin.y  + self.view.frame.origin.y < kMin)
   {
      [self setViewMovedUp:NO];
   }
}

- (void)keyboardWillHide:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately
   if (self.view.frame.origin.y < 0 ) {
      [self setViewMovedUp:NO];
   }

}


- (void)viewWillAppear:(BOOL)animated
{
   // register for keyboard notifications
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) 
                                                name:UIKeyboardWillShowNotification object:self.view.window]; 
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) 
                                                name:UIKeyboardWillHideNotification object:self.view.window]; 
}

- (void)viewWillDisappear:(BOOL)animated
{
   // unregister for keyboard notifications while not visible.
   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 
}

rect.origin.y=+currTextField.frame.origin.yдобре працює спасибі
u.gen

30

Код RPDP успішно переміщує текстове поле з шляху клавіатури. Але коли ви прокручуєте до вершини після використання та відхилення клавіатури, верх прокручується вгору поза вікном. Це справедливо для Симулятора та пристрою. Щоб прочитати вміст у верхній частині цього виду, потрібно перезавантажити його.

Невже його наступний код не повинен приводити погляд назад?

else
{
    // revert back to the normal state.
    rect.origin.y += kOFFSET_FOR_KEYBOARD;
    rect.size.height -= kOFFSET_FOR_KEYBOARD;
}

23

Я не впевнений, що якщо переміщення подання вгору є правильним підходом, я зробив це по-різному, змінивши розмір UIScrollView. Я детально пояснив це на невеликій статті


Посилання на статтю мертве.
Тео

22

Щоб повернутися до початкового стану перегляду, додайте:

-(void)textFieldDidEndEditing:(UITextField *)sender

{
    //move the main view, so that the keyboard does not hide it.
    if  (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

20

Спробуйте цей короткий трюк.

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = textField.frame.origin.y / 2; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

19

Є так багато рішень, але я витратив кілька годин, перш ніж він почне працювати. Отже, я ставлю цей код тут (просто вставте проект, будь-які зміни не потребують):

@interface RegistrationViewController : UIViewController <UITextFieldDelegate>{
    UITextField* activeField;
    UIScrollView *scrollView;
}
@end

- (void)viewDidLoad
{
    [super viewDidLoad];

    scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];

    //scrool view must be under main view - swap it
    UIView* natView = self.view;
    [self setView:scrollView];
    [self.view addSubview:natView];

    CGSize scrollViewContentSize = self.view.frame.size;
    [scrollView setContentSize:scrollViewContentSize];

    [self registerForKeyboardNotifications];
}

- (void)viewDidUnload {
    activeField = nil;
    scrollView = nil;
    [self unregisterForKeyboardNotifications];
    [super viewDidUnload];
}

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShown:)
                                                 name:UIKeyboardWillShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];

}

-(void)unregisterForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillShowNotification
                                                  object:nil];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillHideNotification
                                                  object:nil];
}

- (void)keyboardWillShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    CGRect frame = self.view.frame;
    frame.size.height -= kbSize.height;
    CGPoint fOrigin = activeField.frame.origin;
    fOrigin.y -= scrollView.contentOffset.y;
    fOrigin.y += activeField.frame.size.height;
    if (!CGRectContainsPoint(frame, fOrigin) ) {
        CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y + activeField.frame.size.height - frame.size.height);
        [scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
     [scrollView setContentOffset:CGPointZero animated:YES];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    activeField = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    activeField = nil;
}

-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

PS: Я сподіваюся, що код допоможе комусь швидко досягти бажаного ефекту. (Xcode 4.5)


Привіт Hotjard, я отримую EXE_BAD_ACCESS у [self.view addSubview: natView];
Бала

18

@ user271753

Щоб повернути погляд до оригіналу, додайте:

-(BOOL)textFieldShouldReturn:(UITextField *)textField{
   [textField resignFirstResponder];
   [self setViewMovedUp:NO];
   return YES;
}

16

Для переміщення кадру перегляду не потрібно перегляду прокрутки. Ви можете змінити кадр viewcontroller'sподання так, щоб весь вигляд перемістився вгору достатньо, щоб поставити текстове поле першого відповіді над клавіатурою. Коли я зіткнувся з цією проблемою, я створив підкласUIViewController який робить це. Він зауважує, що на клавіатурі з'явиться повідомлення та знайде перший підвідвід респондента та (при необхідності) він оживить основний вигляд вгору просто настільки, щоб перший відповідь опинився над клавіатурою. Коли клавіатура ховається, вона оживляє погляд назад, де він був.

Щоб використовувати цей підклас, перетворіть у власний контролер подання підклас GMKeyboardVC, і він успадкує цю функцію (просто переконайтеся, що ви реалізуєте, viewWillAppearі viewWillDisappearвони повинні викликати супер). Клас - на github .


Яка ліцензія? Деякі ваші файли там мають ліцензію з відкритим кодом, а деякі - ні.
Хайме

Попередження: цей код не є дружнім для проектів ARC.
Альмо

Ви просто додаєте параметр збірки, щоб вказати, що це не ARC-файли, або бажано перетворити його в ARC та подати запит на витяг.
progrmr

14

Швидкий 4 .

Ви можете легко переміщатися вгору і вниз UITextFieldАбо UIViewЗ UIKeyBoardWithAnimation введіть тут опис зображення

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var textField: UITextField!
    @IBOutlet var chatView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textField.resignFirstResponder()
    }

    @objc func keyboardWillChange(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y
        print("deltaY",deltaY)

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.chatView.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
        },completion: nil)
    }

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

}

2
Майже ідеально. На iPhone X, хоча у вас є дивний розрив між клавіатурою та текстовим полем.
Хьюман

12

Ось таке рішення, яке я придумав для конкретного планування. Це рішення схоже на рішення Метта Галлахера в тому, що прокручується розділ. Я все ще новачок у розробці iPhone, і не знайомий з тим, як працюють макети. Таким чином, цей злом.

Моя реалізація потрібна для підтримки прокрутки при натисканні на поле, а також прокрутки, коли користувач вибере наступний на клавіатурі.

У мене був UIView з висотою 775. Елементи керування розподілені в основному групами по 3 на великому просторі. Я закінчив наступний макет IB.

UIView -> UIScrollView -> [UI Components]

Ось іде хак

Я встановив висоту UIScrollView на 500 одиниць більше, ніж фактичний макет (1250). Потім я створив масив з абсолютними позиціями, до яких мені потрібно прокрутити, та простою функцією для отримання їх на основі номера IB Tag.

static NSInteger stepRange[] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410
};

NSInteger getScrollPos(NSInteger i) {
    if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) {
        return 0 ;
    return stepRange[i] ;
}

Тепер все, що вам потрібно зробити, - це використовувати наступні два рядки коду у textFieldDidBeginEditing та textFieldShouldReturn (останній, якщо ви створюєте наступну навігацію в полі)

CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;

Приклад.

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
    [self.scrollView setContentOffset:point animated:YES] ;
}


- (BOOL)textFieldShouldReturn:(UITextField *)textField {

    NSInteger nextTag = textField.tag + 1;
    UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];

    if (nextResponder) {
        [nextResponder becomeFirstResponder];
        CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ;
        [self.scrollView setContentOffset:point animated:YES] ;
    }
    else{
        [textField resignFirstResponder];
    }

    return YES ;
}

Цей метод не «прокручується назад», як це роблять інші методи. Це не було вимогою. Знову це було для досить «високого» UIView, і у мене не було днів, щоб вивчити двигуни внутрішнього планування.


12

Відповідно до документів , станом на iOS 3.0, UITableViewControllerклас автоматично змінює розмір та перетворює його подання таблиці, коли відбувається редагування текстових полів. Я думаю, що недостатньо для введення текстового поля всередині аUITableViewCell як дехто вказав.

З документів :

Контролер подання таблиці підтримує вбудоване редагування рядків подання таблиці; якщо, наприклад, рядки вбудовують текстові поля в режимі редагування, він прокручує рядок, що редагується над віртуальною клавіатурою, що відображається.


Я знайшов той самий коментар. Так, це правда. Дивна річ у тому, що він працює в одному UITabelViewController, а в другому - ні. Але я не міг знайти жодних відмінностей у своїй реалізації.
Morpheus78

11

Тут я знайшов найпростіше рішення для управління клавіатурою.

Вам потрібно просто скопіювати та вставити нижче зразок коду та змінити текстове поле чи будь-який вигляд, який потрібно перемістити вгору.

Крок 1

Просто скопіюйте та вставте нижче двох методів у свій контролер

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];
}

- (void)deregisterFromKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Крок-2

зареєструвати та скасувати реєстрацію Повідомлень про клавіатуру у viewWillAppear та viewWillDisappear відповідно.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self registerForKeyboardNotifications];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [self deregisterFromKeyboardNotifications];
    [super viewWillDisappear:animated];
}

Крок-3

Ось іде частина душі, просто замініть своє текстове поле і змініть висоту на скільки ви хочете рухатись догори.

- (void)keyboardWasShown:(NSNotification *)notification
{
    NSDictionary* info = [notification userInfo];
    CGSize currentKeyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    //you need replace your textfield instance here
    CGPoint textFieldOrigin = self.tokenForPlaceField.frame.origin;
    CGFloat textFieldHeight = self.tokenForPlaceField.frame.size.height;

    CGRect visibleRect = self.view.frame;
    visibleRect.size.height -= currentKeyboardSize.height;

    if (!CGRectContainsPoint(visibleRect, textFieldOrigin))
    {
        //you can add yor desired height how much you want move keypad up, by replacing "textFieldHeight" below

        CGPoint scrollPoint = CGPointMake(0.0, textFieldOrigin.y - visibleRect.size.height  + textFieldHeight); //replace textFieldHeight to currentKeyboardSize.height, if you want to move up with more height
        [self.scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification *)notification
{
    [self.scrollView setContentOffset:CGPointZero animated:YES];
}

Довідка : ну, будь ласка, оцініть цього хлопця , який поділився цим прекрасним чистим чистим рішенням коду.

Сподіваюсь, це буде комусь корисно.


Я не думаю, що це найкраще. Ithink @Dheeraj VS має рацію: це можна зробити легко та автоматично, якщо це текстове поле знаходиться в комірці таблиці (навіть коли table.scrollable = НІ). Зверніть увагу, що: положення та розмір столу повинні бути розумними. наприклад: - якщо y позиції таблиці дорівнює 100, відлічується від нижнього огляду, то клавіатура висотою 300 перекриє всю таблицю. - якщо висота таблиці = 10, а текстове поле в ній повинно прокручуватися вгору 100, коли з’являється клавіатура, щоб бути видимим, то це текстове поле буде поза межами таблиці.
samthui7

@ samthui7 Відповідь Dheeraj працює лише в тому випадку, якщо ви використовуєте TableViewController, а не лише перегляд таблиці. Це робить обмеження, яке іноді не підходить.
Бен Г

10

Шукав хороший підручник для початківців по цій темі, знайшов кращий підручник тут .

У MIScrollView.hприкладі внизу підручника обов’язково введіть пробіл

@property (nonatomic, retain) id backgroundTapDelegate;

як бачиш.


Привіт, savagenoob, дякую за надане посилання та ласкаво просимо до stackoverflow. Спробуйте поставити якомога більше інформації, відповідаючи на (майбутні) запитання - прості посилання трохи крихкі. Це було сказано, якщо відповідь - це посилання на хороший підручник, який, можливо, буде не помічено.
Maarten Bodewes

10

Коли UITextFieldзнаходиться в UITableViewCellпрокруткою повинен бути встановлений автоматично.

Якщо це не так, це, мабуть, через неправильний код / ​​налаштування табличного перегляду.

Наприклад, коли я перезавантажував свою довгу таблицю з однією UITextFieldвнизу,

-(void) viewWillAppear:(BOOL)animated
{
   [self.tableview reloadData];
}

тоді моє текстове поле внизу було затемнено клавіатурою, яка з’явилася, коли я натиснув всередині текстового поля.

Щоб виправити це, мені довелося це зробити -

-(void) viewWillAppear:(BOOL)animated
{
    //add the following line to fix issue
    [super viewWillAppear:animated];
    [self.tableview reloadData];
}

Мене бентежить, для чого цей код? Коли відображається клавіатура,viewWillAppear не викликається. І reloadDataне робить затемнені рядки видимими.
Адам Джонс

9

Використовуйте цю сторону, вам не потрібно писати навіть один рядок

https://github.com/hackiftekhar/IQKeyboardManager

завантажте проект та перетягніть IQKeyboardManagerпроект. Якщо ви знайшли будь-яку проблему, будь ласка, прочитайте READMEдокумент.

Хлопці дійсно знімають головний біль для управління клавіатурою.


8

Примітка : ця відповідь припускає, що ваше текстове поле знаходиться в прокрутціПерегляд.

Я вважаю за краще займатися цим використанням scrollContentInset і scrollContentOffset, а не возитися з кадрами мого погляду.

Спочатку давайте послухаємо сповіщення на клавіатурі

//call this from viewWillAppear
-(void)addKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}
//call this from viewWillDisappear
-(void)removeKeyboardNotifications{
    [[NSNotificationCenter default
    Center] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Наступним кроком є ​​збереження властивості, яка представляє поточного першого відповіді (UITextfield / UITextVIew, у якому зараз є клавіатура).

Ми використовуємо методи делегата для встановлення цього властивості. Якщо ви використовуєте інший компонент, вам знадобиться щось подібне.

Зауважте, що для текстового поля ми встановили це у didBeginEditing, а для textView у ShouldBeginEditing. Це тому, що textViewDidBeginEditing чомусь викликається після UIKeyboardWillShowNotification.

-(BOOL)textViewShouldBeginEditing:(UITextView * )textView{
    self.currentFirstResponder = textView;
    return YES;
}

-(void)textFieldDidBeginEditing:(UITextField *)textField{
    self.currentFirstResponder = textField;
}

Нарешті, ось магія

- (void)keyboardWillShow:(NSNotification*)aNotification{
    NSDictionary* info = [aNotification userInfo];
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];


    /*if currentFirstResponder is overlayed by the keyboard, move it so it bottom ends where the keyboard begins*/
    if(self.currentFirstResponder){

        //keyboard origin in currentFirstResponderFrame
        CGPoint keyboardOrigin = [self.currentFirstResponder convertPoint:kbFrame.origin fromView:nil];

        float spaceBetweenFirstResponderAndKeyboard = abs(self.currentFirstResponder.frame.size.height-keyboardOrigin.y);

        //only scroll the scrollview if keyboard overlays the first responder
        if(spaceBetweenFirstResponderAndKeyboard>0){
            //if i call setContentOffset:animate:YES it behaves differently, not sure why
            [UIView animateWithDuration:0.25 animations:^{
                [self.scrollView setContentOffset:CGPointMake(0,self.scrollView.contentOffset.y+spaceBetweenFirstResponderAndKeyboard)];
            }];
        }
    }

    //set bottom inset to the keyboard height so you can still scroll the whole content

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbFrame.size.height, 0.0);
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;

}

- (void)keyboardWillHide:(NSNotification*)aNotification{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;
}

8

Це рішення за допомогою Swift.

import UIKit

class ExampleViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var scrollView: UIScrollView!

    @IBOutlet var textField1: UITextField!
    @IBOutlet var textField2: UITextField!
    @IBOutlet var textField3: UITextField!
    @IBOutlet var textField4: UITextField!
    @IBOutlet var textField5: UITextField!

    var activeTextField: UITextField!

    // MARK: - View
    override func viewDidLoad() {
        super.viewDidLoad()
        self.textField1.delegate = self
        self.textField2.delegate = self
        self.textField3.delegate = self
        self.textField4.delegate = self
        self.textField5.delegate = self
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.registerForKeyboardNotifications()
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        self.unregisterFromKeyboardNotifications()
    }

    // MARK: - Keyboard

    // Call this method somewhere in your view controller setup code.
    func registerForKeyboardNotifications() {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
        center.addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterFromKeyboardNotifications () {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.removeObserver(self, name: UIKeyboardDidShowNotification, object: nil)
        center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }

    // Called when the UIKeyboardDidShowNotification is sent.
    func keyboardWasShown (notification: NSNotification) {
        let info : NSDictionary = notification.userInfo!
        let kbSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue() as CGRect!).size

        let contentInsets: UIEdgeInsets = 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 app might not need or want this behavior.
        var aRect = self.view.frame
        aRect.size.height -= kbSize.height;
        if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) {
            self.scrollView.scrollRectToVisible(self.activeTextField.frame, animated: true)
        }
    }

    // Called when the UIKeyboardWillHideNotification is sent
    func keyboardWillBeHidden (notification: NSNotification) {
        let contentInsets = UIEdgeInsetsZero;
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;
    }

    // MARK: -  Text Field

    func textFieldDidBeginEditing(textField: UITextField) {
        self.activeTextField = textField
    }

    func textFieldDidEndEditing(textField: UITextField) {
        self.activeTextField = nil
    }

}

Правильна відповідь, але у мене виникають проблеми з нулем при використанні TextField та TextView. Будь-яка допомога?
Тіха Аун

@Thiha Aung, Чи ваші змінні IBOutlet у вихідному коді підключені до ІБ?
Хомам

Так, вони також підключені. Просто мати цю помилку при використанні UITextView в цьому рядку: якщо (! CGRectContainsPoint (aRect, self.activeTextField.frame.origin)) {
Thiha Aung

Значить self.activeTextField є нульовим
Thiha Aung
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.