텍스트 필드를 선택할 때 UITableView 스크롤 만들기


251

많은 시행 착오 끝에 나는 포기하고 질문을하고 있습니다. 비슷한 문제를 가진 많은 사람들을 보았지만 모든 답변을 올바르게 얻을 수는 없습니다.

UITableView커스텀 셀로 구성된을 가지고 있습니다 . 셀은 서로 옆에 5 개의 텍스트 필드로 구성됩니다 (그리드와 같은 정렬).

의 맨 아래에있는 셀을 스크롤하고 편집하려고하면 셀 UITableView이 키보드 위에 올바르게 배치되도록 할 수 없습니다.

뷰 크기 변경 등에 대해 많은 답변을 보았지만 지금까지 아무도 잘 작동하지 않았습니다.

누구든지 구체적인 코드 예제로 이것을 수행하는 "올바른"방법을 명확히 할 수 있습니까?


11
이 Applle 문서에는이 질문에 대한 솔루션을 구현하는 단계가 요약되어 있습니다. http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
ChrisP

@ChrisP이 링크는 iOS 4.0 용으로 업데이트되지 않았다고 말합니다
Bae

이 코드는 도움이 될 수 있습니다 : gist.github.com/TimMedcalf/9505416
landonandrey

Url 아래를 따라 가면 작동합니다. stackoverflow.com/questions/48922266/…
Venkatesh G

답변:


126

UIViewController 대신 UITableViewController를 사용하면 자동으로 수행됩니다.


13
시도하고 작동하지 않는 것을 발견 했습니까? 아니면 당신이 믿기에는 해결책이 너무 간단합니까? UIViewController 대신 UITableViewController를 확장하면 텍스트 필드가 포함 된 셀이 텍스트 필드가 첫 번째 응답자가 될 때마다 키보드 위로 스크롤됩니다. 추가 코드가 필요하지 않습니다.
Sam Ho

3
예, 그러나 특히 iPad에서는 UITableViewController를 포함하지 않는 방법이 필요합니다.
Bob Spryn 1

13
명확히하기 위해, 테이블 뷰를 사용할 때마다 특히 iPad에서 전체 화면이어야한다고 말하는 합리적인 대답은 아닙니다. 그렇게하지 않는 훌륭한 앱의 예가 있습니다. 예를 들어, iPad의 연락처 앱을 포함하여 많은 Apple 자체가 있습니다.
밥 Spryn

32
[super viewWillAppear : YES]를 재정의하면 작동하지 않습니다. 그 외에는 작동해야합니다.
Rambatino

18
viewWillAppear : (BOOL) animated를 재정의하는 경우 [super viewWillAppear : animated]; :)
Médéric Petit

93

스크롤을 수행하는 기능은 훨씬 간단 할 수 있습니다.

- (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];
}

그게 다야. 전혀 계산이 없습니다.


2
왜 안돼?! UITableViewScrollPositionTop을 UITableViewScrollPositionMiddle로 바꾸십시오. 물론 가시 영역을 조정하려면 UITableView의 크기를 조정하면됩니다.
Mihai Damian

3
키보드가 표시 될 때있는 UITableViewController는 테이블 뷰 크기 조정 돌봐 경우 작동하지 않는 것 : 컨트롤러가 함께 볼 크기 감소 contentInset요청할 때 분명히 고려되지 않은, visibleRows또는 indexPathsForVisibleRows.
Julian D.

16
테이블 뷰의 마지막 몇 행에는 작동하지 않습니다. 키보드는 여전히 키보드 위로 스크롤 할 수없는 모든 행을가립니다.
Alex Zavatone

3
자동 스크롤 동작이 테이블의 마지막 몇 행에서 작동하게하려면이 행이 편집을 시작하는시기를 감지하고 특정 높이의 빈보기로 테이블보기의 끝에 바닥 글을 추가하십시오. 이렇게하면 테이블 뷰가 셀을 올바른 위치로 스크롤 할 수 있습니다.
Sammio2

10
셀에 실제로 연결되어 있는지 확인하지 않으면 superview 호출 체인을 통해 셀에 액세스하는 것은 신뢰할 수 없습니다. 참조 stackoverflow.com/a/17757851/1371070stackoverflow.com/a/17758021/1371070
세자르

70

나는 매우 일반적인 일을하고 있으며, 코드에 특정한 것을 계산할 필요가 없습니다. 코드에서 언급을 확인하십시오.

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

스위프트 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()
        }
    }
}

