Tworzenie przewijania UITableView po wybraniu pola tekstowego


251

Po wielu próbach i błędach poddaję się i zadaję pytanie. Widziałem wiele osób z podobnymi problemami, ale nie mogę uzyskać wszystkich odpowiedzi.

Mam taki, UITableViewktóry składa się z niestandardowych komórek. Komórki składają się z 5 pól tekstowych obok siebie (coś w rodzaju siatki).

Kiedy próbuję przewijać i edytować komórki u dołu UITableView, nie mogę prawidłowo ustawić komórek nad klawiaturą.

Widziałem wiele odpowiedzi na temat zmiany rozmiarów widoków itp., Ale żadna z nich do tej pory nie działała dobrze.

Czy ktoś mógłby wyjaśnić „właściwy” sposób na zrobienie tego na konkretnym przykładzie z kodem?


11
W niniejszej dokumentacji Applle opisano kroki, które należy wykonać, aby rozwiązać to pytanie. http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
ChrisP

@ChrisP Ten link stwierdza, że ​​nie został zaktualizowany dla iOS 4.0
Bae

Ten kod może być pomocny: gist.github.com/TimMedcalf/9505416
landonandrey

Postępuj zgodnie z poniższym
Venkatesh G

Odpowiedzi:


126

Jeśli użyjesz UITableViewController zamiast UIViewController, zrobi to automatycznie.


13
Czy próbowałeś i okazało się, że to nie działa? A może rozwiązanie jest zbyt proste, abyś w to uwierzył? Po prostu rozszerz UITableViewController zamiast UIViewController, a komórka zawierająca pola tekstowe przewinie się nad klawiaturą, gdy pola tekstowe staną się pierwszą odpowiedzią. Nie wymaga dodatkowego kodu.
Sam Ho,

3
Tak, ale szczególnie na iPadzie potrzebujemy sposobu, który nie wymaga użycia UITableViewController.
Bob Spryn

13
Aby to wyjaśnić, nie jest rozsądną odpowiedzią stwierdzenie, że za każdym razem, gdy korzystasz z widoku tabeli, musi to być pełny ekran, szczególnie na iPadzie. Istnieje mnóstwo przykładów świetnych aplikacji, które tego nie robią. Na przykład wiele własnych produktów Apple, w tym aplikacja Kontakty na iPadzie.
Bob Spryn

32
Nie zadziała, jeśli przesłonisz [super viewWillAppear: YES]. Poza tym powinien działać.
Rambatino

18
Jeśli zastąpisz viewWillAppear: (BOOL) animowany, nie zapomnij zadzwonić [super viewWillAppear: animated]; :)
Médéric Petit

93

Funkcja przewijania może być znacznie prostsza:

- (void) textFieldDidBeginEditing:(UITextField *)textField {
    UITableViewCell *cell;

    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
    // Load resources for iOS 6.1 or earlier
        cell = (UITableViewCell *) textField.superview.superview;

    } else {
        // Load resources for iOS 7 or later
        cell = (UITableViewCell *) textField.superview.superview.superview; 
       // TextField -> UITableVieCellContentView -> (in iOS 7!)ScrollView -> Cell!
    }
    [tView scrollToRowAtIndexPath:[tView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

Otóż ​​to. Żadnych obliczeń.


2
Czemu nie?! Wystarczy zamienić UITableViewScrollPositionTop na UITableViewScrollPositionMiddle. Oczywiście wystarczy przeskalować UITableView, aby dostosować widoczny obszar.
Mihai Damian,

3
Nie działa, jeśli UITableViewController zadbał o zmianę rozmiaru widoku tabeli, gdy pokazana jest klawiatura: kontroler zmniejsza widoczny rozmiar za pomocą a contentInset, który najwyraźniej nie jest brany pod uwagę przy pytaniu o visibleRowslub indexPathsForVisibleRows.
Julian D.

16
Nie działa dla kilku ostatnich wierszy widoku tabeli. Klawiatura nadal zasłania wszystkie wiersze, których nie można przewijać nad klawiaturą.
Alex Zavatone

3
Aby działanie automatycznego przewijania działało na ostatnich kilku wierszach tabeli, wykryj, kiedy te wiersze zaczną się edytować, i dodaj stopkę na końcu widoku tabeli z pustym widokiem o określonej wysokości. Umożliwi to widokowi tabeli przewijanie komórek w odpowiednie miejsce.
Sammio2

10
Dostanie się do komórki przez szereg wezwań do nadzoru jest niewiarygodne, chyba że upewnisz się, że faktycznie docierasz do komórki. Zobacz stackoverflow.com/a/17757851/1371070 i stackoverflow.com/a/17758021/1371070
Cezar

70

Robię coś bardzo podobnego, to jest ogólne, nie ma potrzeby obliczania czegoś konkretnego dla twojego kodu. Sprawdź uwagi w kodzie:

W MyUIViewController.h

@interface MyUIViewController: UIViewController <UITableViewDelegate, UITableViewDataSource>
{
     UITableView *myTableView;
     UITextField *actifText;
}

@property (nonatomic, retain) IBOutlet UITableView *myTableView;
@property (nonatomic, retain) IBOutlet UITextField *actifText;

- (IBAction)textFieldDidBeginEditing:(UITextField *)textField;
- (IBAction)textFieldDidEndEditing:(UITextField *)textField;

-(void) keyboardWillHide:(NSNotification *)note;
-(void) keyboardWillShow:(NSNotification *)note;

@end

W MyUIViewController.m

@implementation MyUIViewController

@synthesize myTableView;
@synthesize actifText;

- (void)viewDidLoad 
{
    // Register notification when the keyboard will be show
    [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(keyboardWillShow:)
                                          name:UIKeyboardWillShowNotification
                                          object:nil];

    // Register notification when the keyboard will be hide
    [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(keyboardWillHide:)
                                          name:UIKeyboardWillHideNotification
                                          object:nil];
}

// To be link with your TextField event "Editing Did Begin"
//  memoryze the current TextField
- (IBAction)textFieldDidBeginEditing:(UITextField *)textField
{
    self.actifText = textField;
}

// To be link with your TextField event "Editing Did End"
//  release current TextField
- (IBAction)textFieldDidEndEditing:(UITextField *)textField
{
    self.actifText = nil;
}

-(void) keyboardWillShow:(NSNotification *)note
{
    // Get the keyboard size
    CGRect keyboardBounds;
    [[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];

    // Detect orientation
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    CGRect frame = self.myTableView.frame;

    // Start animation
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:0.3f];

    // Reduce size of the Table view 
    if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
        frame.size.height -= keyboardBounds.size.height;
    else 
        frame.size.height -= keyboardBounds.size.width;

    // Apply new size of table view
    self.myTableView.frame = frame;

    // Scroll the table view to see the TextField just above the keyboard
    if (self.actifText)
      {
        CGRect textFieldRect = [self.myTableView convertRect:self.actifText.bounds fromView:self.actifText];
        [self.myTableView scrollRectToVisible:textFieldRect animated:NO];
      }

    [UIView commitAnimations];
}

-(void) keyboardWillHide:(NSNotification *)note
{
    // Get the keyboard size
    CGRect keyboardBounds;
    [[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];

    // Detect orientation
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    CGRect frame = self.myTableView.frame;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:0.3f];

    // Increase size of the Table view 
    if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
        frame.size.height += keyboardBounds.size.height;
    else 
        frame.size.height += keyboardBounds.size.width;

    // Apply new size of table view
    self.myTableView.frame = frame;

    [UIView commitAnimations];
}

@end

Wersja Swift 1.2+:

class ViewController: UIViewController, UITextFieldDelegate {
    @IBOutlet weak var activeText: UITextField!
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: Selector("keyboardWillShow:"),
            name: UIKeyboardWillShowNotification,
            object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: Selector("keyboardWillHide:"),
            name: UIKeyboardWillHideNotification,
            object: nil)
    }

    func textFieldDidBeginEditing(textField: UITextField) {
        activeText = textField
    }

    func textFieldDidEndEditing(textField: UITextField) {
        activeText = nil
    }

    func keyboardWillShow(note: NSNotification) {
        if let keyboardSize = (note.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            var frame = tableView.frame
            UIView.beginAnimations(nil, context: nil)
            UIView.setAnimationBeginsFromCurrentState(true)
            UIView.setAnimationDuration(0.3)
            frame.size.height -= keyboardSize.height
            tableView.frame = frame
            if activeText != nil {
                let rect = tableView.convertRect(activeText.bounds, fromView: activeText)
                tableView.scrollRectToVisible(rect, animated: false)
            }
            UIView.commitAnimations()
        }
    }

    func keyboardWillHide(note: NSNotification) {
        if let keyboardSize = (note.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            var frame = tableView.frame
            UIView.beginAnimations(nil, context: nil)
            UIView.setAnimationBeginsFromCurrentState(true)
            UIView.setAnimationDuration(0.3)
            frame.size.height += keyboardSize.height
            tableView.frame = frame
            UIView.commitAnimations()
        }
    }
}

