2012-12-12 1 views
10

Często wyszukiwałam kod, który zmienia rozmiar widoku tabeli, aby dopasować go do wyświetlania i ukrywania klawiatury, ale prawie każdy pojedynczy wpis, na który natknąłem się zakłada, że ​​widok tabeli zajmuje cały widok jego widoku kontroler. Mam aplikację na iPada, w której widok tabeli zajmuje tylko część ekranu. Jaki jest poprawny sposób zmiany rozmiaru widoku tabeli w tym przypadku? (Cały kod w postach pisałem wyżej nie)Generyczny algorytm zmiany rozmiaru klawiatury UITableView

+4

Nie wymieniono żadnego kodu, który się nie powiedzie. – Mundi

+0

Dlaczego nie dopasować znalezionego kodu do różnicy między dolną częścią widoku tabeli a pełną ramką widoku? – Anton

Odpowiedz

17

Poniższy kod robi to, co chcesz i współpracuje z dowolnym urządzeniem i dowolnym układem. Kod jest dostępny dzięki uprzejmości Sensible TableView framework (z pozwoleniem na kopiowanie i używanie).

- (void)keyboardWillShow:(NSNotification *)aNotification 
{ 
if(keyboardShown) 
    return; 

keyboardShown = YES; 

// Get the keyboard size 
UIScrollView *tableView; 
if([self.tableView.superview isKindOfClass:[UIScrollView class]]) 
    tableView = (UIScrollView *)self.tableView.superview; 
else 
    tableView = self.tableView; 
NSDictionary *userInfo = [aNotification userInfo]; 
NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]; 
CGRect keyboardRect = [tableView.superview convertRect:[aValue CGRectValue] fromView:nil]; 

// Get the keyboard's animation details 
NSTimeInterval animationDuration; 
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration]; 
UIViewAnimationCurve animationCurve; 
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve]; 

// Determine how much overlap exists between tableView and the keyboard 
CGRect tableFrame = tableView.frame; 
CGFloat tableLowerYCoord = tableFrame.origin.y + tableFrame.size.height; 
keyboardOverlap = tableLowerYCoord - keyboardRect.origin.y; 
if(self.inputAccessoryView && keyboardOverlap>0) 
{ 
    CGFloat accessoryHeight = self.inputAccessoryView.frame.size.height; 
    keyboardOverlap -= accessoryHeight; 

    tableView.contentInset = UIEdgeInsetsMake(0, 0, accessoryHeight, 0); 
    tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, accessoryHeight, 0); 
} 

if(keyboardOverlap < 0) 
    keyboardOverlap = 0; 

if(keyboardOverlap != 0) 
{ 
    tableFrame.size.height -= keyboardOverlap; 

    NSTimeInterval delay = 0; 
    if(keyboardRect.size.height) 
    { 
     delay = (1 - keyboardOverlap/keyboardRect.size.height)*animationDuration; 
     animationDuration = animationDuration * keyboardOverlap/keyboardRect.size.height; 
    } 

    [UIView animateWithDuration:animationDuration delay:delay 
         options:UIViewAnimationOptionBeginFromCurrentState 
        animations:^{ tableView.frame = tableFrame; } 
        completion:^(BOOL finished){ [self tableAnimationEnded:nil finished:nil contextInfo:nil]; }]; 
} 
} 

- (void)keyboardWillHide:(NSNotification *)aNotification 
{ 
if(!keyboardShown) 
    return; 

keyboardShown = NO; 

UIScrollView *tableView; 
if([self.tableView.superview isKindOfClass:[UIScrollView class]]) 
    tableView = (UIScrollView *)self.tableView.superview; 
else 
    tableView = self.tableView; 
if(self.inputAccessoryView) 
{ 
    tableView.contentInset = UIEdgeInsetsZero; 
    tableView.scrollIndicatorInsets = UIEdgeInsetsZero; 
} 

if(keyboardOverlap == 0) 
    return; 

// Get the size & animation details of the keyboard 
NSDictionary *userInfo = [aNotification userInfo]; 
NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]; 
CGRect keyboardRect = [tableView.superview convertRect:[aValue CGRectValue] fromView:nil]; 