장치 방향을 통합하는 동안 알림을 사용하고 키보드 높이를 얻는 것이 훌륭했습니다. 스크롤 부분이 어떤 이유로 든 나를 위해 작동하지 않았기 때문에 이것을 사용해야했습니다.[tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionMiddle animated: YES];
taber

7
이것이 내가 생각하는 가장 좋은 대답입니다. 매우 깨끗합니다. 단 두 가지 : 1) viewDidLoad가 [super viewDidLoad]를 호출하지 않고 2) frame.size.height 행에 대한 탭 바 계산이 필요했습니다. 그렇지 않으면 완벽합니다! 감사.
toxaq

3
toxaq에 대한 수정 사항은 다음과 같습니다. MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate]; CGFloat tabBarHeight = appDelegate.tabBarController.tabBar.frame.size.height; 그런 다음 키보드 높이를 사용할 때마다 키보드 높이에서 tabBarHeight를 뺍니다.
Steve N

1
사용자가 텍스트 필드를 탭하면 완벽하게 작동합니다. 그러나 사용자가 Return 키를 누르지 않고 다른 텍스트 필드를 누르면 테이블 뷰 크기가 줄어 듭니다.
Bhavin Ramani 2016 년

1
@BhavinRamani는 동의했다. 키보드가 이미 표시되는지 여부를 기억하고 불필요한 경우 코드 재실행을 건너 뛰기 위해 간단한 부울 속성을 추가했습니다.
Dirty Henry

46

Bartłomiej Semańczyk 솔루션을 기반으로 하는 Swift 3를 위한 가장 간단한 솔루션 :

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)
    })
}

사소한 세부 ... 사용 Notification대신은 NSNotification더 "스위프트 3-Y":-) 것
니콜라스 미아리

navbar가있는 경우 재배치에 도움이됩니다-let if if = = let frame = self.navigationController? .navigationBar.frame {let y = frame.size.height + frame.origin.y}
Sean Dev

회전이 발생했을 때 로딩에 결함이하고있는 tableview를 스크롤 할 때 일부 셀은 manully 사라
jothikenpachi

좋은 솔루션 감사합니다! 참고-더 이상 removeObserver를 수행 할 필요가 없습니다.
Nick McConnell

44

나는 같은 문제가 있었지만 한 가지 관점에서만 나타납니다. 그래서 컨트롤러의 차이점을 찾기 시작했습니다.

스크롤 동작이 - (void)viewWillAppear:(BOOL)animated슈퍼 인스턴스 에서 설정되어 있음을 알았습니다 .

따라서 다음과 같이 구현하십시오.

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

당신이 사용하는 경우 그리고 그것은 중요하지 않습니다 UIViewController또는 UITableViewController; UITableView에 self.view의 하위 뷰로 넣어서 확인했습니다 UIViewController. 같은 행동이었습니다. 통화 [super viewWillAppear:animated];가 없으면 보기를 스크롤 할 수 없습니다.


1
이것은 훌륭하게 작동했습니다. 사람들이 왜 UITableView가 나를 위해 그것을 할 것이라고 말했는지 궁금해졌습니다. 감사!
olivaresF

5
나는이 문제도 가지고 있었고,이 대답은 그것을 정상으로 만들어야합니다!
Amiel Martin

나는 내 자신에 그것을 알아 내려고 노력하는 데 너무 많은 시간을 잃었다 ... 고마워;)
budidino

+1이 조금 울기 시작했습니다. 그 줄이 있었지만 [tableViewController viewWillAppear : animated]도 필요했습니다. UITableViewController를 UIViewController에 추가하기 때문입니다. :) 더 눈물없이
콜린은 라마레

41

나는 여기에 전체 게시물을 읽지 않았으므로 이것을 놓쳤을 수도 있지만, 내가 생각해 낸 것은 기만적으로 단순 해 보입니다. 나는 이것을 모든 상황에서 테스트하는 데 wringer를 넣지 않았지만 그것이 잘 작동하는 것처럼 보입니다.

키보드의 높이로 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;
    }];
}

너무 간단합니까? 내가 뭔가를 놓치고 있습니까? 지금까지 그것은 나를 위해 잘 작동하지만, 내가 말했듯이, 나는 그것을 wringer를 넣지 않았습니다 ...