korzystanie z powiadomień i uzyskiwanie wysokości klawiatury z uwzględnieniem orientacji urządzenia było niesamowite, dzięki za to! część przewijania nie działała dla mnie z jakiegoś powodu, więc musiałem użyć tego:[tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionMiddle animated: YES];
Taber

7
To chyba najlepsza odpowiedź tutaj. Bardzo czysto. Tylko dwie rzeczy: 1) Twój viewDidLoad nie wywołuje [super viewDidLoad] i 2) Musiałem mieć w matematyce tabbar na liniach frame.size.height. W przeciwnym razie idealne! Dzięki.
toxaq

3
Oto modyfikacja, którą opisuje toksaq: MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate]; CGFloat tabBarHeight = appDelegate.tabBarController.tabBar.frame.size.height; Następnie odejmij tabBarHeight od wysokości klawiatury, gdziekolwiek używasz wysokości klawiatury.
Steve N,

1
Jeśli użytkownik dotknie pola tekstowego, działa idealnie. ale jeśli użytkownik dotknie innego pola tekstowego bez naciskania klawisza Return, zmniejszy to rozmiar widoku tabeli.
Bhavin Ramani,

1
@BhavinRamani zgodził się. Dodałem prostą właściwość boolean, aby zapamiętać, czy klawiatura jest już wyświetlana, czy nie, i pomiń ponowne wykonanie kodu, gdy nie jest to konieczne.
Dirty Henry,

46

Najprostsze rozwiązanie dla Swift 3 , oparte na rozwiązaniu Bartłomieja Semańczyka :

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

// MARK: Keyboard Notifications

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    UIView.animate(withDuration: 0.2, animations: {
        // For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
        self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
    })
}

Drobny szczegół ... NotificationZamiast tego NSNotificationbyłoby więcej „Swift 3-y” :-)
Nicolas Miari

Pomoże to w zmianie położenia, jeśli istnieje pasek nawigacyjny - otaczający UIView.animate z tym, jeśli let - if let frame = self.navigationController? .NavigationBar.frame {let y = frame.size.height + frame.origin.y}
Sean Dev

kiedy następuje obrót, pojawia się usterka w ładowaniu, a niektóre komórki znikają, gdy widok tabeli jest przewijany
ręcznie

Dobre rozwiązanie dzięki! Uwaga - nie musisz już wykonywać operacji removeObserver.
Nick McConnell,

44

Miałem ten sam problem, ale zauważyłem, że pojawia się tylko w jednym widoku. Zacząłem więc szukać różnic w kontrolerach.

Dowiedziałem się, że zachowanie przewijania jest ustawione w - (void)viewWillAppear:(BOOL)animatedsuper wystąpieniu.

Dlatego pamiętaj o wdrożeniu w ten sposób:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // your code
}

I nie ma znaczenia, czy używasz UIViewControllerlub UITableViewController; sprawdziłem to, umieszczając UITableViewjako subview self.view w UIViewController. To było to samo zachowanie. Widok nie pozwalał na przewijanie, jeśli [super viewWillAppear:animated];brakowało połączenia .


1
To działało doskonale. Zastanawiałem się, dlaczego ludzie mówili, że UITableView zrobi to za mnie i to rozwiązało. Dzięki!
olivaresF

5
Miałem również ten problem, ta odpowiedź powinna znaleźć się na szczycie!
Amiel Martin

Straciłem tyle czasu, próbując to
rozgryźć

+1 zaczynało trochę płakać, miałem tę linię, ale potrzebowałem też [tableViewController viewWillAppear: animated]; ponieważ dodam UITableViewController do UIViewController. nigdy więcej łez :)
Colin Lamarre

41

mogłem to przegapić, ponieważ nie przeczytałem tutaj całego postu, ale to, co wymyśliłem, wydaje się zwodniczo proste. nie przeszedłem tego przez pisarza, testując we wszystkich sytuacjach, ale wygląda na to, że powinno działać dobrze.

