ti-enxame.com

Função UITextField acima do UITableViewCell em um UIViewController regular

Eu tentei a maioria dos exemplos aqui no StackOverflow. Eu também usei o da Apple. O problema que parece ter com eles é que eles não contam para o UITextField estar em um UITableView. Já fiz isso muitas vezes, mas não desse jeito. Eu tenho um UITableViewCell personalizado com um UITextField nele.

No meu UITableView (que não é um UITableViewController), eu preciso ser capaz de evitar esconder o UITextField sob o UITableView.

Eu tenho isso a partir de agora:

-(void)viewDidLoad
{
....
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

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

....
}

- (void)scrollToRectOfTextField {
    UITableViewCell *cell = (UITableViewCell*)[self.activeTextField superview];
    CGRect r = CGRectMake(self.activeTextField.frame.Origin.x,
                          cell.frame.Origin.y+self.activeTextField.frame.Origin.y,
                          self.activeTextField.frame.size.width,
                          self.activeTextField.frame.size.height);
    [self.tableView scrollRectToVisible:r animated:YES];
}

- (void)keyboardDidShow:(NSNotification *)notification
{
    if (UI_USER_INTERFACE_IDIOM()== UIUserInterfaceIdiomPhone) {
        NSDictionary *userInfo = [notification userInfo];
        CGSize size = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
        NSLog(@"TableView: %@", NSStringFromCGRect(self.tableView.frame));
        CGRect newTableViewFrame = CGRectMake(self.tableView.frame.Origin.x,
                                              self.tableView.frame.Origin.y,
                                              self.tableView.frame.size.width, self.tableView.frame.size.height - size.height);
        self.tableView.frame = newTableViewFrame;
        NSLog(@"New TableView: %@", NSStringFromCGRect(self.tableView.frame));


        self.tableView.contentSize = CGSizeMake(self.tableView.contentSize.width, self.tableView.contentSize.height-size.height);
    }
}