NSTimeInterval animationDuration; 
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration]; 
UIViewAnimationCurve animationCurve; 
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve]; 

CGRect tableFrame = tableView.frame; 
tableFrame.size.height += keyboardOverlap; 

if(keyboardRect.size.height) 
    animationDuration = animationDuration * keyboardOverlap/keyboardRect.size.height; 

[UIView animateWithDuration:animationDuration delay:0 
        options:UIViewAnimationOptionBeginFromCurrentState 
       animations:^{ tableView.frame = tableFrame; } 
       completion:nil]; 
} 

- (void) tableAnimationEnded:(NSString*)animationID finished:(NSNumber *)finished contextInfo:(void *)context 
{ 
// Scroll to the active cell 
if(self.activeCellIndexPath) 
{ 
    [self.tableView scrollToRowAtIndexPath:self.activeCellIndexPath atScrollPosition:UITableViewScrollPositionNone animated:YES]; 
    [self.tableView selectRowAtIndexPath:self.activeCellIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; 
} 
} 

Uwagi:

a. Powyższe dwie metody zostały dodane do centrum powiadomień przy użyciu następującego kodu:

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

b. Użyte powyżej znaki iv zostały zadeklarowane w następujący sposób:

BOOL keyboardShown; 
CGFloat keyboardOverlap; 

c. "self.activeCellIndexPath" jest zawsze ustawione na indexPath komórki posiadającej aktualnie aktywny UITextField/UITextView.

Ciesz się! :)

+2

To jest niesamowite! działa jak urok, dziękuję bardzo! – Matt

+0

Dodałbym tylko jedno uzupełnienie. Jeśli twoja wysokość UITableViewCell może być większa niż rozmiar pozostały do ​​prezentacji UITableView, lepiej zmodyfikować metodę tableAnimationEnded, aby przewinąć ją do "TOP" zamiast "NONE". Również jeśli wyświetlasz klawiaturę po wybraniu komórki i już wybrałeś wybór UITableView, nie musisz ponownie wybierać "activeCellIndexPath". – Lubakis

2

Proste rozwiązanie - zarejestruj aby otrzymywać powiadomienia klawiatura startowych lub viewDidLoad z:

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

następnie po otrzymaniu powiadomienia, można użyć danego rect klawiatury, aby ustawić ramkę swojego tableview:

- (void)keyboardWillShow:(NSNotification *)notification 
{ 
    // Get the size of the keyboard. 
    CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; 

    CGRect newTableFrame = _myTableView.frame; 
    //Here make adjustments to the tableview frame based on the value in keyboard size 
    ... 

    _myTableView.frame = newTableFrame; 
} 

- (void)keyboardWillHide:(NSNotification *)notification 
{ 
    //Here change the table frame back to what it originally was. 
} 
+2

Proponuję zarejestrować się w viewWillAppear: i wyrejestrować w viewWillDisappear: (ze względu na zachowanie obserwatora centrum informacyjnego i ponieważ viewDidUnload: (dla wyrejestrowania) jest przestarzałe od iOS 6) – JakubKnejzlik

6

Znalazłem najłatwiejsze rozwiązanie, aby mieć ten jeden (nie jestem fanem używając subviews do tego rodzaju rzeczy):

rejestr na klawiaturze powiadomień zmiana ramki (idealy zarejestrować w viewWillAppear: i wyrejestrować w viewWillDisappear :):

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil]; 

a następnie w metodzie:

- (void)keyboardDidChangeFrame:(NSNotification*)aNotification 
{ 
    NSDictionary* info = [aNotification userInfo]; 
    UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; 
    CGRect kbIntersectFrame = [window convertRect:CGRectIntersection(window.frame, kbFrame) toView:self.scrollView]; 
    kbIntersectFrame = CGRectIntersection(self.bounds, kbIntersectFrame); 

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbIntersectFrame.size.height, 0.0); 
    self.scrollView.contentInset = contentInsets; 
    self.scrollView.scrollIndicatorInsets = contentInsets; 
} 

lub jeśli chcesz pozbyć się "skakać" po zmianie contentInset:

- (void)keyboardDidChangeFrame:(NSNotification*)aNotification 
{ 
    NSDictionary* info = [aNotification userInfo]; 
    UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; 
    CGRect kbIntersectFrame = [window convertRect:CGRectIntersection(window.frame, kbFrame) toView:self.scrollView]; 
    kbIntersectFrame = CGRectIntersection(self.scrollView.bounds, kbIntersectFrame); 

    // get point before contentInset change 
    CGPoint pointBefore = self.scrollView.contentOffset; 
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbIntersectFrame.size.height, 0.0); 
    self.scrollView.contentInset = contentInsets; 
    self.scrollView.scrollIndicatorInsets = contentInsets; 
    // get point after contentInset change 
    CGPoint pointAfter = self.scrollView.contentOffset; 
    // avoid jump by settings contentOffset 
    self.scrollView.contentOffset = pointBefore; 
    // and now animate smoothly 
    [self.scrollView setContentOffset:pointAfter animated:YES]; 
} 
+0

Działa to naprawdę dobrze i jest prostsze niż powyższy Tarek. Nie kopie przy użyciu UIWindow, ale to powinno być łatwo przełączone na widok, nie? – Brian

+1

Tak, teoretycznie. Wystarczy wziąć pod uwagę, że współrzędne klawiatury są w "wymiarach okna", więc jeśli widok nie ma takiej samej wielkości/pozycji okna, nie może działać poprawnie. – JakubKnejzlik

3

Prostym rozwiązaniem jest dodanie moje rozszerzenie UIViewController+Keyboard.swift do projektu, z jednej linii setupKeyboardNotifcationListenerForScrollView(tableView) będzie automatycznie zmieniać rozmiar automatycznie. Nie ma potrzeby podklasy niczego, tylko rozszerzenie!Jego open source w SingleLineKeyboardResize

0

Oto metoda klawiatura:

func keyboardControl(notification: NSNotification, isShowing: Bool) { 
    var userInfo = notification.userInfo! 
    let keyboardRect = userInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue 
    let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey]!.unsignedIntValue 

    let convertedFrame = self.view.convertRect(keyboardRect, fromView: nil) 
    let heightOffset = self.view.bounds.size.height - convertedFrame.origin.y 
    let options = UIViewAnimationOptions(rawValue: UInt(curve) << 16) 
    let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey]!.doubleValue 

    //your UITableView bottom constrant 
    self.tableViewMarginBottomConstraint.constant = heightOffset 

    var contentInsets = UIEdgeInsetsZero 
    if isShowing { 
     contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardRect.size.height), 0.0) 
    } 

    UIView.animateWithDuration(
     duration, 
     delay: 0, 
     options: options.union(.LayoutSubviews).union(.BeginFromCurrentState), 
     animations: { 
      self.listTableView.contentInset = contentInsets 
      self.listTableView.scrollIndicatorInsets = contentInsets 
      self.listTableView.scrollBottomToLastRow() 
      self.view.layoutIfNeeded() 
     }, 
     completion: { bool in 
    }) 
} 

Oto przedłużenie UITableView:

extension UITableView { 
    func totalRows() -> Int { 
     var i = 0 
     var rowCount = 0 
     while i < self.numberOfSections { 
      rowCount += self.numberOfRowsInSection(i) 
      i++ 
     } 
     return rowCount 
    } 

    var lastIndexPath: NSIndexPath { 
     get { return NSIndexPath(forRow: self.totalRows()-1, inSection: 0) } 
    } 

    func scrollBottomToLastRow() { 
     self.scrollToRowAtIndexPath(self.lastIndexPath, atScrollPosition: .Bottom, animated: false) 
    } 
} 
0

Można osiągnąć to, czego szukasz za pomocą IQKeyboardManager, to bezkodowa biblioteka, musisz tylko dodać ją do swojego Podfile: pod 'IQKeyboardManager' i to wszystko, przekaże efekt przewijania, gdy klawiatura zostanie wyświetlona, ​​nawet jeśli UITextField/UITextView nie jest częścią scrollView/tableView.