po prostu dostosuj contentInset widoku tabeli o wysokość klawiatury, a następnie przewiń komórkę do dołu:

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

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
    self.myTableView.contentInset = contentInsets;
    self.myTableView.scrollIndicatorInsets = contentInsets;

    [self.myTableView scrollToRowAtIndexPath:self.currentField.indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

i oczywiście

- (void)keyboardWasHidden:(NSNotification *)aNotification
{
    [UIView animateWithDuration:.3 animations:^(void) 
    {
        self.myTableView.contentInset = UIEdgeInsetsZero;
        self.myTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
    }];
}

czy to jest zbyt proste? czy coś mi brakuje? jak dotąd działa dla mnie dobrze, ale jak powiedziałem, nie przełożyłem tego przez pisarza ...


IMO, to najlepsze rozwiązanie. Jedyne, co bym zmienił, to twój zakodowany czas trwania na[aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
Andy

To bardzo proste. Ale jednym z problemów jest to, że nie będzie animować zmiany contentInseti gwałtownie zmieniać granic przewijania.
Geek

Ten działał dla mnie najlepiej, jednak kilka problemów. 1) Nie wiem, skąd można pobrać „currentField.indexPath”, więc musiałem zapisać indexPath.row jako znacznik pola i utworzyć indexPath później. 2) Nie działa w przypadku wierszy u góry tabeli, przewija je poza ekran. Musiałem dodać trochę kodu, aby przewijać tylko, jeśli bieżąca ścieżka indeksu pola jest większa niż może zmieścić się na ekranie. 3) musiał użyć kbSize.Width (zamiast wysokości) na iPadzie, jeśli jest to krajobraz
Travis M.

przepraszamy, przyzwyczailiśmy się do własnego kodu, że czasami zapominamy, co? currentField to bieżące pole tekstowe, nad którym pracuję, a indexPath to rozszerzenie, które dodałem do klasy, które po prostu dodaje NSIndexPath, więc wiem, w której komórce to jest.
mickm

W ten sposób nie należy przesuwać ramek po prostu modyfikując właściwości tabeli.
Nextorlg

35

Myślę, że opracowałem rozwiązanie pasujące do zachowania aplikacji Apple.

Po pierwsze, w twoim widokuWillAppear: subskrybuj powiadomienia z klawiatury, abyś wiedział, kiedy klawiatura pokaże się i ukryje, a system powie ci rozmiar klawiatury, ale nie zapomnij wyrejestrować się z widokuWillDisappear :.

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

Zaimplementuj metody podobne do poniższych, aby dostosować rozmiar tabeliView, aby dopasować widoczny obszar po wyświetleniu klawiatury. Tutaj osobno śledzę stan klawiatury, więc mogę wybrać, kiedy sam ustawić tableView z powrotem na pełną wysokość, ponieważ otrzymujesz te powiadomienia przy każdej zmianie pola. Nie zapomnij wdrożyć keyboardWillHide: i wybierz odpowiednie miejsce, aby naprawić rozmiar tableView.

-(void) keyboardWillShow:(NSNotification *)note
{
    CGRect keyboardBounds;
    [[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &keyboardBounds];
    keyboardHeight = keyboardBounds.size.height;
    if (keyboardIsShowing == NO)
    {
        keyboardIsShowing = YES;
        CGRect frame = self.view.frame;
        frame.size.height -= keyboardHeight;

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationBeginsFromCurrentState:YES];
        [UIView setAnimationDuration:0.3f];
        self.view.frame = frame;
        [UIView commitAnimations];
    }
}

Oto bit przewijania, najpierw obliczamy kilka rozmiarów, a następnie widzimy, gdzie jesteśmy w widocznym obszarze, i ustawiamy prostokąt, który chcemy przewinąć, aby był albo widokiem połowy powyżej, albo poniżej środka pola tekstowego gdzie jest to w widoku. W tym przypadku mamy tablicę pól UITextField i wyliczenie, które je śledzi, więc pomnożenie rowHeight przez numer wiersza daje nam rzeczywiste przesunięcie ramki w tym zewnętrznym widoku.

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    CGRect frame = textField.frame;
    CGFloat rowHeight = self.tableView.rowHeight;
    if (textField == textFields[CELL_FIELD_ONE])
    {
        frame.origin.y += rowHeight * CELL_FIELD_ONE;
    }
    else if (textField == textFields[CELL_FIELD_TWO])
    {
        frame.origin.y += rowHeight * CELL_FIELD_TWO;
    }
    else if (textField == textFields[CELL_FIELD_THREE])
    {
        frame.origin.y += rowHeight * CELL_FIELD_THREE;
    }
    else if (textField == textFields[CELL_FIELD_FOUR])
    {
        frame.origin.y += rowHeight * CELL_FIELD_FOUR;
    }
    CGFloat viewHeight = self.tableView.frame.size.height;
    CGFloat halfHeight = viewHeight / 2;
    CGFloat midpoint = frame.origin.y + (textField.frame.size.height / 2);
    if (midpoint < halfHeight)
    {
        frame.origin.y = 0;
        frame.size.height = midpoint;
    }
    else
    {
        frame.origin.y = midpoint;
        frame.size.height = midpoint;
    }
    [self.tableView scrollRectToVisible:frame animated:YES];
}

To wydaje się działać całkiem nieźle.


Niezłe rozwiązanie. Dzięki za opublikowanie.
Alex Reynolds,

2
UIKeyboardBoundsUserInfoKeyjest przestarzałe od iOS 3.2. Zobacz moje rozwiązanie poniżej, które działa we wszystkich aktualnych wersjach iOS ≥ 3.0. / @ iPhoneDev
Ortwin Gentz

To było bardziej skomplikowane, niż trzeba. Odpowiedź @ user91083 była prosta i działa.
Richard Brightwell

1
W tym rozwiązaniu jest mały problem. keyboardWillShow nazywa się PO textFieldDidBeginEditing, więc kiedy chcemy przewinąć do jakiejś komórki, ramka tableView jeszcze się nie zmieniła, więc nie będzie działać
HiveHicks

35

Jeśli możesz użyć UITableViewController, otrzymasz tę funkcję za darmo. Czasami jednak nie jest to opcja, szczególnie jeśli potrzebujesz wielu widoków, nie tylko UITableView.

Niektóre przedstawione tutaj rozwiązania nie działają na iOS ≥4, niektóre nie działają na iPadzie lub w trybie poziomym, niektóre nie działają na klawiaturach Bluetooth (gdzie nie chcemy przewijania), niektóre nie działa podczas przełączania między wieloma polami tekstowymi. Jeśli więc wybierzesz jakieś rozwiązanie, sprawdź te przypadki. To rozwiązanie, którego używamy, używane w InAppSettingsKit :

- (void)_keyboardWillShow:(NSNotification*)notification {
    if (self.navigationController.topViewController == self) {
        NSDictionary* userInfo = [notification userInfo];

        // we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0
        NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"];
        if (!keyboardFrameValue) {
            keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"];
        }

        // Reduce the tableView height by the part of the keyboard that actually covers the tableView
        CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
        if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
            windowRect = IASKCGRectSwap(windowRect);
        }
        CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
        if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
            viewRectAbsolute = IASKCGRectSwap(viewRectAbsolute);
        }
        CGRect frame = _tableView.frame;
        frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
        [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
        _tableView.frame = frame;
        [UIView commitAnimations];

        UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview;
        NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell];

        // iOS 3 sends hide and show notifications right after each other
        // when switching between textFields, so cancel -scrollToOldPosition requests
        [NSObject cancelPreviousPerformRequestsWithTarget:self];

        [_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
    }
}