IMO는 이것이 최고의 솔루션입니다. 내가 바꾼 유일한 것은 하드 코딩 된 지속 시간입니다[aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
Andy

매우 간단합니다. 그러나 내가 찾은 한 가지 문제는 변경 사항에 애니메이션을 적용하지 않고 contentInset스크롤 범위를 크게 변경 한다는 것입니다 .
Geek

그러나 이것은 나에게 가장 효과적이었습니다. 1) "currentField.indexPath"를 얻을 수있는 곳을 모르겠으므로 indexPath.row를 필드의 태그로 저장하고 나중에 indexPath를 만들어야했습니다. 2) 테이블 상단의 행에는 작동하지 않으며 화면 밖으로 스크롤됩니다. currentField의 indexPath가 화면에 맞는 것보다 큰 경우에만 스크롤하도록 코드를 추가해야했습니다. 3) 가로 인 경우 iPad에서 kbSize.Width (높이 대신)를 사용해야했습니다
Travis M.

죄송합니다. 우리 자신의 코드에 너무 익숙해 져 가끔 잊어 버리는 경우도 있습니다. currentField 내가 함께 일하고 있어요 현재 텍스트 필드, 그리고 내가이있는 세포 무엇인지 알 수 있도록 단순히 NSIndexPath를 추가하는 indexPath 내가 클래스에 추가 한 확장 기능입니다.
mickm

이것은 테이블 속성을 수정하는 것만으로 프레임을 이동하지 않는 방법입니다.
Nextorlg

35

애플 앱의 동작에 맞는 솔루션을 생각해 냈습니다.

먼저 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];
    }
}

이제 스크롤 비트가 있습니다. 먼저 몇 가지 크기를 계산 한 다음 보이는 영역의 위치를 ​​확인하고 스크롤하려는 rect를 텍스트 필드의 중간 위 또는 아래의 절반보기로 설정하십시오. 보기의 위치에 있습니다. 이 경우, 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];
}

이것은 꽤 잘 작동하는 것 같습니다.


좋은 해결책. 게시 해 주셔서 감사합니다.
Alex Reynolds

2
UIKeyboardBoundsUserInfoKeyiOS 3.2부터는 더 이상 사용되지 않습니다. 모든 현재 iOS 릴리스 ≥ 3.0에서 작동하는 아래의 솔루션을 참조하십시오. / @ iPhoneDev
Ortwin Gentz

필요 이상으로 복잡했습니다. @ user91083의 답변은 간단하고 효과적입니다.
Richard Brightwell

1
이 솔루션에는 작은 문제가 있습니다. keyboardWillShow를 AFTER textFieldDidBeginEditing이라고합니다. 따라서 일부 셀로 스크롤하려고 할 때 tableView의 프레임이 아직 변경되지 않았으므로 작동하지 않습니다.
HiveHicks

35

를 사용할 수 있으면 UITableViewController기능이 무료로 제공됩니다. 그러나 때로는 옵션이 아닌 경우가 있습니다. 특히 UITableView.

여기에 제시된 솔루션 중 일부는 iOS ≥4에서 작동하지 않으며, 일부는 iPad 또는 가로 모드에서 작동하지 않으며, 일부는 Bluetooth 키보드 (스크롤링을 원하지 않는) 작동하지 않습니다. 여러 텍스트 필드를 전환 할 때 작동합니다. 따라서 솔루션을 선택하면 이러한 사례를 테스트해야합니다. 이것은 우리가 솔루션을 사용하십시오 사용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 에있는 클래스전체 코드입니다 . 이를 테스트하려면 위에서 언급 한 시나리오를 테스트 할 수있는 "전체 목록"하위 분할 창을 사용하십시오.


상수 대신 문자열을 사용하는 것이 유용한 지 모르겠습니다. Apple이 어떤 이유로 문자열을 내부적으로 변경하려는 아이디어가 나오면 솔루션이 더 이상 작동하지 않기 때문입니다. 더 이상 사용되지 않을 때 경고가 표시되지 않습니다. 생각합니다

@iPortable : 이상적이지 않습니다. 모든 버전 ≥3.0에서 실행되는 더 나은 솔루션을 제안 할 수 있습니까?
Ortwin Gentz

1
매력처럼 작동하지만 UIInterfaceOrientationPortraitUpsideDown에서는 작동하지 않습니다. 그런 다음 높이 감소 계산도 거꾸로해야합니다. CGFloat reduceHeight = keyboardRect.size.height-(CGRectGetMinY (viewRectAbsolute)-CGRectGetMinY (windowRect));
Klaas

