多くの試行錯誤の後、私はあきらめて質問をしています。同様の問題を抱えている人をたくさん見ましたが、すべての答えを正しく機能させることはできません。
カスタムセルで構成されるUITableView
があります。セルは5つのテキストフィールドで構成されています(グリッドのようなもの)。
UITableView
の下部にあるセルをスクロールして編集しようとすると、キーボードの上にセルを適切に配置することができません。
私は、ビューのサイズの変更などについて多くの回答を見てきましたが、今のところうまく機能しているものはありません。
誰かが具体的なコード例でこれを行う「正しい」方法を明確にできますか?
UIViewControllerの代わりにUITableViewControllerを使用すると、自動的に使用されます。
スクロールを行う関数は、はるかに簡単です。
- (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];
}
それでおしまい。計算は一切ありません。
私は非常によく似た何かをやっています。それは一般的なもので、コードに特定の何かを計算する必要はありません。コードのコメントを確認してください:
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
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
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()
}
}
}
私は同じ問題を抱えていましたが、1つのビューにしか表示されないことに気付きました。そこで、コントローラーの違いを探し始めました。
スクロール動作は、スーパーインスタンスの- (void)viewWillAppear:(BOOL)animated
で設定されていることがわかりました。
そのため、必ず次のように実装してください。
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// your code
}
また、UIViewController
またはUITableViewController
を使用するかどうかは関係ありません。 UITableView
をself.viewのサブビューとしてUIViewController
を入れることでチェックしました。同じ振る舞いでした。呼び出し[super viewWillAppear:animated];
がない場合、ビューはスクロールできませんでした。
Swift 3の最も単純なソリューションは、 BartłomiejSemańczykソリューションに基づいています :
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)
})
}
ここで投稿全体を読んでいなかったので、私はこれを見逃したかもしれませんが、私が思いついたのは一見単純です。私はこれを絞り器に入れていないので、すべての状況でテストしますが、うまくいくはずです。
キーボードの高さによってtableviewのcontentInsetを調整し、セルを一番下までスクロールします。
- (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];
}
そしてもちろん
- (void)keyboardWasHidden:(NSNotification *)aNotification
{
[UIView animateWithDuration:.3 animations:^(void)
{
self.myTableView.contentInset = UIEdgeInsetsZero;
self.myTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}];
}
これは単純すぎますか?私は何かが欠けていますか?これまでのところ私にとってはうまく機能していますが、私が言ったように、私はそれを絞り器に入れていません...
Appleのアプリの動作に一致するソリューションを思いついたと思います。
まず、viewWillAppear:でキーボード通知をサブスクライブします。これにより、キーボードがいつ表示および非表示になるかがわかり、システムはキーボードのサイズを通知しますが、viewWillDisappear:で登録を忘れないでください。
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
以下のようなメソッドを実装して、キーボードが表示されたら可視領域に合わせてtableViewのサイズを調整します。ここでは、キーボードの状態を個別に追跡しているため、tableViewを自分でフルハイトに戻すタイミングを選択できます。これは、フィールドが変更されるたびにこれらの通知を受け取るためです。 keyboardWillHide:の実装を忘れずに、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];
}
}
これがスクロールビットです。最初にいくつかのサイズを確認してから、表示領域のどこにいるかを確認し、スクロールする四角形をテキストフィールドの中央の上または下の半分のビューに設定しますビューのどこにあるか。この場合、UITextFieldsの配列とそれらを追跡する列挙型があるため、rowHeightに行番号を掛けると、この外部ビュー内のフレームの実際のオフセットが得られます。
- (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];
}
これはかなりうまくいくようです。
UITableViewController
を使用できる場合、無料で機能を利用できます。ただし、UITableView
だけでなく複数のビューが必要な場合は特に、これはオプションではありません。
ここで紹介するソリューションには、iOS≥4では動作しないもの、iPadやランドスケープモードで動作しないもの、Bluetoothキーボードで動作しないもの(スクロールが必要ない場所)、動作しないものがあります。複数のテキストフィールドを切り替えるときに機能します。したがって、ソリューションを選択する場合は、これらのケースを必ずテストしてください。これが私たちの解決策です つかいます usedin 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];
}
}
InAppSettingsKitの クラスの完全なコード です。テストするには、上記のシナリオをテストできる「完全リスト」子ペインを使用します。
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
}
皆さんがこれらすべてを読んで解決策を得たことを願っています。しかし、次のように解決策を見つけました。 UITextField
のセルが既にあることを期待しています。そのため、行フィールドをテキストフィールドのタグに保持するだけで準備できます。
cell.textField.tag = IndexPath.row;
以下のグローバルスコープでactiveTextField
、UITextField
のインスタンスを作成します。
@interface EditViewController (){
UITextField *activeTextField;
}
だから、今あなたはコピーして最後に私のコードを貼り付けます。また、UITextFieldDelegate
を追加することも忘れないでください
#pragma mark - TextField Delegation
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
activeTextField = textField;
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField{
activeTextField = nil;
}
キーボードを登録します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];
}
キーボードNotifications
を処理します。
UIKeyboardDidShowNotification
が送信されたときに呼び出されます。
- (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];
}
UIKeyboardWillHideNotification
が送信されたときに呼び出されます
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
[self.tableView setContentInset:contentInsets];
[self.tableView setScrollIndicatorInsets:contentInsets];
}
次のように、registerForKeyboardNotifications
メソッドをViewDidLoad
メソッドに呼び出します。
- (void)viewDidLoad {
[super viewDidLoad];
// Registering keyboard notification
[self registerForKeyboardNotifications];
// Your codes here...
}
これで、textFields
がキーボードで隠されなくなります。
いくつかの回答(特にOrtwin Gentz、ユーザー98013)と別の投稿の空白を組み合わせて記入すると、これはiPadまたはiPadでポートレートモードまたはランドスケープモードですぐに使用できます。
@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
私のアプローチ:
最初にUITextFieldをサブクラス化し、indexPathプロパティを追加します。 cellFor ...メソッドiでindexPathプロパティを渡します。
次に、次のコードを追加します。
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);}];
textFieldShould/WillBegin ...などへ.
キーボードが表示されなくなったら、次の方法でキーボードを逆にする必要があります。
[UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, 0);}];
Three20
を使用する場合は、autoresizesForKeyboard
プロパティを使用します。 View Controllerの-initWithNibName:bundle
メソッドに設定するだけです
self.autoresizesForKeyboard = YES
これは次のことを行います
完了しました。
正しい答えはサム・ホーの答えです:
「UIViewControllerの代わりにUITableViewControllerを使用すると、自動的に使用されます。」.
UITableViewをUITableViewControllerのTableViewプロパティに必ず接続してください(UITableViewControllerのViewプロパティのサブビューとして追加しないでください)。
また、UITableViewのAutoresizingMaskプロパティをFlexibleHeightに設定してください。
Uitableviewを使用してテキストフィールドを配置する場合( Jeff Lamarcheから )、このようなデリゲートメソッドを使用して、tableviewをスクロールできます。
(注:私のテキストフィールドは、tableviewの行と同じインデックスを持つ配列に格納されます)
- (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];
}
キーボード通知は機能しますが、そのためのAppleのサンプルコードは、スクロールビューがウィンドウのルートビューであると想定しています。これは通常そうではありません。適切なオフセットを得るには、タブバーなどを補正する必要があります。
思ったより簡単です。 UITableViewControllerで使用するコードは次のとおりです。 2つのインスタンス変数、hiddenRectと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;
}
UITextField's
delegate
メソッドを使用します。
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
}
- (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;
}
より合理化されたソリューション。 UITextFieldデリゲートメソッドに組み込まれるため、UIKeyboard通知をいじる必要はありません。
実装ノート:
kSettingsRowHeight-UITableViewCellの高さ。
offsetTargetとoffsetThresholdはkSettingsRowHeightに基づいています。別の行の高さを使用する場合、それらの値をポイントのyプロパティに設定します。 [alt:別の方法で行オフセットを計算します。]
- (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;
}
私はあなたの問題のようなものに遭遇しました(iPhoneのsettings.appに似た画面に多数の編集可能なセルが別のものの上に積み重ねられていることを望みました)と、このアプローチがうまくいくことがわかりました:
プロトコルのセットを持つ要点 を作成しました。これにより、キーボードが表示、非表示、または変更されたときに余分なスペースを追加する作業が簡単になります。
機能:
スクロールビューを含むView Controllerの基本的な使用例(テーブルビューももちろんサポートされています)。
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 }
}
プロトコルKeyboardChangeFrameObserver
は、キーボードフレームが変更されるたびにイベントを起動します(表示、非表示、フレーム変更を含む)。
addKeyboardFrameChangesObserver()
または同様のメソッドでviewWillAppear()
を呼び出します。removeKeyboardFrameChangesObserver()
または同様のメソッドでviewWillDisappear()
を呼び出します。ModifableInsetsOnKeyboardFrameChanges
プロトコルは、UIScrollView
サポートをコアプロトコルに追加します。キーボードフレームが変更されると、スクロールビューのインセットが変更されます。
クラスはスクロールビューを設定する必要があります。キーボードフレームを変更すると、インセットが増減します。
var scrollViewToModify: UIScrollView { get }
テーブルにテキストフィールドがあるので、最善の方法は実際にテーブルのサイズを変更することです-キーボードのサイズ(約165ピクセルだと思います)だけtableView.frameの高さを低く設定し、次にそれを再度展開する必要がありますキーボードが閉じられます。
ユーザーをスクロールさせたくない場合は、オプションでその時点でtableViewのユーザー操作を無効にすることもできます。
SwiftでUITableViewCellのUITextFieldのindexPathを取得 :からのテキストフィールドの正確なポイントを使用した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)
}
非常に興味深いディスカッションスレッド、私は同じ問題に直面しました。
そこで、ここのスレッドを読んで、私のバージョンを実装しました。これは、iPadでlandscapeモードでコンテンツをプッシュするのに役立ちました。ここにコードがあります(これは絶対確実なものではありませんが、私の問題を修正しました)最初に、カスタムセルクラスにデリゲートを作成する必要があります
//ハンドルランドスケープモードのみに実装
- (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];
}
}
// 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
GoogleとStack Overflowで見つかった大量のソリューションを紹介した後、私は自分でそのような問題を解決しました。
まず、UIScrollViewのIBOutletを設定したことを確認してから、 Apple Doc:Keyboard Management をよく見てください。最後に、背景をスクロールできてもキーボードがテキストフィールドを覆っている場合は、次のコードをご覧ください。
// 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];
この作品とAppleの主な違いはif条件にあります。 Appleのスクロール距離の計算と、キーボードで覆われているテキストフィールドが正確でないかどうかの条件を信じているので、上記のように修正しました。
動作するかどうか教えてください
私はほとんど同じアプローチを試みましたが、同じためのよりシンプルで小さなコードを思いつきました。 IBOutlet iTextViewを作成し、IBのUITextViewに関連付けました。
-(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];
}
これは完全に機能し、iPadでも機能します。
- (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];
}
このソルトンは私のために働いています、ラインに注意してください
[tableView setContentOffset:CGPointMake(0.0, activeField.frame.Origin.y-kbSize.height+160) animated:YES];
あなたがそれに合うように160の値を変更することができます
- (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];
}
そのため、これらの現在のソリューションを使用しようとする(そして完全に失敗する)数時間の過酷な作業の後、私は最終的に物事がうまく機能するようになり、新しいアニメーションブロックを使用するように更新しました。私の答えは、完全に 上記のOrtwinの答え に基づいています。
なんらかの理由で、上記のコードは私にはうまくいきませんでした。私の設定は他の設定とかなり似ているように見えましたが、おそらくiPadまたは4.3を使用していたからでしょう。奇抜な計算をして、テーブルビューをスクリーンから放り出していました。
私のソリューションの最終結果を参照してください: http://screencast.com/t/hjBCuRrPC (写真を無視してください。:-P)
だから私はOrtwinがやっていることの要点で行ったが、キーボードの高さで私のテーブルビューのOrigin.yとsize.heightを加算するために、それが何らかの計算をする方法を変えた。その結果からウィンドウの高さを引くと、どのくらいの交差点が続いているかがわかります。 0より大きい(別名オーバーラップがある)場合、フレームの高さのアニメーションを実行します。
さらに、1)アニメーションが完了するまでセルにスクロールするのを待つこと、および2)キーボードを非表示にするときにUIViewAnimationOptionBeginFromCurrentStateオプションを使用することによって解決される再描画の問題がいくつかありました。
注意点がいくつかあります。
繰り返しますが、もし私がOrtwinがその核心を提供しなければ、私はこの答えに近づかなかったでしょう。コードは次のとおりです。
- (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;
}
UITableViewがUITableViewではなくUITableViewControllerのサブクラスで管理され、テキストフィールドデリゲートがUITableViewControllerである場合、すべてのスクロールを自動的に管理する必要があります。これらの他のコメントはすべて実際に実装するのが非常に困難です。
良い例については、Appleサンプルコードプロジェクト:TaggedLocationsを参照してください。
自動的にスクロールすることがわかりますが、これを行うコードはないようです。このプロジェクトにはカスタムテーブルビューセルもあるため、それをガイドとして使用してアプリケーションを構築する場合、目的の結果が得られるはずです。
私はこれらを使用していますが、それらは魅力のように機能します:
BSKeyboardControls- BSKeyboardControls github
TPKeyboardAvoiding- TPKeyboardAvoiding github
私はこれをプロジェクトでよく使用します。このソリューションは、スクロールビュー、テーブルビュー、またはコレクションビューで機能し、セットアップが簡単です。また、キーボードの「次へ」ボタンを自動的に接続して、テキストフィールドを切り替えます。
それを確認してください こちら
別の簡単な方法(1つのセクションでのみ機能します)
//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];
Swift 4.2 ...の小さな変動.
UITableViewには多くのセクションがありましたが、フローティングヘッダー効果を回避する必要があったため、「dummyViewHeight」アプローチを使用しましたStack Overflowのどこかで見た...これがこの問題の私の解決策です(キーボード+ツールバー+提案でも機能します):
クラス定数として宣言します。
let dummyViewHeight: CGFloat = 40.0
それから
override func viewDidLoad() {
super.viewDidLoad()
//... some stuff here, not needed for this example
// Create non floating header
tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: dummyViewHeight))
tableView.contentInset = UIEdgeInsets(top: -dummyViewHeight, left: 0, bottom: 0, right: 0)
addObservers()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
removeObservers()
}
そして、ここにすべての魔法...
@objc func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
let keyboardHeight = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue.size.height
tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight))
tableView.contentInset = UIEdgeInsets(top: -dummyViewHeight, left: 0, bottom: keyboardHeight, right: 0)
}
}
@objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.25) {
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: self.dummyViewHeight))
self.tableView.contentInset = UIEdgeInsets(top: -self.dummyViewHeight, left: 0, bottom: 0, right: 0)
}
}
簡単で迅速なソリューション。
スクロールが発生するたびに右のセルにスクロールします
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
テーブルがこのモード「_keepMyCellOnTop」にあることを知っていると仮定します。選択したセル「_selectedCellIndex」を知っているか、選択したセルまでスクロールします。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (_keepMyCellOnTop)
{
[self.tableView scrollToRowAtIndexPath:_selectedCellIndex atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
}
これにより、スクロールが防止されます。
-(void) scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
にコードを配置すると、上下にスクロールします
UITableViewController
は、実際にスクロールを自動的に行います。 UIViewController
を使用する場合との違いは、NavigationController
を使用する場合は、TableViewController
を使用してプログラムでNavbar-Buttonitemsを作成する必要があることです。
私のソリューション(またはQuickDialogの)を帽子に投げ込みます。基本的に、スクロールに合わせてアニメーション化するのを待ちます。マジックナンバーの代わりにキーボードアニメーションJITを取得するのは良いことです。
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
if (textField == self.emailTextField) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 50 * USEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
});
}
}
これが、Sam HoとMarcel Wの答えが混ざったこの作品の作り方と、私のくだらないコードに対するバグ修正の一部です。 UITableViewControllerを使用していました。キーボードが表示されると、テーブルのサイズが正しく変更されるようになりました。
1)viewDidLoad
に以下を追加しました:
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
2)super
およびviewWillAppear
のawakeFromNib
に相当するものを呼び出すのを忘れていました。これらを再び追加しました。
viewdidloadで
-(void)viewdidload{
[super viewdidload];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
-(void)keyboardWillChange:(NSNotification*)sender{
NSLog(@"keyboardwillchange sender %@",sender);
float margin=0 // set your own topmargin
CGFloat originY = [[sender.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].Origin.y;
if (originY >= self.view.frame.size.height){
NSLog(@"keyboardclose");
[tb_ setFrame:CGRectMake(0, margin, self.view.frame.size.width, self.view.frame.size.height-margin)];
}else{
NSLog(@"keyobard on");
float adjustedHeight = self.view.frame.size.height - margin - (self.view.frame.size.height-originY);
[tb_ setFrame:CGRectMake(0, margin, self.view.frame.size.width, adjustedHeight)];
}
}
私のバージョンを見てください:)
- (void)keyboardWasShown:(NSNotification *)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect bkgndRect = cellSelected.superview.frame;
bkgndRect.size.height += kbSize.height;
[cellSelected.superview setFrame:bkgndRect];
[tableView setContentOffset:CGPointMake(0.0, cellSelected.frame.Origin.y-kbSize.height) animated:YES];
}
- (void)keyboardWasHidden:(NSNotification *)aNotification
{
[tableView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
}
最善の方法はUITableViewControllerを使用することだと思います。
IViewControllerにUITableViewが必要な場合、UITableViewControllerが埋め込まれたContentViewを作成し、UIViewControllerのviedDidLoadに次の行を配置します。
self.tableView = ((UITableViewController*)self.childViewControllers[0]).tableView;
self.tableView.delegate = self;
self.tableView.dataSource = self;
簡単;)
UITableViewControllerを使用しているときに別のバグを発見しました。キーボードが表示されたときに自動的にスクロールしていませんでした。 UITableViewのcontentInsetAdjustmentBehavior = .neverが原因であることに気付きました。
IOS7カレンダーアプリの「イベント編集」画面に触発された私のソリューションを次に示します。
このソリューションの重要なポイントの1つは、ユーザーがテーブルをスクロールするとキーボードが閉じられることです。
実装:
1)選択したテキストフィールドを保存するプロパティを追加します。
@property (strong) UITextField *currentTextField;
ユーザーがテーブルをスクロールするときにキーボードを非表示にする必要があるかどうかを確認するために使用するBOOL変数。
BOOL hideKeyboardOnScroll;
2)UITextFieldデリゲートコールバックの処理:
#pragma mark - UITextFieldDelegate
- (void) textFieldDidBeginEditing: (UITextField *) textField {
self.currentTextField = textField;
}
- (void) textFieldDidEndEditing: (UITextField *) textField {
self.currentTextField = nil;
}
- (BOOL) textFieldShouldReturn: (UITextField *) textField {
[textField resignFirstResponder];
CGPoint newContentOffset = CGPointZero;
if (tableView.contentSize.height > tableView.frame.size.height) {
newContentOffset.y = MIN(tableView.contentOffset.y, tableView.contentSize.height - tableView.frame.size.height);
}
[tableView setContentOffset: newContentOffset animated: YES];
return YES;
}
3)UIScrollViewDelegateメソッドを処理して、そのユーザースクロールビューを確認します。
#pragma mark - UIScrollViewDelegate
- (void) scrollViewDidScroll: (UIScrollView *) scrollView {
if (hideKeyboardOnScroll == YES) {
[self.currentTextField resignFirstResponder];
}
}
4)viewcontrollerの[viewWillAppear]メソッドでキーボード通知をサブスクライブし、[viewWillDisappear]メソッドでサブスクライブを解除します。
- (void) viewWillAppear: (BOOL) animated {
[super viewWillAppear: animated];
[ [NSNotificationCenter defaultCenter] addObserver: self selector: @selector(keyboardWillShow:)
name: UIKeyboardWillShowNotification object: nil];
[ [NSNotificationCenter defaultCenter] addObserver: self selector: @selector(keyboardWillHide:)
name: UIKeyboardWillHideNotification object: nil];
}
- (void) viewWillDisappear: (BOOL) animated {
[super viewWillDisappear: animated];
[ [NSNotificationCenter defaultCenter] removeObserver: self name: UIKeyboardDidShowNotification object: nil];
[ [NSNotificationCenter defaultCenter] removeObserver: self name: UIKeyboardWillHideNotification object: nil];
}
5)キーボード通知の処理:
- (void) keyboardWillShow: (NSNotification *) notification {
CGRect keyboardFrame = [ [ [notification userInfo] objectForKey: UIKeyboardFrameBeginUserInfoKey] CGRectValue];
// Find cell with textfield.
CGRect textFieldFrame = [tableView convertRect: self.currentTextField.frame fromView: self.currentTextField];
NSIndexPath *indexPath = [tableView indexPathForRowAtPoint: textFieldFrame.Origin];
UITableViewCell *cell = [tableView cellForRowAtIndexPath: indexPath];
//
// Shrink tableView size.
CGRect tableViewFrame = tableView.frame;
tableView.frame = CGRectMake(tableView.frame.Origin.x, tableView.frame.Origin.y, tableView.frame.size.width,
self.view.frame.size.height - tableView.frame.Origin.y - keyboardFrame.size.height);
//
// Check if cell is visible in shrinked table size.
BOOL cellIsFullyVisible = YES;
if ( cell.frame.Origin.y < tableView.contentOffset.y ||
(cell.frame.Origin.y + cell.frame.size.height) > (tableView.contentOffset.y + tableView.frame.size.height) ) {
cellIsFullyVisible = NO;
}
//
// If cell is not fully visible when scroll table to show cell;
if (cellIsFullyVisible == NO) {
CGPoint contentOffset = CGPointMake(tableView.contentOffset.x, CGRectGetMaxY(cell.frame) - tableView.frame.size.height);
if (cell.frame.Origin.y < tableView.contentOffset.y) {
contentOffset.y = cell.frame.Origin.y;
}
contentOffset.y = MAX(0, contentOffset.y);
// For some reason [setContentOffset] is called without delay then
// this code may not work for some cells. That why we call it with brief delay.
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[UIView animateWithDuration: 0.5 animations:^{
[tableView setContentOffset: contentOffset animated: NO];
} completion: ^(BOOL finished) {
hideKeyboardOnScroll = YES;
}];
});
} else {
hideKeyboardOnScroll = YES;
}
//
// Finally restore original table frame.
tableView.frame = tableViewFrame;
//
}
- (void) keyboardWillHide: (NSNotification *) notification {
[super keyboardWillHide: notification];
hideKeyboardOnScroll = NO;
}
IOS 5.0のlibリファレンスをもう一度調べたところ、「キーボードの下にあるコンテンツの移動」というタイトルのセクションが見つかりました。 TextAndWebiPhoneOS KeyboardManagement
これは、おそらくiOS 5以降の新機能ですか?他のことをしている最中なのでまだ読んでいませんが、おそらく他の人はもっと知っていて、ここで私や他の人を啓発できるでしょう。
Apple文書は、ここで説明した内容よりも優先されますか、それともiOS 5 SDKユーザーにとって有用な情報ですか?
これを行う「正しい」方法はないと思います。ユースケースに最適なソリューションを選択する必要があります。 iPadアプリでは、UIViewController
としてモーダル表示されるUIModalPresentationFormSheet
があり、UITableView
で構成されています。このテーブルには、セルごとに2つのUITextFields
が含まれます。 scrollToRowAtIndexPath:atScrollPosition:animated:
メソッドでtextFieldDidBeginEditing:
を呼び出すだけでは機能しません。したがって、私はtableFooterView
を作成しました:
- (void)viewDidLoad
{
[super viewDidLoad];
m_footerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, m_tableView.frame.size.width, 300.0f)];
[m_footerView setBackgroundColor:[UIColor clearColor]];
[m_tableView setTableFooterView:m_footerView];
[m_footerView release];
}
キーボードは、tableFooterView
ではなく、UITextFields
を非表示にするという考え方です。したがって、tableFooterView
は十分に大きくなければなりません。その後、scrollToRowAtIndexPath:atScrollPosition:animated:
メソッドでtextFieldDidBeginEditing:
を使用できます。
キーボード通知のオブザーバーを追加することでtableFooterView
を動的に表示および非表示にすることも可能だと思いますが、まだ試していません。
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification
{
[m_tableView setTableFooterView:m_footerView];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
[m_tableView setTableFooterView:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
アニメーションとキーボードフレームの変更を伴うSwift 3-4のソリューション:
まず、Boolを作成します。
// MARK: - Private Properties
private var isKeyboardShowing = false
次に、システムキーボード通知にオブザーバーを追加します。
// MARK: - Overriding ViewController Life Cycle Methods
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: .UIKeyboardWillChangeFrame, object: nil)
}
第三に、アニメーション機能を準備します。
func adjustTableViewInsets(keyboardHeight: CGFloat, duration: NSNumber, curve: NSNumber){
var extraHeight: CGFloat = 0
if keyboardHeight > 0 {
extraHeight = 20
isKeyboardShowing = true
} else {
isKeyboardShowing = false
}
let contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight + extraHeight, right: 0)
func animateFunc() {
//refresh constraints
//self.view.layoutSubviews()
tableView.contentInset = contentInset
}
UIView.animate(withDuration: TimeInterval(duration), delay: 0, options: [UIViewAnimationOptions(rawValue: UInt(curve))], animations: animateFunc, completion: nil)
}
次に、ターゲット/アクションメソッド(オブザーバーによって呼び出される)を追加します。
// MARK: - Target/Selector Actions
func keyboardWillShow(notification: NSNotification) {
if !isKeyboardShowing {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
let keyboardHeight = keyboardSize.height
let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber
adjustTableViewInsets(keyboardHeight: keyboardHeight, duration: duration, curve: curve)
}
}
}
func keyboardWillHide(notification: NSNotification) {
let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber
adjustTableViewInsets(keyboardHeight: 0, duration: duration, curve: curve)
}
func keyboardWillChangeFrame(notification: NSNotification) {
if isKeyboardShowing {
let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber
if let newKeyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
let keyboardHeight = newKeyboardSize.height
adjustTableViewInsets(keyboardHeight: keyboardHeight, duration: duration, curve: curve)
}
}
}
最後に、deinitまたはviewWillDisappearでオブザーバーを削除することを忘れないでください。
deinit {
NotificationCenter.default.removeObserver(self)
}
// scroll tableview so content ends at the middle of the tableview (out of the way of the keyboard)
CGPoint newContentOffset = CGPointMake(0, [self.tableView contentSize].height - (self.tableView.bounds.size.height / 2));
[self.tableView setContentOffset:newContentOffset animated:YES];
計算は必要ありません。動作するコードの下で使用してください。
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)}
func keyboardWillShow(_ notification:Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
}}
func keyboardWillHide(_ notification:Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}}