- (void)keyboardWillHide:(NSNotification *)notification
{
    NSDictionary *userInfo = [notification userInfo];
    CGSize size = [[userInfo objectForKey: UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
    self.tableView.frame = CGRectMake(self.tableView.frame.Origin.x,
                                      self.tableView.frame.Origin.y,
                                      self.tableView.frame.size.width,
                                      self.tableView.frame.size.height + size.height);

    NSLog(@"%@", NSStringFromCGRect(self.tableView.frame));
}


-(void)textFieldDidBeginEditing
{
     [self scrollToRectOfTextField];
}

Isso parece empurrar um monte de espaço em branco acima do meu teclado. Por favor, note também que eu tenho um inputAccessoryView no meu UITextField também.

Ah, e eu não posso usar classes de terceiros. Eu amo TPKeyboardAvoidingScrollView, mas não posso usar nada de terceiros.

20
Matt Hudson

Passei o dia todo tentando descobrir isso. Eu postei aqui, então encontrei um link de blog e uma solução incrivelmente simples. Se parece com isso:

-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
    CGPoint pointInTable = [textField.superview convertPoint:textField.frame.Origin toView:self.tableView];
    CGPoint contentOffset = self.tableView.contentOffset;

    contentOffset.y = (pointInTable.y - textField.inputAccessoryView.frame.size.height);

    NSLog(@"contentOffset is: %@", NSStringFromCGPoint(contentOffset));

    [self.tableView setContentOffset:contentOffset animated:YES];

    return YES;
}


-(BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
    [textField resignFirstResponder];

    if ([textField.superview.superview isKindOfClass:[UITableViewCell class]])
    {
        UITableViewCell *cell = (UITableViewCell*)textField.superview.superview;
        NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

        [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:TRUE];
    }

    return YES;
}

Verifique isso para iOS 8

49
Matt Hudson

Eu tentei o link que @inturbidus postou para iOS8 mas infelizmente não funcionou para mim. Depois de algumas pesquisas, a Apple tem código de exemplo em seu site para fazer isso funcionar naturalmente como no UITableViewController. Aqui está o link da Apple para a versão Objective-C e também estou adicionando a versão Swift aqui.

Link Objective-C da Apple (Role para listagem 5-1): https://developer.Apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement. html

Adaptação rápida da versão Objective-C  

var activeField: UITextField?

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

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

func registerForKeyboardNotifications() {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
}    

func keyboardWasShown(aNotification: NSNotification) {
    let info = aNotification.userInfo as! [String: AnyObject],
    kbSize = (info[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue().size,
    contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: kbSize.height, right: 0)

    self.tableView.contentInset = contentInsets
    self.tableView.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, activeField!.frame.Origin) {
        self.tableView.scrollRectToVisible(activeField!.frame, animated: true)
    }
}

func keyboardWillBeHidden(aNotification: NSNotification) {
    let contentInsets = UIEdgeInsetsZero
    self.tableView.contentInset = contentInsets
    self.tableView.scrollIndicatorInsets = contentInsets
}
21
Salman Hasrat Khan

Obrigado @Salman pelo link.
Apenas observe que o exemplo da Apple é usado para scrollview na visualização normal.
Na exibição de tabela, você deve converter Point e Rect de activeField em coordenadas da tabela, portanto, altero algumas linhas na função keyboardWasShown:

func keyboardWasShown(aNotification: NSNotification) {
        let info = aNotification.userInfo as! [String: AnyObject],
        kbSize = (info[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue().size,
        contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: kbSize.height, right: 0)

        self.tableBasket.contentInset = contentInsets
        self.tableBasket.scrollIndicatorInsets = contentInsets

        var aRect = self.view.frame
        aRect.size.height -= kbSize.height

        let pointInTable = activeField!.superview!.convertPoint(activeField!.frame.Origin, toView: tableView)
        let rectInTable = activeField!.superview!.convertRect(activeField!.frame, toView: tableView)

        if !CGRectContainsPoint(aRect, pointInTable) {
            self.tableView.scrollRectToVisible(rectInTable, animated: true)
        }
}

E se a sua visualização tiver tabBarController, lembre-se de redefinir contentInsets com a altura tabBar em vez de UIEdgeInsetsZero (se não, suas últimas linhas inferiores talvez ocultadas em tabBar):

func keyboardWillBeHidden(aNotification: NSNotification) {
        //UIEdgeInsetsZero is used in view without tabBar
        //let contentInsets = UIEdgeInsetsZero
        let tabBarHeight = self.tabBarController!.tabBar.frame.height
        let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: tabBarHeight, right: 0)
        self.tableView.contentInset = contentInsets
        self.tableView.scrollIndicatorInsets = contentInsets
    }
}
7
Nhan Lam Dai

Eu fiz uma mistura com as respostas de Matt e também Salman . Isso funciona para muitos campos de texto em um tableView. Swift 3

Basicamente é obter o BOTTOM Y ponto do texto de entrada tocado. Uma vez que temos que eu verifico se o teclado cobre o textInput e se fizer isso eu alteraria o contentOffset do tableView

Primeiro registre-se nas notificações. No init se é um UIView ou em viewDidLoad se é um UIViewController:

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

Em seguida, configure o campo de texto tocado como entrada atual

var inputActive: UITextField!

func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
    inputActive = textInput
    return true
}

Por fim, implemente o método de notificações:

func keyboardWillShow(notification: NSNotification) {
    var userInfo = notification.userInfo!
    if let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        // Get my height size
        let myheight = tableView.frame.height
        // Get the top Y point where the keyboard will finish on the view
        let keyboardEndPoint = myheight - keyboardFrame.height
        // Get the the bottom Y point of the textInput and transform it to the currentView coordinates.
        if let pointInTable = inputActive.superview?.convert(inputActive.frame.Origin, to: tableView) {
            let textFieldBottomPoint = pointInTable.y + inputActive.frame.size.height + 20
            // Finally check if the keyboard will cover the textInput
            if keyboardEndPoint <= textFieldBottomPoint {
                tableView.contentOffset.y = textFieldBottomPoint - keyboardEndPoint
            } else {
                tableView.contentOffset.y = 0
            }
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    tableView.contentOffset.y = 0
}

Poucas coisas para adicionar. O preenchimento é alguma distância extra que eu adicionei no meu caso foi de 20 pontos. Também no meu caso, o UITableView foi adicionado a um UIViewController, mas isso deve funcionar também em um UITableViewController

Espero que isto ajude!

7
Victor

Minha solução (trabalhada no Xcode 8.3.3, Swift 3, iOS 10.3.1):

class MyTableViewController: UITableViewController {

    override func viewWillAppear(_ animated: Bool) {
        NotificationCenter.default.addObserver(self, selector: #selector(actionKeyboardDidShow(with:)), name: .UIKeyboardDidShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(actionKeyboardWillHide(with:)), name: .UIKeyboardWillHide, object: nil)
    }

    override func viewDidDisappear(_ animated: Bool) {
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidShow, object: nil)
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
    }

    @objc private func actionKeyboardDidShow(with notification: Notification) {
        guard let userInfo = notification.userInfo as? [String: AnyObject],
            let keyboardFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
            else { return }

        var contentInset = self.tableView.contentInset
        contentInset.bottom += keyboardFrame.height

        self.tableView.contentInset = contentInset
        self.tableView.scrollIndicatorInsets = contentInset
    }

    @objc private func actionKeyboardWillHide(with notification: Notification) {
        guard let userInfo = notification.userInfo as? [String: AnyObject],
            let keyboardFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
            else { return }

        var contentInset = self.tableView.contentInset
        contentInset.bottom -= keyboardFrame.height

        self.tableView.contentInset = contentInset
        self.tableView.scrollIndicatorInsets = contentInset
    }

}
2
jqgsninimo

Eu fiz uma mistura entre a resposta do @Victor e algum código que eu tinha. Criei uma extensão útil que pode ser usada em muitos UITextField.

1.- Primeiro precisamos de uma extensão para o UITextField para gerenciar as notificações do teclado

extension UITextField {

    func keepTextFieldAboveKeyboard(tableView:UITableView) {

        var willShowNotification: NSObjectProtocol?
        var willHideNotification: NSObjectProtocol?

        willShowNotification = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillShow, object: nil, queue: OperationQueue.main) {(notification) in

            var userInfo = notification.userInfo!

            if let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
                // Get my height size
                let myheight = tableView.frame.height
                // Get the top Y point where the keyboard will finish on the view
                let keyboardEndPoint = myheight - keyboardFrame.height
                // Get the the bottom Y point of the textInput and transform it to the currentView coordinates.
                if let pointInTable = self.superview?.convert(self.frame.Origin, to: tableView) {

                    let textFieldBottomPoint = pointInTable.y + self.frame.size.height + 20

                    // Finally check if the keyboard will cover the textInput
                    if keyboardEndPoint <= textFieldBottomPoint {

                        tableView.contentOffset.y = textFieldBottomPoint - keyboardEndPoint
                    } else {

                        tableView.contentOffset.y = 0
                    }
                }
            }

            NotificationCenter.default.removeObserver(willShowNotification!)
        }

        willHideNotification = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillHide, object: nil, queue: OperationQueue.main) { (notification) in

            tableView.contentOffset.y = 0

            NotificationCenter.default.removeObserver(willHideNotification!)
        }

    }

}

2.- Esta extensão anterior deve ser chamada dentro de textFieldShouldBeginEditing então podemos ter uma extensão para isso também

extension UITextField : UITextFieldDelegate {

    public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool  {

        guard let tableView = textField.parentView(of: UITableView.self) else { return true };

        textField.keepTextFieldAboveKeyboard(tableView:tableView);

        return true;

    }
}

3.- Finalmente, como você observa no método anterior, precisamos do TableView onde a célula com o UITextField está localizada. Então podemos usar essa extensão (que também é útil para outros propósitos)

extension UIView {
    func parentView<T: UIView>(of type: T.Type) -> T? {
        guard let view = self.superview else {
            return nil
        }
        return (view as? T) ?? view.parentView(of: T.self)
    }
}

4.- Finalmente na célula onde o UITextField está localizado , apenas escrevemos esta simples linha de código

textField.delegate = textField;

Completamente reutilizável e universal.

1
Joaquin Pereira

Esta resposta é para os usuários do Xamarin iOS, no entanto, alguém pode modificá-lo facilmente para fazê-lo funcionar com o Swift ou o Objective C.