이것은 내 iPad 및 시뮬레이터 (4.3)에서 눈에 띄는 시각적 결함이 있습니다. 사용하기 너무 눈에.니다. :(
Bob Spryn

이 솔루션이 화면 하단의 툴바를 설명한다는 점이 마음에 듭니다.
pdemarest

24

스위프트를 위한 가장 간단한 솔루션 :

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
    }

최소한의 계산만으로 완벽하게 작동합니다. 이 답변이 완료되도록 테이블 삽입을 복원하는 코드를 추가했습니다.
Vitalii

최고의 솔루션 감사합니다. : 나는 여기 스위프트 3 버전 게시했습니다 stackoverflow.com/a/41040630/1064438
squall2022

슈퍼 완벽한 솔루션은 다른 것을 시도했지만 몇 가지 문제가 있습니다. iOS 10.2에서 완벽하게 작동합니다.
Wangdu Lin

8

나는 너희들이 이미 그 모든 것을 읽는 해결책을 얻었기를 바랍니다. 그러나 다음과 같이 내 솔루션을 찾았습니다. 와 (과) 셀이 이미있을 것으로 예상합니다 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더 이상 키보드에 의해 당신의 의지가 숨겨지지 않기를 바랍니다 .


6

여러 답변 (특히 Ortwin Gentz, 사용자 98013)과 다른 게시물의 공백을 결합하고 채우면 세로 또는 가로 모드의 iPad에서 SDK 4.3의 상자에서 즉시 작동합니다.

@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

나는이 코드를 iOS 4.x에서 잘 사용했지만 iOS5에서는 scroll_OldPosition에서 _topmostRowBeforeKeyboardWasShown이 이미 해제되어 있기 때문에 충돌합니다. 솔루션이 무엇인지 아직 확실하지 않습니다. 아마도 객체 대신 색인을 기억하십시오.
Thomas Tempelmann

5

uitableview를 사용하여 텍스트 필드를 배치하는 경우 ( Jeff Lamarche의 ) delegate 메소드를 사용하여 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];
    }

tableView 프레임을 업데이트하지 않습니다. 그런 다음 키보드가 표시되면 scrollBars 및 스크롤 동작이 잘못되었습니다. 내 솔루션을 참조하십시오.
Ortwin Gentz

5

키보드 알림은 작동하지만 Apple의 샘플 코드는 스크롤보기가 창의 루트보기라고 가정합니다. 일반적으로 그렇지 않습니다. 올바른 오프셋을 얻으려면 탭 막대 등을 보정해야합니다.

소리보다 쉽습니다. UITableViewController에서 사용하는 코드는 다음과 같습니다. 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;
}

UIKeyboardCenterEndUserInfoKeyUIKeyboardBoundsUserInfoKey아이폰 OS 3.2으로 사용되지 않습니다. 모든 현재 iOS 릴리스 ≥ 3.0에서 작동하는 아래의 솔루션을 참조하십시오.
Ortwin Gentz

5

당신이 사용하는 경우 Three20, 다음 사용 autoresizesForKeyboard속성을. 뷰 컨트롤러의 -initWithNibName:bundle방법으로 설정하십시오.

self.autoresizesForKeyboard = YES

이것은 다음을 처리합니다.

  1. 키보드 알림 청취 및 테이블 뷰 프레임 조정
  2. 첫 번째 응답자로 스크롤

완료했다.


Three20은 무엇입니까? 지정할 수 있습니까?
Mubin Mall

5

내 접근 방식 :

먼저 UITextField를 하위 클래스로 만들고 indexPath 속성을 추가합니다. cellFor ... 메소드에서 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);}];

4

보다 유선형 솔루션. UITextField 대리자 메서드에 빠지기 때문에 UIKeyboard 알림을 엉망으로 만들 필요가 없습니다.

구현 노트 :

kSettingsRowHeight-UITableViewCell의 높이입니다.

offsetTarget 및 offsetThreshold는 kSettingsRowHeight에서 제외됩니다. 다른 행 높이를 사용하는 경우 해당 값을 point의 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;

}


4

사용 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
}

목표 -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;
}

안녕하세요, Swift에서 작동하도록 문제가 있습니다. 내 UITextFields가 UITableViewCell에 연결되었습니다. UIViewController 내에이 코드를 구현하면 UITextField에 액세스 할 수 없습니다. 어떤 아이디어?
Vetuka

4

스위프트 4.2 완벽한 솔루션