- (void) scrollToOldPosition {
  [_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

- (void)_keyboardWillHide:(NSNotification*)notification {
    if (self.navigationController.topViewController == self) {
        NSDictionary* userInfo = [notification userInfo];

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
        [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
        _tableView.frame = self.view.bounds;
        [UIView commitAnimations];

        [self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1];
    }
}   

Oto pełny kod klasy w InAppSettingsKit. Aby to przetestować, użyj okienka podrzędnego „Pełna lista”, w którym możesz przetestować wyżej wymienione scenariusze.


Nie wiem, czy warto używać ciągów zamiast stałych, ponieważ jeśli Apple z jakiegoś powodu wpadnie na pomysł wewnętrznej zmiany Ciągu, twoje rozwiązanie już nie działa. Podobnie nie otrzymałeś ostrzeżenia, gdy staje się przestarzałe. Myślę, że

@ iPortable: to nie jest idealne, wiem. Czy możesz zasugerować lepsze rozwiązanie, które działa na wszystkich wersjach ≥3,0?
Ortwin Gentz,

1
Działa jak urok, ale nie dla UIInterfaceOrientationPortraitUpsideDown. Następnie obliczenie zmniejszenia wysokości musi również opierać się na odwrocie: CGFloat redukcjaWysokość = keyboardRect.size.height - (CGRectGetMinY (viewRectAbsolute) - CGRectGetMinY (windowRect));
Klaas

Ma to bardzo zauważalne usterki wizualne na moim iPadzie i symulatorze (4.3). Zbyt zauważalny w użyciu. :(
Bob Spryn

Podoba mi się, że to rozwiązanie będzie stanowić pasek narzędzi u dołu ekranu.
pdemarest

24

Najprostsze rozwiązanie dla Swift :

override func viewDidLoad() {
    super.viewDidLoad()

    searchBar?.becomeFirstResponder()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillShow(_:)), name: UIKeyboardDidShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillHide(_:)), name: UIKeyboardDidHideNotification, object: nil)
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue.size.height {
            tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    UIView.animateWithDuration(0.2, animations: { self.table_create_issue.contentInset = UIEdgeInsetsMake(0, 0, 0, 0) })
    // For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
    }

Działa idealnie, potrzebne są minimalne obliczenia. Dodałem kod, który przywraca wstawki do tabeli, aby zakończyć odpowiedź.
Vitalii,

Najlepsze rozwiązanie dzięki. Tutaj opublikowałem wersję Swift 3: stackoverflow.com/a/41040630/1064438
squall2022

Super idealne rozwiązanie, jakie kiedykolwiek widziałem, próbowałem innych, ale mam pewne problemy. Twoje rozwiązanie działa idealnie na iOS 10.2.
Wangdu Lin,

8

Mam nadzieję, że macie już rozwiązanie, czytając je wszystkie. Ale moje rozwiązanie znalazłem w następujący sposób. Oczekuję, że masz już komórkę UITextField. Więc przygotowując, po prostu trzymaj indeks wiersza w znaczniku pola tekstowego.

cell.textField.tag = IndexPath.row;

Utwórz activeTextFieldinstancję o UITextFieldzasięgu globalnym, jak poniżej:

@interface EditViewController (){

    UITextField *activeTextField;

}

Więc teraz po prostu skopiuj wklej mój kod na końcu. I również nie zapomnij dodaćUITextFieldDelegate

#pragma mark - TextField Delegation

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

    activeTextField = textField;

    return YES;
}

- (void)textFieldDidEndEditing:(UITextField *)textField{

    activeTextField = nil;

}

Rejestruje klawiaturę notifications

#pragma mark - Keyboard Activity

- (void)registerForKeyboardNotifications

{

    [[NSNotificationCenter defaultCenter] addObserver:self

                                         selector:@selector(keyboardWasShown:)

                                             name:UIKeyboardDidShowNotification object:nil];



    [[NSNotificationCenter defaultCenter] addObserver:self

                                         selector:@selector(keyboardWillBeHidden:)

                                             name:UIKeyboardWillHideNotification object:nil];



}

Uchwyty Klawiatura Notifications:

Wywoływany, gdy wiadomość UIKeyboardDidShowNotificationjest wysyłana.

- (void)keyboardWasShown:(NSNotification*)aNotification

{

    NSDictionary* info = [aNotification userInfo];

    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);

    [self.tableView setContentInset:contentInsets];

    [self.tableView setScrollIndicatorInsets:contentInsets];

    NSIndexPath *currentRowIndex = [NSIndexPath indexPathForRow:activeTextField.tag inSection:0];

    [self.tableView scrollToRowAtIndexPath:currentRowIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];

}

Wywoływany, gdy wiadomość UIKeyboardWillHideNotificationjest wysyłana

- (void)keyboardWillBeHidden:(NSNotification*)aNotification

{

    UIEdgeInsets contentInsets = UIEdgeInsetsZero;

    [self.tableView setContentInset:contentInsets];

    [self.tableView setScrollIndicatorInsets:contentInsets];

}

Teraz pozostaje jedna rzecz: Wywołaj registerForKeyboardNotificationsmetodę do ViewDidLoadmetody w następujący sposób:

- (void)viewDidLoad {

    [super viewDidLoad];

    // Registering keyboard notification

    [self registerForKeyboardNotifications];

    // Your codes here...

}

Gotowe, masz nadzieję, że twoja textFieldswola nie będzie już ukryta przez klawiaturę.


6

Łącząc i wypełniając puste pola z kilku odpowiedzi (w szczególności Ortwin Gentz, użytkownik 98013) i innego postu, to zadziała po wyjęciu z pudełka dla SDK 4.3 na iPadzie w trybie pionowym lub poziomym:

@implementation UIView (FindFirstResponder)
- (UIResponder *)findFirstResponder
{
  if (self.isFirstResponder) {        
    return self;     
  }

  for (UIView *subView in self.subviews) {
    UIResponder *firstResponder = [subView findFirstResponder];
    if (firstResponder != nil) {
      return firstResponder;
    }
  }

  return nil;
}
@end

@implementation MyViewController

- (UIResponder *)currentFirstResponder {
  return [self.view findFirstResponder];
}

- (IBAction)editingEnded:sender {
  [sender resignFirstResponder];
}

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