A solução é muito simples e funcionou perfeitamente no meu caso em que eu precisava que uma célula aparecesse na metade superior da tela para que o campo de texto da célula ficasse acima do teclado. Então o que eu fiz é o seguinte:

Eu implementei textField.ShouldBeginEditing da seguinte maneira:

myTextField.ShouldBeginEditing = textField =>
{
   myTableView.ScrollToRow(indexPath, UITableViewScrollPosition.Top, true);

   return true;
};

Onde: 

myTextField é o campo de texto da célula,

myTableView é a visualização de tabela,

indexPath é o caminho do índice da célula dentro da visão de tabela.

Como eu mencionei anteriormente, esta solução funciona perfeitamente no meu caso, então eu pensei que poderia compartilhá-la com vocês, caso isso funcione para você também. Codificação feliz :)

1
Georgios Iniatis

Solução de jqgsninimo atualizada para 4.2. Funciona no iOS 12.0.

Swift 4.2

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow(with:)), name: UIResponder.keyboardDidShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(with:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidShowNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}

@objc func keyboardDidShow(with notification: Notification) {
    guard let userInfo = notification.userInfo as? [String: AnyObject],
        let keyboardFrame = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        else { return }

    var contentInset = self.tableView.contentInset
    contentInset.bottom += keyboardFrame.height

    tableView.contentInset = contentInset
    tableView.scrollIndicatorInsets = contentInset
}

@objc func keyboardWillHide(with notification: Notification) {
    guard let userInfo = notification.userInfo as? [String: AnyObject],
        let keyboardFrame = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        else { return }

    var contentInset = self.tableView.contentInset
    contentInset.bottom -= keyboardFrame.height

    tableView.contentInset = contentInset
    tableView.scrollIndicatorInsets = contentInset
}
1
Jayden Irwin

Role UITextField acima do teclado em um UITableViewCell em um UIViewController Swift 3 e Xcode 8.1  

1) set Delegate = UITextFieldDelegate 

  override func viewWillAppear(_ animated: Bool) {

        NotificationCenter.default.addObserver(self, selector: #selector(DayViewController.keyboardWillShow), name:NSNotification.Name.UIKeyboardWillShow, object: nil);
        NotificationCenter.default.addObserver(self, selector: #selector(DayViewController.keyboardWillHide), name:NSNotification.Name.UIKeyboardWillHide, object: nil);

   }
      func textFieldDidBeginEditing(_ textField: UITextField) {
          self.activeField = textField
   }
      func textFieldDidEndEditing(_ textField: UITextField) {
        self.activeField = nil
   }


//MARK: - Keyboard Show and Hide Methods
    func keyboardWillShow(notification: NSNotification)
    {
        let info = notification.userInfo! as! [String: AnyObject],
        kbSize = (info[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue.size,
        contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: kbSize.height, right: 0)

        self.FourthTblMainTableView.contentInset = contentInsets
        self.FourthTblMainTableView.scrollIndicatorInsets = contentInsets

        var aRect = self.FourthTblMainTableView.frame
        aRect.size.height -= kbSize.height
    }
    func keyboardWillHide(notification: NSNotification)
    {
         let contentInsets = UIEdgeInsets.zero
        self.FourthTblMainTableView.contentInset = contentInsets
        self.FourthTblMainTableView.scrollIndicatorInsets = contentInsets
    }
1
Parth Changela

Em Swift4 podemos criar uma extensãode UIViewController .

extension UIViewController {
    //MARK: Keyboard user interactions
    func handleContainerViewFrameOnKeyboardShowHide(originalContainerFrame: CGRect) {
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillShow, object: nil, queue: OperationQueue.main) {(notification) in
            var info = (notification as NSNotification).userInfo!
            let keyboardFrame: CGRect = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
            var tableViewFrame = originalContainerFrame
            tableViewFrame.size.height = originalContainerFrame.size.height - keyboardFrame.size.height
            self.view.frame = tableViewFrame
        }
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillHide, object: nil, queue: OperationQueue.main) { (notification) in
            self.view.frame = originalContainerFrame
        }
    }
}
0
Susim Samanta