키보드가 표시되거나 숨겨 지거나 변경 될 때 공간을 추가하는 작업을 단순화 하는 프로토콜 세트로 GIST를 만들었 습니다.

특징 :

  • 키보드 프레임 변경 (예 : 이모티콘 → 일반 키보드와 같은 키보드 높이 변경)에서 올바르게 작동합니다.
  • UITableView 예제에 대한 TabBar 및 ToolBar 지원 (다른 예제에서는 잘못된 삽입이 수신 됨).
  • 동적 애니메이션 지속 시간 (하드 코딩되지 않음).
  • 사용자의 목적에 맞게 쉽게 수정할 수있는 프로토콜 중심 접근 방식.

용법

일부 스크롤보기가 포함 된보기 컨트롤러의 기본 사용법 예 (테이블보기도 물론 지원됩니다).

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은 키보드 프레임이 변경 될 때마다 이벤트를 발생시킵니다 (표시, 숨기기, 프레임 변경 포함).

  1. 전화 addKeyboardFrameChangesObserver()viewWillAppear()또는 이와 유사한 방법.
  2. 전화 removeKeyboardFrameChangesObserver()viewWillDisappear()또는 이와 유사한 방법.

구현 : 스크롤보기

ModifableInsetsOnKeyboardFrameChanges프로토콜 UIScrollView은 핵심 프로토콜에 대한 지원을 추가 합니다. 키보드 프레임이 변경되면 스크롤보기의 삽입물이 변경됩니다.

수업은 스크롤보기를 설정해야하며 키보드 프레임 변경시 인셋이 증가 / 감소합니다.

var scrollViewToModify: UIScrollView { get }

3

테이블에 텍스트 필드가 있으므로 가장 좋은 방법은 테이블의 크기를 조정하는 것입니다. 키보드의 크기에 따라 tableView.frame의 높이를 더 작게 설정해야합니다 (약 165 픽셀이라고 생각합니다). 키보드가 닫힙니다.

사용자 스크롤을 원하지 않는 경우 해당 시점에 tableView에 대한 사용자 상호 작용을 선택적으로 비활성화 할 수도 있습니다.


두 번째로 키보드 크기를 동적으로 찾기 위해 UIKeyboardWillShowNotification에 등록하십시오.
benzado

알림 객체가 반환 한 숫자는 작동하지 않습니다. 또는 적어도 2.2가 아니고 반환 된 숫자가 정확하지 않아서 높이를 올바르게 조정하기 위해 165 값을 하드 코딩해야했습니다 (5-10 픽셀 정도).
Kendall Helmstetter Gelner

2

이것은 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];
        }

왜 각 텍스트 필드에 대해 특수 사례를 iffing하고 사용하고 있습니까? 셀의 NSIndexPath에서 각 텍스트 필드를 식별하고 불쾌한 if 문을 2 줄의 코드로 변경하십시오. 실제로 cellForRowAtIndexPath 호출을 원한 다음 셀에서 textField를 가져옵니다.
Alex Zavatone

실제로 iOS에서이 상황이 얼마나 믿을 수 없는지 고려할 때,이 상황에 대해 "완전히 풀리고 말도 안되는 문자"코드를 작성하는 것이 좋습니다.
Fattie

이 답변을 고려한 것은 6 년 전입니다.
WrightsCS

2

나는 거의 같은 접근 방식을 시도하고 더 간단하고 작은 코드를 만들었습니다. 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];

    }

2

그래서 현재의 솔루션을 사용하려고 노력하는 몇 시간의 거친 작업 (그리고 완전히 실패한) 후에 마침내 일이 잘 이루어지고 새로운 애니메이션 블록을 사용하도록 업데이트했습니다. 내 답변은 전적으로 Ortwin의 답변을 기반으로 합니다.

그래서 어떤 이유로 든 위의 코드가 저에게 효과적이지 않았습니다. 내 설정은 다른 설정과 상당히 비슷해 보였지만 아마도 iPad 또는 4.3에 있었기 때문일 수 있습니다. 그것은 엉뚱한 수학을하고 있었고 화면에서 테이블 뷰를 찍었습니다.

내 솔루션의 최종 결과를 참조하십시오 : http://screencast.com/t/hjBCuRrPC (사진을 무시하십시오. :-P)

그래서 Ortwin 이하 고있는 일의 요점과 함께 갔지만 키보드 높이로 테이블 뷰의 origin.y & size.height를 추가하는 수학 계산 방식을 변경했습니다. 그 결과에서 창의 높이를 빼면 교차로가 얼마나 많은지 알려줍니다. 0보다 크면 (일부 중복이 있음) 프레임 높이의 애니메이션을 수행합니다.