- (void)textFieldDidBeginEditing:(UITextField *)textField {
  UITableViewCell *cell = (UITableViewCell*) [[textField superview] superview];
  [_tableView scrollToRowAtIndexPath:[_tableView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

- (void)keyboardWillShow:(NSNotification*)notification {
  if ([self currentFirstResponder] != nil) {
    NSDictionary* userInfo = [notification userInfo];

    // we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0
    NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"];
    if (!keyboardFrameValue) {
      keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"];
    }

    // Reduce the tableView height by the part of the keyboard that actually covers the tableView
    CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
    CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
    CGRect frame = _tableView.frame;
    if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) {
      windowRect = CGRectMake(windowRect.origin.y, windowRect.origin.x, windowRect.size.height, windowRect.size.width);
      viewRectAbsolute = CGRectMake(viewRectAbsolute.origin.y, viewRectAbsolute.origin.x, viewRectAbsolute.size.height, viewRectAbsolute.size.width);
    }
    frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
    _tableView.frame = frame;
    [UIView commitAnimations];

    UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview;
    NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell];

    // iOS 3 sends hide and show notifications right after each other
    // when switching between textFields, so cancel -scrollToOldPosition requests
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    _topmostRowBeforeKeyboardWasShown = [[_tableView indexPathsForVisibleRows] objectAtIndex:0];
    [_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
  }
}

- (void) scrollToOldPosition {
  [_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

- (void)keyboardWillHide:(NSNotification*)notification {
  if ([self currentFirstResponder] != nil) {

    NSDictionary* userInfo = [notification userInfo];

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
    _tableView.frame = self.view.bounds;
    [UIView commitAnimations];

    [self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1];
  }
}   

@end

Użyłem tego kodu w iOS 4.x w porządku, ale w iOS5 ulega awarii w scrollToOldPosition, ponieważ _topmostRowBeforeKeyboardWasShown jest już w tej chwili zwolniony. Nie jestem jeszcze pewien, jakie jest rozwiązanie. Prawdopodobnie pamiętasz indeks zamiast obiektu.
Thomas Tempelmann

5

Jeśli używasz uitableview do umieszczania pól tekstowych ( od Jeffa Lamarche ), możesz po prostu przewinąć widok tabeli przy użyciu metody delegowania.

(Uwaga: moje pola tekstowe są przechowywane w tablicy o tym samym indeksie, co tam wiersz w widoku tabeli)

- (void) textFieldDidBeginEditing:(UITextField *)textField
    {

        int index;
        for(UITextField *aField in textFields){

            if (textField == aField){
                index = [textFields indexOfObject:aField]-1;
            }
        }

         if(index >= 0) 
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];

        [super textFieldDidBeginEditing:textField];
    }

Nie aktualizujesz ramki tableView. Następnie paski przewijania i zachowanie przewijania są nieprawidłowe, gdy jest wyświetlana klawiatura. Zobacz moje rozwiązanie.
Ortwin Gentz

5

Powiadomienia z klawiatury działają, ale przykładowy kod Apple zakłada, że ​​widok przewijania jest widokiem głównym okna. Zazwyczaj tak nie jest. Musisz skompensować paski tabulatorów itp., Aby uzyskać właściwe przesunięcie.

To łatwiejsze niż się wydaje. Oto kod, którego używam w UITableViewController. Ma dwie zmienne instancji, hiddenRect i keyboardShown.

// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification {
    if (keyboardShown)
        return;

    NSDictionary* info = [aNotification userInfo];

    // Get the frame of the keyboard.
    NSValue *centerValue = [info objectForKey:UIKeyboardCenterEndUserInfoKey];
    NSValue *boundsValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
    CGPoint keyboardCenter = [centerValue CGPointValue];
    CGRect keyboardBounds = [boundsValue CGRectValue];
    CGPoint keyboardOrigin = CGPointMake(keyboardCenter.x - keyboardBounds.size.width / 2.0,
                                         keyboardCenter.y - keyboardBounds.size.height / 2.0);
    CGRect keyboardScreenFrame = { keyboardOrigin, keyboardBounds.size };


    // Resize the scroll view.
    UIScrollView *scrollView = (UIScrollView *) self.tableView;
    CGRect viewFrame = scrollView.frame;
    CGRect keyboardFrame = [scrollView.superview convertRect:keyboardScreenFrame fromView:nil];
    hiddenRect = CGRectIntersection(viewFrame, keyboardFrame);

    CGRect remainder, slice;
    CGRectDivide(viewFrame, &slice, &remainder, CGRectGetHeight(hiddenRect), CGRectMaxYEdge);
    scrollView.frame = remainder;

    // Scroll the active text field into view.
    CGRect textFieldRect = [/* selected cell */ frame];
    [scrollView scrollRectToVisible:textFieldRect animated:YES];

    keyboardShown = YES;
}


// Called when the UIKeyboardDidHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
    // Reset the height of the scroll view to its original value
    UIScrollView *scrollView = (UIScrollView *) self.tableView;
    CGRect viewFrame = [scrollView frame];
    scrollView.frame = CGRectUnion(viewFrame, hiddenRect);

    keyboardShown = NO;
}

UIKeyboardCenterEndUserInfoKeyi UIKeyboardBoundsUserInfoKeysą przestarzałe od iOS 3.2. Zobacz moje rozwiązanie poniżej, które działa we wszystkich aktualnych wersjach iOS ≥ 3.0.
Ortwin Gentz

5

Jeśli używasz Three20, użyj autoresizesForKeyboardwłaściwości. Wystarczy ustawić -initWithNibName:bundlemetodę kontrolera widoku

self.autoresizesForKeyboard = YES

Zajmuje się to:

  1. Odsłuchiwanie powiadomień z klawiatury i dostosowywanie ramki widoku tabeli
  2. Przewijanie do pierwszego odpowiadającego

Gotowe i gotowe.


czym jest Three20 tutaj? Czy możesz to określić?
Mubin Mall

5

Moje podejście:

Najpierw podklasę UITextField i dodaję właściwość indexPath. W komórce ... Metoda i przekazuję właściwość indexPath.

Następnie dodaję następujący kod:

UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:textField.indexPath];

CGPoint cellPoint = [cell convertPoint:textField.center toView:self.tableView];
[UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, cellPoint.y-50);}];

do textFieldShould / WillBegin ... itp.

Kiedy klawiatura znika, musisz ją odwrócić za pomocą:

[UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, 0);}];

4

Bardziej płynne rozwiązanie. Wchodzi w metody delegowania UITextField, więc nie wymaga bałaganu z powiadomieniami UIKeyboard.

Uwagi dotyczące wdrożenia:

kSettingsRowHeight - wysokość UITableViewCell.

offsetTarget i offsetThreshold są wyłączone z kSettingsRowHeight. Jeśli używasz innej wysokości wiersza, ustaw te wartości na właściwość y punktu. [alt: oblicz przesunięcie wiersza w inny sposób.]

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
CGFloat offsetTarget    = 113.0f; // 3rd row
CGFloat offsetThreshold = 248.0f; // 6th row (i.e. 2nd-to-last row)

CGPoint point = [self.tableView convertPoint:CGPointZero fromView:textField];

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

