Как я могу заставить UITextField двигаться вверх при наличии клавиатуры - при начале редактирования?

Невозможно идентифицировать компьютеры, обращающиеся к веб-сайту, без участия их владельцев. Однако, если вы позволите, вы можете сохранить файл cookie, чтобы идентифицировать машину, когда он снова посещает ваш сайт. Ключ в том, что посетитель находится под контролем; они могут удалить cookie и появиться в качестве нового посетителя в любое время.

1649
задан 24 revs, 14 users 51% 4 January 2019 в 11:19
поделиться

10 ответов

  1. Вам понадобится ScrollView , только если содержимое, которое у вас сейчас, не умещается на экране iPhone. (Если вы добавляете ScrollView в качестве супервизора компонентов. Просто чтобы заставить TextField прокручиваться вверх при появлении клавиатуры, это не нужно.)

  2. Для отображения текстовые поля , не скрытые клавиатурой, стандартным способом является перемещение вверх / вниз по виду с текстовыми полями всякий раз, когда отображается клавиатура.

Вот пример кода:

#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];
}
1029
ответ дан 22 November 2019 в 20:09
поделиться

см. UICatalogView примеры живых и загруженных iphone

0
ответ дан 3 October 2019 в 21:30
поделиться

Одна вещь, которую следует принять во внимание, - хотите ли вы когда-нибудь использовать UITextField отдельно. Я не встречал ни одного хорошо разработанного приложения для iPhone, которое действительно использует UITextFields за пределами UITableViewCells .

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

47
ответ дан 22 November 2019 в 20:09
поделиться

Я не уверен, что перемещение просмотра - это правильный подход, я сделал это другим способом, размер UIScrollView. Я объяснил это в деталях на небольшом статьи

23
ответ дан 22 November 2019 в 20:09
поделиться

Код RPDP успешно перемещает текстовое поле в сторону от клавиатуры. Но когда вы прокручиваете вверх после использования и отключения клавиатуры, верхняя часть прокручивается вверх из поля зрения. Это верно как для симулятора, так и для устройства. Чтобы прочитать содержимое в верхней части этого представления, необходимо перезагрузить представление.

Разве его следующий код не должен вернуть представление?

else
{
    // revert back to the normal state.
    rect.origin.y += kOFFSET_FOR_KEYBOARD;
    rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
30
ответ дан 22 November 2019 в 20:09
поделиться

Небольшое исправление, которое работает для многих 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]; 
}
31
ответ дан 22 November 2019 в 20:09
поделиться

Чтобы вернуться в исходное состояние просмотра, добавьте:

-(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];
    }
}
22
ответ дан 22 November 2019 в 20:09
поделиться

Вот хакерское решение, которое я придумал для конкретного макета. Это решение похоже на решение Мэтта Галлахера в том, что оно прокручивает секцию в поле зрения. Я все еще новичок в разработке iPhone и не знаком с принципами работы макетов. Итак, этот хак.

Моя реализация должна была поддерживать прокрутку при щелчке по полю, а также прокрутку, когда пользователь нажимает «Далее» на клавиатуре.

У меня был UIView высотой 775. Элементы управления разбросаны в основном группами по 3 человека на большом пространстве. В итоге у меня получился следующий макет IB.

UIView -> UIScrollView -> [UI Components]

А вот и хитрость

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

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
ответ дан 22 November 2019 в 20:09
поделиться

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

Вот некоторые вещи, которые следует учитывать, если ваш UIScrollView не прокручивается должным образом.

1) Убедитесь, что ваш contentSize больше, чем размер кадра UIScrollView . Способ понять UIScrollViews состоит в том, что UIScrollView подобен окну просмотра содержимого, определенного в contentSize. Поэтому, когда для UIScrollview прокрутка в любом месте, contentSize должен быть больше, чем UIScrollView . В противном случае прокрутка не требуется, поскольку все, что определено в contentSize, уже отображается. Кстати, contentSize по умолчанию = CGSizeZero .

2) Теперь, когда вы понимаете, что UIScrollView на самом деле является окном в ваше «содержимое», это способ убедиться, что клавиатура не закрывает «окно» просмотра UIScrollView было бы изменить размер UIScrollView так, чтобы при наличии клавиатуры у вас было окно UIScrollView размером только с исходным UIScrollView frame.size.height минус высота клавиатуры. Это гарантирует, что ваше окно будет иметь только ту небольшую видимую область.

3) Вот загвоздка: когда я впервые реализовал это, я решил, что мне нужно получить CGRect отредактированного текстового поля и вызвать метод scrollRecToVisible UIScrollView. Я реализовал метод UITextFieldDelegate textFieldDidBeginEditing с вызовом метода scrollRecToVisible . На самом деле это сработало со странным побочным эффектом: прокрутка привязывала UITextField к месту. Долгое время я не мог понять, что это было. Затем я закомментировал метод делегирования textFieldDidBeginEditing , и все заработало !! (???). Как оказалось, я считаю, что 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 установлен и больше, чем ваш UIScrollView в viewDidLoad
  4. Сжать UIScrollView при наличии клавиатуры
  5. Вернуть назад UIScrollView , когда клавиатура отключится.
  6. Используйте ivar, чтобы определить, отображается ли клавиатура уже на экране, поскольку уведомления клавиатуры отправляются каждый раз, когда UITextField вкладывается, даже если клавиатура уже присутствует, чтобы избежать сжатия ] UIScrollView , когда он уже сжался

