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
Odpowiedz
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ę! :)
To jest niesamowite! działa jak urok, dziękuję bardzo! – Matt
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
Check out this project, to przeciągnij i upuść, więc po prostu zadeklarować tablview jako typ TPKeyboardAvoidingTableView
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.
}
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
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];
}
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
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
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
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)
}
}
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.
Nie wymieniono żadnego kodu, który się nie powiedzie. – Mundi
Dlaczego nie dopasować znalezionego kodu do różnicy między dolną częścią widoku tabeli a pełną ramką widoku? – Anton