CGRect frame = self.tableView.frame;
if (point.y > offsetThreshold) {
    self.tableView.frame = CGRectMake(0.0f,
                      offsetTarget - point.y + kSettingsRowHeight,
                      frame.size.width,
                      frame.size.height);
} else if (point.y > offsetTarget) {
    self.tableView.frame = CGRectMake(0.0f,
                      offsetTarget - point.y,
                      frame.size.width,
                      frame.size.height);
} else {
    self.tableView.frame = CGRectMake(0.0f,
                      0.0f,
                      frame.size.width,
                      frame.size.height);
}

[UIView commitAnimations];

return YES;

}

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

[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.2];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];

CGRect frame = self.tableView.frame;
self.tableView.frame = CGRectMake(0.0f,
                  0.0f,
                  frame.size.width,
                  frame.size.height);

[UIView commitAnimations];

return YES;

}


4

Użyj UITextField's delegatemetody:

Szybki

func textFieldShouldBeginEditing(textField: UITextField) -> bool {
  let txtFieldPosition = textField.convertPoint(textField.bounds.origin, toView: yourTableViewHere)
  let indexPath = yourTablViewHere.indexPathForRowAtPoint(txtFieldPosition)
  if indexPath != nil {
     yourTablViewHere.scrollToRowAtIndexPath(indexPath!, atScrollPosition: .Top, animated: true)
  }
  return true
}

Cel C

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
  CGPoint txtFieldPosition = [textField convertPoint:CGPointZero toView: yourTablViewHere];
  NSLog(@"Begin txtFieldPosition : %@",NSStringFromCGPoint(txtFieldPosition));
  NSIndexPath *indexPath = [yourTablViewHere indexPathForRowAtPoint:txtFieldPosition];

  if (indexPath != nil) {
     [yourTablViewHere scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
  }
  return YES;
}

Cześć, mam problem z uruchomieniem tego w Swift. Moje UITextFields połączone z UITableViewCell. Jeśli zaimplementuję ten kod w moim UIViewController, nie mam dostępu do UITextFields. Jakieś pomysły?
Vetuka,

4

Kompletne rozwiązanie Swift 4.2

Stworzyłem GIST z zestawem protokołów, który upraszcza pracę dzięki dodawaniu dodatkowego miejsca, gdy klawiatura jest pokazywana, ukryta lub zmieniana.

Funkcje :

  • Prawidłowo działa ze zmianami ramki klawiatury (np. Zmiany wysokości klawiatury, takie jak emojii → normalna klawiatura).
  • Obsługa TabBar i ToolBar dla przykładu UITableView (w innych przykładach otrzymujesz niepoprawne wstawki).
  • Czas trwania dynamicznej animacji (nie zakodowany na stałe).
  • Podejście zorientowane na protokół, które można łatwo zmodyfikować dla swoich celów.

Stosowanie

Przykład użycia podstawowego w kontrolerze widoku zawierającym widok przewijania (widok tabeli jest oczywiście obsługiwany).

class SomeViewController: UIViewController {
  @IBOutlet weak var scrollView: UIScrollView!

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    addKeyboardFrameChangesObserver()
  }

  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    removeKeyboardFrameChangesObserver()
  }
}

extension SomeViewController: ModifableInsetsOnKeyboardFrameChanges {
  var scrollViewToModify: UIScrollView { return scrollView }
}

Rdzeń: obserwator zmienia ramkę

Protokół KeyboardChangeFrameObserveruruchamia zdarzenie po każdej zmianie ramki klawiatury (w tym pokazywaniu, ukrywaniu, zmianie ramki).

  1. Zadzwoń addKeyboardFrameChangesObserver()na viewWillAppear()lub podobną metodę.
  2. Zadzwoń removeKeyboardFrameChangesObserver()na viewWillDisappear()lub podobną metodę.

Realizacja: przewijany widok

ModifableInsetsOnKeyboardFrameChangesprotokół dodaje UIScrollViewobsługę podstawowego protokołu. Zmienia on wypustki widoku przewijania po zmianie ramki klawiatury.

Twoja klasa musi ustawić widok przewijania, wstawki będą zwiększane / zmniejszane przy zmianie ramki klawiatury.

var scrollViewToModify: UIScrollView { get }

3

Ponieważ masz pola tekstowe w tabeli, najlepszym sposobem jest zmiana rozmiaru tabeli - musisz ustawić tableView.frame na mniejszą wysokość o rozmiar klawiatury (myślę, że około 165 pikseli), a następnie rozwinąć go ponownie, gdy klawiatura jest zwolniona.

W tym momencie możesz opcjonalnie również wyłączyć interakcję użytkownika dla tableView, jeśli nie chcesz, aby użytkownik przewijał.


Po drugie, rejestruję się w UIKeyboardWillShowNotification, aby dynamicznie znajdować rozmiar klawiatury.
benzado

Liczba zwrócona przez obiekt powiadomienia nie działa jednak. A przynajmniej tak nie było w 2.2, zwrócona liczba była niepoprawna i musiałem zakodować wartość 165, aby poprawnie wyregulować wysokość (było wyłączone o pięć do dziesięciu pikseli)
Kendall Helmstetter Gelner

2

Działa to doskonale, a także na iPadzie.

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

    if(textField == textfield1){
            [accountName1TextField becomeFirstResponder];
        }else if(textField == textfield2){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield3 becomeFirstResponder];

        }else if(textField == textfield3){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield4 becomeFirstResponder];

        }else if(textField == textfield4){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield5 becomeFirstResponder];

        }else if(textField == textfield5){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:3 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield6 becomeFirstResponder];

        }else if(textField == textfield6){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:4 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield7 becomeFirstResponder];

        }else if(textField == textfield7){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:5 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield8 becomeFirstResponder];

        }else if(textField == textfield8){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:6 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textfield9 becomeFirstResponder];

        }else if(textField == textfield9){
            [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:7 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES];
            [textField resignFirstResponder];
        }

Dlaczego używasz specjalnych przypadków dla każdego pola tekstowego? Zidentyfikuj każde pole tekstowe z NSIndexPath komórki i zmień tę nieprzyjemną instrukcję if na 2 linie kodu. Naprawdę chcesz wywołać funkcję cellForRowAtIndexPath, a następnie pobrać pole tekstowe z komórki.
Alex Zavatone

Biorąc pod uwagę, jak niesamowicie płatna jest ta sytuacja na iOS, myślę, że można napisać „całkowicie rozwinięty, śmiesznie dosłowny” kod dla tej sytuacji.
Fattie

Biorąc pod uwagę tę odpowiedź udzielono ponad 6 lat temu.
WrightsCS,

2