Следует отметить, что UIKeyboardWillShowNotification срабатывает, даже если клавиатура уже находится на экране, когда вы нажимаете другой UITextField . Я позаботился об этом, используя ivar, чтобы избежать изменения размера UIScrollView , когда клавиатура уже находится на экране. Случайное изменение размера UIScrollView , когда клавиатура уже там, будет иметь катастрофические последствия!

Надеюсь, этот код избавит некоторых из вас от головной боли.

445
ответ дан 22 November 2019 в 20:09
поделиться

- Шоу SwiftUI

только активное TextField

Это переместит представление достаточно только, чтобы не скрывать только активный TextField. When each TextField is clicked, the view is only moved up enough to make the clicked text field visible.

struct ContentView: View {
    @ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 3)
    @State private var name = Array<String>.init(repeating: "", count: 3)

    var body: some View {

        VStack {
            Group {
                Text("Some filler text").font(.largeTitle)
                Text("Some filler text").font(.largeTitle)
            }

            TextField("text #1", text: $name[0], onEditingChanged: { if [110] { self.kGuardian.showField = 0 } })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[0]))

            TextField("text #2", text: $name[1], onEditingChanged: { if [110] { self.kGuardian.showField = 1 } })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[1]))

            TextField("text #3", text: $name[2], onEditingChanged: { if [110] { self.kGuardian.showField = 2 } })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[2]))

            }.offset(y: kGuardian.slide).animation(.easeInOut(duration: 0.25))
    }

}

Шоу весь TextField s

Это перемещается весь текстовое поле, если клавиатура появляется для какого-либо из них. Но только если необходимый. Если клавиатура не скроет текстовые поля, то они не переместятся. When the keyboard is opened, the 3 textfields are moved up enough to keep then all visible

struct ContentView: View {
    @ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 1)
    @State private var name = Array<String>.init(repeating: "", count: 3)

    var body: some View {

        VStack {
            Group {
                Text("Some filler text").font(.largeTitle)
                Text("Some filler text").font(.largeTitle)
            }

            TextField("enter text #1", text: $name[0])
                .textFieldStyle(RoundedBorderTextFieldStyle())

            TextField("enter text #2", text: $name[1])
                .textFieldStyle(RoundedBorderTextFieldStyle())

            TextField("enter text #3", text: $name[2])
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[0]))

        }.offset(y: kGuardian.slide).animation(.easeInOut(duration: 0.25))
    }

}

Оба примера используют те же общие коды: GeometryGetter и KeyboardGuardian, Вдохновленный @kontiki

GeometryGetter

, Это - представление, которое поглощает размер и положение его родительского представления. Инкапсулируют описание здесь для достижения этого, это называют в .background модификаторе. Это - очень мощный модификатор, не только способ украсить фон представления. При передаче представления .background (MyView ()), MyView получает измененное представление как родителя. Используя GeometryReader то, что позволяет представлению знать геометрию родителя.

, Например: Text("hello").background(GeometryGetter(rect: $bounds)) заполнит переменные границы, с размером и положением текстового представления и использования пространства глобальной координаты.

struct GeometryGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { geometry in
            Group { () -> AnyView in
                DispatchQueue.main.async {
                    self.rect = geometry.frame(in: .global)
                }

                return AnyView(Color.clear)
            }
        }
    }
}

Примечание, которое эти DispatchQueue.main.async должно избежать возможности изменения состояния представления, в то время как это представляется.

KeyboardGuardian

цель KeyboardGuardian, должен отслеживать клавиатуру, показывают/скрывают события и вычисляют, сколько пространства представление должно быть смещено.

Примечание, что это обновляет слайд, когда пользовательские вкладки от одного поля до другого*

import SwiftUI
import Combine

final class KeyboardGuardian: ObservableObject {
    public var rects: Array<CGRect>
    public var keyboardRect: CGRect = CGRect()

    // keyboardWillShow notification may be posted repeatedly,
    // this flag makes sure we only act once per keyboard appearance
    public var keyboardIsHidden = true

    @Published var slide: CGFloat = 0

    var showField: Int = 0 {
        didSet {
            updateSlide()
        }
    }

    init(textFieldCount: Int) {
        self.rects = Array<CGRect>(repeating: CGRect(), count: textFieldCount)

        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardDidHide(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil)

    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @objc func keyBoardWillShow(notification: Notification) {
        if keyboardIsHidden {
            keyboardIsHidden = false
            if let rect = notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect {
                keyboardRect = rect
                updateSlide()
            }
        }
    }

    @objc func keyBoardDidHide(notification: Notification) {
        keyboardIsHidden = true
        updateSlide()
    }

    func updateSlide() {
        if keyboardIsHidden {
            slide = 0
        } else {
            let tfRect = self.rects[self.showField]
            let diff = keyboardRect.minY - tfRect.maxY

            if diff > 0 {
                slide += diff
            } else {
                slide += min(diff, 0)
            }

        }
    }
}
2
ответ дан 22 November 2019 в 20:09
поделиться