또한 1) 애니메이션이 완료 될 때까지 셀로 스크롤 대기 및 2) 키보드를 숨길 때 UIViewAnimationOptionBeginFromCurrentState 옵션을 사용하여 다시 그리기 문제가 해결되었습니다.

몇 가지 유의할 사항.

  • _topmostRowBeforeKeyboardWasShown & _originalFrame은 헤더에 선언 된 인스턴스 변수입니다.
  • self.guestEntryTableView는 내 tableView입니다 (외부 파일에 있습니다)
  • IASKCGRectSwap은 프레임의 좌표를 뒤집는 Ortwin의 방법입니다.
  • 적어도 50px가 표시 될 경우에만 tableView의 높이를 업데이트하십시오.
  • UIViewController에 없기 때문에 self.view가 없으므로 tableView를 원래 프레임으로 반환합니다.

다시 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;
}

프레임 등을 업데이트하기 전에 뷰의 좌표 시스템을 수정하는 FixOriginRotation 함수를 추가했습니다. 이것이 처음에 문제가 발생한 이유 중 일부라고 생각합니다. 장치와 함께 회전 된 iOS Window Coordinate System을 인식하지 못했습니다!
밥 Spryn

2

이 솔루션은 저에게 효과적입니다.

[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];
}

2

매우 흥미로운 토론 스레드, 나는 또한 같은 문제가 더 악화 될 수 있기 때문에 직면했다

  1. 나는 커스텀 셀을 사용하고 있었고 그 안에 텍스트 필드가있었습니다.
  2. UITableController를 사용하여 요구 사항을 충족해야하므로 UITableViewController를 활용할 수 없습니다.
  3. 테이블 셀에 필터 / 정렬 기준이 있습니다. 즉, ur 셀은 인덱스 경로를 계속 변경하고 추적하므로 모든 것이 도움이되지 않습니다.

따라서 여기에서 스레드를 읽고 내 버전을 구현하여 가로 모드 에서 iPad의 컨텐츠를 올리는 데 도움이되었습니다 . 여기 코드가 있습니다 (이것은 바보가 아니며 전부 문제입니다.하지만 문제가 해결되었습니다). 첫 번째 u는 편집을 시작할 때 텍스트 필드를 ur 컨트롤러로 보내서 activefield = theTextField를 설정하는 사용자 정의 셀 클래스에 대리자가 있어야합니다.

// 핸들링 모드 만 구현

- (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


2

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 조건에 있습니다. 스크롤 거리에 대한 사과의 계산 및 키보드로 덮인 텍스트 필드의 조건이 정확하지 않다고 생각하므로 위와 같이 수정했습니다.

작동하는지 알려주세요


2

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)
}

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];

1

UITableView가 UITableView가 아닌 ​​UITableViewController의 서브 클래스에 의해 관리되고 텍스트 필드 델리게이트가 UITableViewController 인 경우 모든 스크롤을 자동으로 관리해야합니다. 이러한 모든 주석은 실제로 구현하기가 매우 어렵습니다.

좋은 예제를 보려면 애플 예제 코드 프로젝트 : TaggedLocations를 참조하십시오.

자동으로 스크롤되는 것을 알 수 있지만이 작업을 수행하는 코드는 없습니다. 이 프로젝트에는 또한 사용자 정의 테이블 뷰 셀이 있으므로이를 가이드로 사용하여 애플리케이션을 빌드하면 원하는 결과를 얻을 수 있습니다.


1

다음은 Sam Ho와 Marcel W의 답변이 혼합 된이 작업을 수행 한 방법과 엉터리 코드로 작성된 자체 버그 수정 중 일부입니다. UITableViewController를 사용하고있었습니다. 키보드가 표시되면 표의 크기가 올바르게 조정됩니다.

1) viewDidLoad나는 추가했다 :

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2) 및 의 super해당 항목 을 호출하는 것을 잊어 버렸습니다 . 나는 이것을 다시 추가했다.viewWillAppearawakeFromNib


1

UITableViewController실제로 스크롤을 자동으로 수행합니다. 사용 a에 비해 차이점은 UIViewController당신이를 사용하여 프로그래밍 Navbar를-Buttonitems을 만들 필요가 있다는 것입니다 NavigationController를 사용하는 경우, TableViewController.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.