Próbowałem prawie tego samego podejścia i wymyśliłem prostszy i mniejszy kod do tego samego. Utworzyłem IBOutlet iTextView i skojarzyłem go z UITextView w IB.

 -(void)keyboardWillShow:(NSNotification *)notification
    {
        NSLog(@"Keyboard");
        CGRect keyFrame = [[[notification userInfo]objectForKey:UIKeyboardFrameEndUserInfoKey]CGRectValue];

        [UIView beginAnimations:@"resize view" context:nil];
        [UIView setAnimationCurve:1];
        [UIView setAnimationDuration:1.0];
        CGRect frame = iTableView.frame;
        frame.size.height = frame.size.height -  keyFrame.size.height;
        iTableView.frame = frame;
        [iTableView scrollRectToVisible:frame animated:YES];
        [UIView commitAnimations];

    }

2

Więc po godzinach wyczerpującej pracy, próbując wykorzystać te obecne rozwiązania (i całkowicie zawiodłem) w końcu sprawiłem, że wszystko działało dobrze i zaktualizowałem je, aby używały nowych bloków animacji. Moja odpowiedź jest całkowicie oparta na powyższej odpowiedzi Ortwina .

Więc z jakiegokolwiek powodu powyższy kod po prostu nie działał dla mnie. Moja konfiguracja wydawała się dość podobna do innych, ale może dlatego, że byłem na iPadzie lub 4.3 ... nie mam pojęcia. Robił trochę zwariowanej matematyki i strzelał do mojego widoku z ekranu poza ekranem.

Zobacz końcowy wynik mojego rozwiązania: http://screencast.com/t/hjBCuRrPC (zignoruj ​​zdjęcie. :-P)

Poszedłem więc do sedna tego, co robił Ortwin, ale zmieniłem sposób, w jaki robiło to matematykę, aby dodać pochodzenie. Y i rozmiar. Wysokość mojego widoku tabeli z wysokością klawiatury. Kiedy odejmuję wysokość okna od tego wyniku, mówi mi to, ile przecięć mam na sobie. Jeśli jego wartość jest większa niż 0 (aka występuje pewne nakładanie się), wykonuję animację wysokości klatki.

Ponadto wystąpiły pewne problemy z przerysowaniem, które zostały rozwiązane przez 1) Oczekiwanie na przewinięcie do komórki, aż animacja została zakończona i 2) przy użyciu opcji UIViewAnimationOptionBeginFromCurrentState podczas ukrywania klawiatury.

Kilka rzeczy do zapamiętania.

  • _topmostRowBeforeKeyboardWasShown & _originalFrame są zmiennymi instancji zadeklarowanymi w nagłówku.
  • self.guestEntryTableView to mój tableView (jestem w pliku zewnętrznym)
  • IASKCGRectSwap to metoda Ortwina służąca do odwracania współrzędnych ramki
  • Aktualizuję tylko wysokość tabeli, jeśli pokaże się co najmniej 50 pikseli
  • Ponieważ nie jestem w UIViewController, nie mam self.view, więc po prostu przywracam tableView do oryginalnej ramki

Znów nie zbliżyłbym się do tej odpowiedzi, gdybym nie dostarczył jej sedna. Oto kod:

- (IBAction)textFieldDidBeginEditing:(UITextField *)textField
{
    self.activeTextField = textField;

    if ([self.guestEntryTableView indexPathsForVisibleRows].count) {
        _topmostRowBeforeKeyboardWasShown = (NSIndexPath*)[[self.guestEntryTableView indexPathsForVisibleRows] objectAtIndex:0];
    } else {
        // this should never happen
        _topmostRowBeforeKeyboardWasShown = [NSIndexPath indexPathForRow:0 inSection:0];
        [textField resignFirstResponder];
    }
}

- (IBAction)textFieldDidEndEditing:(UITextField *)textField
{
    self.activeTextField = nil;
}

- (void)keyboardWillShow:(NSNotification*)notification {
    NSDictionary* userInfo = [notification userInfo];

    NSValue* keyboardFrameValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];

    // Reduce the tableView height by the part of the keyboard that actually covers the tableView
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
    CGRect viewRectAbsolute = [self.guestEntryTableView convertRect:self.guestEntryTableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
    CGRect keyboardFrame = [keyboardFrameValue CGRectValue];
    if (UIInterfaceOrientationLandscapeLeft == orientation ||UIInterfaceOrientationLandscapeRight == orientation ) {
        windowRect = IASKCGRectSwap(windowRect);
        viewRectAbsolute = IASKCGRectSwap(viewRectAbsolute);
        keyboardFrame = IASKCGRectSwap(keyboardFrame);
    }

    // fix the coordinates of our rect to have a top left origin 0,0
    viewRectAbsolute = FixOriginRotation(viewRectAbsolute, orientation, windowRect.size.width, windowRect.size.height);

    CGRect frame = self.guestEntryTableView.frame;
    _originalFrame = self.guestEntryTableView.frame;

    int remainder = (viewRectAbsolute.origin.y + viewRectAbsolute.size.height + keyboardFrame.size.height) - windowRect.size.height;

    if (remainder > 0 && !(remainder > frame.size.height + 50)) {
        frame.size.height = frame.size.height - remainder;
        float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
        [UIView animateWithDuration: duration
                        animations:^{
                            self.guestEntryTableView.frame = frame;
                        }
                        completion:^(BOOL finished){
                            UITableViewCell *textFieldCell = (UITableViewCell*) [[self.activeTextField superview] superview];
                            NSIndexPath *textFieldIndexPath = [self.guestEntryTableView indexPathForCell:textFieldCell];
                            [self.guestEntryTableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
                        }];
    }

}

- (void)keyboardWillHide:(NSNotification*)notification {
    NSDictionary* userInfo = [notification userInfo];
    float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    [UIView animateWithDuration: duration
                          delay: 0.0
                        options: (UIViewAnimationOptionBeginFromCurrentState)
                     animations:^{
                         self.guestEntryTableView.frame = _originalFrame;
                     }
                     completion:^(BOOL finished){
                         [self.guestEntryTableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
                     }];

}   

#pragma mark CGRect Utility function
CGRect IASKCGRectSwap(CGRect rect) {
    CGRect newRect;
    newRect.origin.x = rect.origin.y;
    newRect.origin.y = rect.origin.x;
    newRect.size.width = rect.size.height;
    newRect.size.height = rect.size.width;
    return newRect;
}

CGRect FixOriginRotation(CGRect rect, UIInterfaceOrientation orientation, int parentWidth, int parentHeight) {
    CGRect newRect;
    switch(orientation)
    {
        case UIInterfaceOrientationLandscapeLeft:
            newRect = CGRectMake(parentWidth - (rect.size.width + rect.origin.x), rect.origin.y, rect.size.width, rect.size.height);
            break;
        case UIInterfaceOrientationLandscapeRight:
            newRect = CGRectMake(rect.origin.x, parentHeight - (rect.size.height + rect.origin.y), rect.size.width, rect.size.height);
            break;
        case UIInterfaceOrientationPortrait:
            newRect = rect;
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            newRect = CGRectMake(parentWidth - (rect.size.width + rect.origin.x), parentHeight - (rect.size.height + rect.origin.y), rect.size.width, rect.size.height);
            break;
    }
    return newRect;
}

Dodano moją funkcję FixOriginRotation, która naprawia układ współrzędnych widoku przed aktualizacją jego ramki itp. Myślę, że jest to część tego, dlaczego na początku miałem problemy. Nie wiedziałem, że system współrzędnych okna iOS został obrócony wraz z urządzeniem!
Bob Spryn

2

To rozwiązanie działa dla mnie, PROSZĘ zwróć uwagę na linię

[tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height+160) animated:YES];

Możesz zmienić wartość 160, aby dopasować ją do siebie

- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGRect bkgndRect = activeField.superview.frame;
                        bkgndRect.size.height += kbSize.height;
     [activeField.superview setFrame:bkgndRect];
     [tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height+160) animated:YES];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
   activeField = textField;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
 {
     activeField = nil;
 }
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    tableView.contentInset = contentInsets;
    tableView.scrollIndicatorInsets = contentInsets;
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGRect bkgndRect = activeField.superview.frame;
    //bkgndRect.size.height += kbSize.height;
    [activeField.superview setFrame:bkgndRect];
    [tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height) animated:YES];
}

2

Bardzo interesujący wątek do dyskusji, również napotkałem ten sam problem, może być gorszy, ponieważ

  1. Używałem niestandardowej komórki, w której znajdowało się pole tekstowe.
  2. Musiałem użyć UIViewController, aby spełnić moje wymagania, więc nie mogę skorzystać z UITableViewController.
  3. Miałem kryteria filtrowania / sortowania w komórce tabeli, tj. Komórki ur ciągle się zmieniają i śledzą ścieżkę indeksu i wszystko nie pomoże.

Przeczytaj więc tutaj wątki i wdrożyłem moją wersję, która pomogła mi w zwiększaniu zawartości w iPadzie w trybie poziomym . Oto kod (to nie jest głupi dowód i wszystko, ale naprawiło mój problem) Najpierw musisz mieć delegata w swojej niestandardowej klasie komórek, która po rozpoczęciu edycji wysyła pole tekstowe do twojego kontrolera widoku i ustawia pole activefield = pole tekstowe

// WYKONANE TYLKO DO OBSŁUGI TRYBU KRAJOBRAZU

- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbValue = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGRect aRect = myTable.frame;

    CGSize kbSize = CGSizeMake(kbValue.height, kbValue.width);

    aRect.size.height -= kbSize.height+50;
// This will the exact rect in which your textfield is present
        CGRect rect =  [myTable convertRect:activeField.bounds fromView:activeField];
// Scroll up only if required
    if (!CGRectContainsPoint(aRect, rect.origin) ) {


            [myTable setContentOffset:CGPointMake(0.0, rect.origin.y) animated:YES];

    }


}

// Wywoływany, gdy wysyłane jest UIKeyboardWillHideNotification

- (void)keyboardWillHide:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    myTable.contentInset = contentInsets;
    myTable.scrollIndicatorInsets = contentInsets;
    NSDictionary* info = [aNotification userInfo];
    CGSize kbValue = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGSize kbSize = CGSizeMake(kbValue.height, kbValue.width);
    CGRect bkgndRect = activeField.superview.frame;
    bkgndRect.size.height += kbSize.height;
    [activeField.superview setFrame:bkgndRect];
    [myTable setContentOffset:CGPointMake(0.0, 10.0) animated:YES];
}

-anoop4real


2

Właśnie rozwiązałem taki problem po tym, jak poleciłem masę rozwiązań znalezionych przez Google i Stack Overflow.

Najpierw upewnij się, że skonfigurowałeś IBOutlet swojego UIScrollView, a następnie przyjrzyj się bliżej Apple Doc: Zarządzanie klawiaturą . Na koniec, jeśli możesz przewijać tło, ale klawiatura nadal pokrywa pola tekstowe, spójrz na ten fragment kodu:

// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;

if (aRect.size.height < activeField.frame.origin.y+activeField.frame.size.height) {

    CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y+activeField.frame.size.height-aRect.size.height);

    [scrollView setContentOffset:scrollPoint animated:YES];

Główna różnica między tym kawałkiem a Apple polega na warunku if. Uważam, że obliczenia odległości przewijania przez Apple i warunek, czy pole tekstowe pokryte klawiaturą nie są dokładne, więc zmodyfikowałem jak wyżej.

Daj znać czy działa


2

Przykład w Swift, używając dokładnego punktu pola tekstowego z Get indexPath z UITextField w UITableViewCell za pomocą Swift :

func textFieldDidBeginEditing(textField: UITextField) {
    let pointInTable = textField.convertPoint(textField.bounds.origin, toView: self.accountsTableView)
    let textFieldIndexPath = self.accountsTableView.indexPathForRowAtPoint(pointInTable)
    accountsTableView.scrollToRowAtIndexPath(textFieldIndexPath!, atScrollPosition: .Top, animated: true)
}

1

Kolejna łatwa metoda (działa tylko z jedną sekcją)

//cellForRowAtIndexPath
UItextField *tf;
[cell addSubview:tf];
tf.tag = indexPath.row;
tf.delegate = self;

//textFieldDidBeginEditing:(UITextField *)text
[[self.tableView scrollToRowsAtIndexPath:[NSIndexPath indexPathForRow:text.tag in section:SECTIONINTEGER] animated:YES];

1

Jeśli Twoim UITableView zarządza podklasa UITableViewController, a nie UITableView, a delegatem pola tekstowego jest UITableViewController, powinien on automatycznie zarządzać całym przewijaniem - wszystkie inne komentarze są bardzo trudne do wdrożenia w praktyce.

Dobry przykład można znaleźć w przykładowym projekcie kodu Apple: TaggedLocations.

Możesz zobaczyć, że przewija się on automatycznie, ale wydaje się, że nie ma kodu, który to robi. Ten projekt ma również niestandardowe komórki widoku tabeli, więc jeśli zbudujesz aplikację z nim jako przewodnikiem, powinieneś uzyskać pożądany wynik.


1

Oto, w jaki sposób stworzyłem tę pracę, która jest mieszanką odpowiedzi Sama Ho i Marcela W oraz niektórych własnych poprawek błędów w moim gównianym kodzie. Korzystałem z UITableViewController. Tabela wyświetla się teraz poprawnie, gdy pokazana jest klawiatura.

1) W viewDidLoaddodałem:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2) Zapomniałem nazwać superekwiwalentów w viewWillAppeari awakeFromNib. Dodałem je z powrotem.


1

UITableViewControllerrzeczywiście przewija automatycznie. Różnica w porównaniu do używania a UIViewControllerpolega na tym, że musisz programowo tworzyć przyciski Navbar-Buttonitems przy użyciu NavigationController, podczas korzystania z TableViewController.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.