Tạo cuộn UITableView khi trường văn bản được chọn


251

Sau rất nhiều thử nghiệm và sai sót, tôi bỏ cuộc và đặt câu hỏi. Tôi đã thấy rất nhiều người có vấn đề tương tự nhưng không thể có được tất cả các câu trả lời để làm việc đúng.

tôi có một UITableView bao gồm các tế bào tùy chỉnh. Các ô được tạo thành từ 5 trường văn bản cạnh nhau (giống như một lưới).

Khi tôi cố gắng cuộn và chỉnh sửa các ô ở dưới cùng của UITableView , tôi không thể quản lý để đặt các ô của mình được đặt đúng vị trí phía trên bàn phím.

Tôi đã thấy nhiều câu trả lời nói về việc thay đổi kích thước khung nhìn, v.v ... nhưng cho đến nay vẫn chưa có câu trả lời nào hoạt động tốt.

Bất cứ ai cũng có thể làm rõ cách "đúng" để làm điều này với một ví dụ mã cụ thể?


11
Tài liệu Applle này phác thảo các bước để thực hiện một giải pháp cho câu hỏi này. http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
ChrisP

@ChrisP Liên kết đó cho biết nó chưa được cập nhật cho iOS 4.0
Bae

Mã này có thể hữu ích: gist.github.com/TimMedcalf/9505416
Landonandrey

Thực hiện theo Url bên dưới, nó sẽ hoạt động: stackoverflow.com/questions/36122266/ trên
Venkatesh G

Câu trả lời:


126

Nếu bạn sử dụng UITableViewContaptor thay vì UIViewControll, nó sẽ tự động làm như vậy.


13
Bạn đã thử và thấy rằng không hoạt động? Hay là giải pháp quá đơn giản để bạn tin tưởng? Chỉ cần mở rộng UITableViewContaptor thay vì UIViewControll và ô chứa các trường văn bản sẽ cuộn phía trên bàn phím bất cứ khi nào các trường văn bản trở thành phản hồi đầu tiên. Không cần thêm mã.
Sam Ho

3
Có, nhưng đặc biệt là trên iPad, chúng tôi cần một cách để thực hiện việc này không liên quan đến UITableViewControll.
Bob Spryn

13
Để làm rõ, đây không phải là một câu trả lời hợp lý để nói rằng mỗi khi bạn sử dụng chế độ xem bảng, nó cần phải ở chế độ toàn màn hình, đặc biệt là trên iPad. Có rất nhiều ví dụ về các ứng dụng tuyệt vời không làm điều đó. Chẳng hạn, nhiều sản phẩm của Apple, bao gồm cả ứng dụng Danh bạ trên iPad.
Bob Spryn

32
Nó sẽ không hoạt động nếu bạn ghi đè [super viewWillAppear: CÓ]. Ngoài ra, nó nên hoạt động.
Rambatino

18
Nếu bạn ghi đè viewWillAppear: (BOOL) hoạt hình, đừng quên gọi [super viewWillAppear: hoạt hình]; :)
Médéric Petit

93

Chức năng cuộn có thể đơn giản hơn nhiều:

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

Đó là nó. Không có tính toán nào cả.


2
Và tại sao không?! Chỉ cần thay thế UITableViewScrollPocationTop bằng UITableViewScrollPocationMiddle. Tất nhiên, bạn chỉ cần thay đổi UITableView để điều chỉnh vùng hiển thị.
Mihai Damian

3
Dường như không hoạt động nếu UITableViewCont kiểm soát thay đổi kích thước chế độ xem bảng khi bàn phím được hiển thị: bộ điều khiển giảm kích thước hiển thị với a contentInset, dường như không được tính đến khi yêu cầu visibleRowshoặc indexPathsForVisibleRows.
Julian D.

16
Không hoạt động cho một vài hàng cuối của chế độ xem bảng. Bàn phím vẫn sẽ che khuất tất cả các hàng không thể cuộn phía trên bàn phím.
Alex Zavatone

3
Để làm cho hành vi cuộn tự động hoạt động trên một vài hàng cuối của bảng, hãy phát hiện khi các hàng này bắt đầu chỉnh sửa và thêm chân trang vào cuối chế độ xem bảng với chế độ xem trống ở độ cao nhất định. Điều này sẽ cho phép xem bảng để di chuyển các ô đến đúng nơi.
Sammio2

10
Đến phòng giam thông qua một chuỗi các cuộc gọi đến giám sát là không đáng tin cậy, trừ khi bạn chắc chắn rằng bạn đang thực sự đến phòng giam. Xem stackoverflow.com/a/17757851/1371070stackoverflow.com/a/17758021/1371070
Cezar

70

Tôi đang làm một cái gì đó rất giống nhau, nó không cần phải tính toán một cái gì đó cụ thể cho mã của bạn. Chỉ cần kiểm tra các nhận xét về mã:

Trong MyUIViewControll.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

Trong MyUIViewControll.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

Phiên bản Swift 1.2+:

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

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

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

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

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

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

sử dụng các thông báo và nhận được chiều cao bàn phím trong khi kết hợp định hướng thiết bị là tuyệt vời, cảm ơn vì điều đó! phần cuộn không hoạt động với tôi vì một số lý do, vì vậy tôi đã phải sử dụng phần này:[tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionMiddle animated: YES];
taber

7
Đây là câu trả lời tốt nhất ở đây tôi nghĩ. Rất sạch sẽ. Chỉ có hai điều: 1) viewDidLoad của bạn không gọi [super viewDidLoad] và 2) Tôi đã phải có một số phép toán trên thanh tab trên các dòng frame.size.height. Nếu không thì hoàn hảo! Cảm ơn.
toxaq

3
Dưới đây là độc tố sửa đổi mô tả: MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate]; CGFloat tabBarHeight = appDelegate.tabBarController.tabBar.frame.size.height; Sau đó trừ tabBarHeight khỏi chiều cao bàn phím bất cứ nơi nào bạn sử dụng chiều cao bàn phím.
Steve N

1
Nếu người dùng chạm vào textfield nó hoạt động hoàn hảo. nhưng nếu người dùng chạm vào trường văn bản khác mà không nhấn phím quay lại thì nó sẽ giảm kích thước của chế độ xem bảng.
Bhavin Ramani

1
@BhavinRamani đồng ý. Tôi đã thêm một thuộc tính boolean đơn giản để ghi nhớ nếu bàn phím đã được hiển thị hay chưa và bỏ qua việc thực thi lại mã khi không cần thiết.
Bẩn Henry

46

Giải pháp đơn giản nhất cho Swift 3 , dựa trên giải pháp Bartłomiej Semańchot :

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

Một chi tiết nhỏ ... Sử dụng Notificationthay vì NSNotificationsẽ là "Swift 3-y" nhiều hơn :-)
Nicolas Miari

Điều này sẽ giúp định vị lại nếu có một thanh điều hướng - bao quanh UIView.animate với điều này if let - if let frame = self.navlationContoder? .NavestionBar.frame {let y = frame.size.height + frame.origin.y}
Sean Dev

khi quá trình quay xảy ra, có sự cố trong quá trình tải và một số ô biến mất khi chế độ xem bảng được cuộn một cách manully
jothikenpachi

Giải pháp tốt cảm ơn! Lưu ý - không cần thực hiện removeObserver nữa.
Nick McConnell

44

Tôi đã có cùng một vấn đề nhưng nhận thấy rằng nó chỉ xuất hiện trong một chế độ xem. Vì vậy, tôi bắt đầu tìm kiếm sự khác biệt trong bộ điều khiển.

Tôi phát hiện ra rằng hành vi cuộn được thiết lập trong - (void)viewWillAppear:(BOOL)animatedsiêu thể hiện.

Vì vậy, hãy chắc chắn để thực hiện như thế này:

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

Và nó không thành vấn đề nếu bạn sử dụng UIViewControllerhoặc UITableViewController; kiểm tra nó bằng cách đặt UITableViewdưới dạng một subview của self.view trong UIViewController. Đó là hành vi tương tự. Chế độ xem không cho phép cuộn nếu cuộc gọi [super viewWillAppear:animated];bị mất.


1
Điều này làm việc xuất sắc. Tôi đã tự hỏi tại sao mọi người nói UITableView sẽ làm điều đó cho tôi và điều này đã giải quyết nó. Cảm ơn!
olivaresF

5
Tôi đã có vấn đề này là tốt, câu trả lời này sẽ làm cho nó lên đến đỉnh!
Amiel Martin

Tôi đã mất rất nhiều thời gian để cố gắng tự mình tìm ra ... cảm ơn;)
budidino

+1 đã bắt đầu khóc một chút, tôi đã có dòng đó nhưng cũng cần [viewViewControll viewWillAppear: animation]; bởi vì tôi đang thêm một UITableViewControll vào UIViewControll. không còn nước mắt nữa :)
colin lamarre

41

Tôi có thể đã bỏ lỡ điều này, vì tôi đã không đọc toàn bộ bài viết ở đây, nhưng những gì tôi nghĩ ra có vẻ đơn giản. Tôi đã không đặt nó qua máy vắt, thử nghiệm trong mọi tình huống, nhưng có vẻ như nó sẽ hoạt động tốt.

chỉ cần điều chỉnh nội dung Cài đặt của chế độ xem bảng theo chiều cao của bàn phím và sau đó cuộn ô xuống dưới cùng:

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

và tất nhiên

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

cái này quá đơn giản phải không? tui bỏ lỡ điều gì vậy? Cho đến nay nó vẫn hoạt động tốt với tôi, nhưng như tôi đã nói, tôi đã không đưa nó qua máy vắt ...


IMO, đây là giải pháp tốt nhất. Điều duy nhất tôi thay đổi là thời lượng được mã hóa cứng của bạn thành[aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
Andy

Nó rất đơn giản. Nhưng một vấn đề tôi thấy là nó sẽ không làm thay đổi contentInsetgiới hạn và thay đổi mạnh mẽ các giới hạn cuộn.
Geek

Điều này làm việc tốt nhất cho tôi, tuy nhiên, một vài vấn đề. 1) Tôi không biết bạn có thể lấy "currentField.indexPath" ở đâu, vì vậy tôi phải lưu indexPath.row làm thẻ của trường và tạo indexPath sau. 2) Không hoạt động cho các hàng ở đầu bảng, nó sẽ cuộn chúng ra khỏi màn hình. Phải thêm một số mã để chỉ cuộn nếu indexPath của currentField lớn hơn những gì có thể vừa trên màn hình. 3) đã phải sử dụng kbSize.Width (thay vì chiều cao) trên iPad nếu phong cảnh
Travis M.

xin lỗi, chúng tôi đã quá quen với mã của chúng tôi đến nỗi đôi khi chúng tôi quên mất, eh? currentField là trường văn bản hiện tại mà tôi đang làm việc và indexPath là một phần mở rộng mà tôi đã thêm vào lớp chỉ cần thêm NSIndexPath để tôi biết đây là ô nào.
mickm

Đây là cách để đi, không di chuyển các khung xung quanh chỉ sửa đổi các thuộc tính bảng.
Nextorlg

35

Tôi nghĩ rằng tôi đã đưa ra giải pháp để phù hợp với hành vi của các ứng dụng của Apple.

Đầu tiên, trong viewWillAppear của bạn: đăng ký thông báo bàn phím, để bạn biết khi nào bàn phím sẽ hiển thị và ẩn, và hệ thống sẽ cho bạn biết kích thước của bàn phím, nhưng đừng quên hủy đăng ký trong viewWillDisappear của bạn :.

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

Thực hiện các phương thức tương tự như bên dưới để bạn điều chỉnh kích thước của bảngView để khớp với vùng hiển thị sau khi bàn phím hiển thị. Ở đây tôi đang theo dõi trạng thái của bàn phím một cách riêng biệt để tôi có thể chọn khi nào nên đặt bảng Xem lại toàn bộ chiều cao, vì bạn nhận được các thông báo này trên mỗi thay đổi trường. Đừng quên triển khai bàn phímWillHide: và chọn một nơi thích hợp để sửa kích thước bảng Xem của bạn.

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

Bây giờ đây là bit cuộn, trước tiên chúng ta sẽ tìm ra một vài kích thước, sau đó chúng ta sẽ thấy chúng ta đang ở đâu trong vùng hiển thị và đặt chỉnh lưu mà chúng ta muốn cuộn thành nửa chế độ xem ở trên hoặc dưới giữa trường văn bản trên đó là trong chế độ xem. Trong trường hợp này, chúng tôi có một mảng UITextFields và một enum theo dõi chúng, do đó, nhân số hàng với số hàng cho chúng ta độ lệch thực của khung trong chế độ xem bên ngoài này.

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

Điều này dường như làm việc khá độc đáo.


Giải pháp tốt đẹp. Cảm ơn đã đăng nó.
Alex Reynold

2
UIKeyboardBoundsUserInfoKeykhông dùng nữa kể từ iOS 3.2. Xem giải pháp của tôi bên dưới hoạt động trên tất cả các bản phát hành iOS hiện tại ≥ 3.0. / @ iPhoneDev
Ortwin Gentz

Điều này phức tạp hơn mức cần thiết. Câu trả lời của @ user91083 rất đơn giản và hiệu quả.
Richard Brightwell

1
Có một vấn đề nhỏ trong giải pháp này. bàn phímWillShow được gọi là SAU
văn bảnFieldDidBegin Chỉnh sửa

35

Nếu bạn có thể sử dụng UITableViewController, bạn sẽ có được chức năng miễn phí. Tuy nhiên, đôi khi, đây không phải là một tùy chọn, đặc biệt nếu bạn cần nhiều chế độ xem không chỉ UITableView.

Một số giải pháp được trình bày ở đây không hoạt động trên iOS 4, một số giải pháp không hoạt động trên iPad hoặc ở chế độ ngang, một số giải pháp không hoạt động đối với bàn phím Bluetooth (nơi chúng tôi không muốn cuộn), một số không hoạt động khi chuyển đổi giữa nhiều trường văn bản. Vì vậy, nếu bạn chọn bất kỳ giải pháp, hãy chắc chắn để kiểm tra các trường hợp này. Đây là giải pháp chúng tôi sử dụng được sử dụng trong InAppSinstallKit :

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

Đây là mã đầy đủ của lớp trong InAppSinstallKit. Để kiểm tra nó, hãy sử dụng ngăn con "Danh sách đầy đủ" nơi bạn có thể kiểm tra các tình huống được đề cập ở trên.


Tôi không biết có hữu ích khi sử dụng chuỗi thay vì hằng không, bởi vì nếu Apple nảy ra ý tưởng thay đổi Chuỗi bên trong vì một số lý do, giải pháp của bạn không còn hoạt động nữa. Tương tự như vậy, bạn đã không nhận được cảnh báo khi nó bị phản đối. Tôi nghĩ

@iPortable: nó không lý tưởng, tôi biết. Bạn có thể đề xuất một giải pháp tốt hơn chạy trên tất cả các phiên bản ≥3.0 không?
Ortwin Gentz

1
Hoạt động như sự quyến rũ, nhưng không dành cho UIInterfaceOrientationPortaboutUpsideDown. Sau đó, việc tính toán giảm chiều cao cũng phải được đặt ngược lại: CGFloat giảmHeight = keyboardRect.size.height - (CGRectGetMinY (viewRectAbsolute) - CGRectGetMinY (windowRect));
Klaas

Điều này có những trục trặc hình ảnh rất đáng chú ý trên iPad và Trình mô phỏng (4.3). Quá đáng chú ý để sử dụng. :(
Bob Spryn

Tôi thích rằng giải pháp này sẽ chiếm một thanh công cụ ở dưới cùng của màn hình.
pdemird

24

Giải pháp đơn giản nhất cho 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
    }

Hoạt động hoàn hảo, tính toán tối thiểu cần thiết. Tôi đã thêm một số mã khôi phục lại bảng chèn để hoàn thành câu trả lời này.
Vitalii

Giải pháp tốt nhất cảm ơn. Tôi đã đăng phiên bản Swift 3 tại đây: stackoverflow.com/a/41040630/1064438
squall2022

Giải pháp siêu hoàn hảo từng thấy, tôi đã thử người khác nhưng có một số vấn đề. Giải pháp của bạn hoạt động hoàn hảo trên ios 10.2.
Wangdu Lin

8

Tôi hy vọng các bạn đã có một giải pháp đọc tất cả những người. Nhưng tôi tìm thấy giải pháp của mình như sau. Tôi hy vọng rằng bạn đã có một tế bào với UITextField. Vì vậy, khi chuẩn bị chỉ cần giữ chỉ mục hàng vào thẻ của trường văn bản.

cell.textField.tag = IndexPath.row;

Tạo một activeTextFieldví dụ UITextFieldvới phạm vi toàn cầu như dưới đây:

@interface EditViewController (){

    UITextField *activeTextField;

}

Vì vậy, bây giờ bạn chỉ cần sao chép dán mã của tôi vào cuối. Và cũng đừng quên thêmUITextFieldDelegate

#pragma mark - TextField Delegation

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

    activeTextField = textField;

    return YES;
}

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

    activeTextField = nil;

}

Bàn phím đăng ký 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];



}

Bàn phím tay cầm Notifications:

Được gọi khi UIKeyboardDidShowNotificationđược gửi.

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

}

Được gọi khi UIKeyboardWillHideNotificationđược gửi

- (void)keyboardWillBeHidden:(NSNotification*)aNotification

{

    UIEdgeInsets contentInsets = UIEdgeInsetsZero;

    [self.tableView setContentInset:contentInsets];

    [self.tableView setScrollIndicatorInsets:contentInsets];

}

Bây giờ một điều còn lại, Gọi registerForKeyboardNotificationsphương thức vào ViewDidLoadphương thức như sau:

- (void)viewDidLoad {

    [super viewDidLoad];

    // Registering keyboard notification

    [self registerForKeyboardNotifications];

    // Your codes here...

}

Bạn đã hoàn thành, hy vọng ý chí của bạn textFieldskhông còn bị ẩn bởi bàn phím.


6

Kết hợp và điền vào chỗ trống từ một số câu trả lời (cụ thể là Ortwin Gentz, người dùng 98013) và một bài đăng khác, điều này sẽ hoạt động tốt cho SDK 4.3 trên iPad ở chế độ Chân dung hoặc Phong cảnh:

@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

Tôi đã sử dụng mã này trong iOS 4.x rất tốt, nhưng trong iOS5, nó gặp sự cố trong scrollToOldPocation vì _top mostRowB BeforePalWasShown đã được giải phóng tại thời điểm đó. Không chắc giải pháp là gì. Có lẽ hãy nhớ chỉ số thay vì đối tượng.
Thomas Tempelmann

5

Nếu bạn sử dụng một uitableview để đặt các trường văn bản của bạn ( từ Jeff Lamarche ), bạn có thể chỉ cần cuộn chế độ xem bảng bằng phương thức ủy nhiệm như vậy.

(Lưu ý: các trường văn bản của tôi được lưu trữ trong một mảng có cùng chỉ mục với hàng trong bảng xem)

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

Bạn không cập nhật khung TableView. Sau đó, scrollBars và hành vi cuộn bị sai khi bàn phím được hiển thị. Xem giải pháp của tôi.
Ortwin Gentz

5

Thông báo bàn phím hoạt động, nhưng mã mẫu của Apple cho rằng chế độ xem cuộn là chế độ xem gốc của cửa sổ. Đây thường không phải là trường hợp. Bạn phải bù cho các thanh tab, v.v., để có được độ lệch phù hợp.

Nó dễ dàng hơn âm thanh. Đây là mã tôi sử dụng trong UITableViewControll. Nó có hai biến đối tượng, hiddenRect và 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;
}

UIKeyboardCenterEndUserInfoKeyUIKeyboardBoundsUserInfoKeykhông được dùng nữa kể từ iOS 3.2. Xem giải pháp của tôi bên dưới hoạt động trên tất cả các bản phát hành iOS hiện tại ≥ 3.0.
Ortwin Gentz

5

Nếu bạn sử dụng Three20, sau đó sử dụng autoresizesForKeyboardtài sản. Chỉ cần đặt trong -initWithNibName:bundlephương thức của trình điều khiển xem của bạn

self.autoresizesForKeyboard = YES

Điều này quan tâm đến:

  1. Nghe thông báo bàn phím và điều chỉnh khung của chế độ xem bảng
  2. Cuộn đến phần trả lời đầu tiên

Ngay và luôn.


Three20 ở đây là gì? Bạn có thể chỉ định điều đó?
Trung tâm thương mại Mubin

5

Cách tiếp cận của tôi:

Tôi phân lớp UITextField đầu tiên và thêm thuộc tính indexPath. Trong cellFor ... Phương thức i bàn giao thuộc tính indexPath.

Sau đó, tôi thêm mã sau đây:

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

đến văn bảnFieldShould / WillBegin ... vv.

Khi Bàn phím biến mất, bạn phải đảo ngược nó bằng:

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

4

Một giải pháp nhiều dòng hơn. Nó trượt vào các phương thức ủy nhiệm của UITextField, do đó nó không yêu cầu thông báo w / UIKeyboard lộn xộn.

Ghi chú thực hiện:

kSinstallRowHeight - chiều cao của UITableViewCell.

offsetTarget và offsetThrưỡng được loại bỏ khỏi kSinstallRowHeight. Nếu bạn sử dụng chiều cao hàng khác nhau, hãy đặt các giá trị đó thành thuộc tính y của điểm. [alt: tính toán bù hàng theo cách khác.]

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

Sử dụng UITextField's delegatephương pháp:

Nhanh

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
}

Mục tiêu-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;
}

Xin chào, tôi đang gặp sự cố khiến điều này hoạt động trên Swift. UITextField của tôi được kết nối với UITableViewCell. Nếu tôi triển khai mã này bên trong UIViewControll của mình, tôi không có quyền truy cập vào UITextFields. Có ý kiến ​​gì không?
Vetuka

4

Giải pháp hoàn chỉnh Swift 4.2

Tôi đã tạo GIST với bộ giao thức đơn giản hóa công việc bằng cách thêm không gian bổ sung khi bàn phím được hiển thị, ẩn hoặc thay đổi.

Các tính năng :

  • Hoạt động chính xác với thay đổi khung bàn phím (ví dụ: thay đổi chiều cao bàn phím như emojii → bàn phím bình thường).
  • Hỗ trợ TabBar & ToolBar cho ví dụ UITableView (tại các ví dụ khác bạn nhận được các phần tử không chính xác).
  • Thời lượng hoạt hình động (không mã hóa cứng).
  • Cách tiếp cận hướng giao thức có thể dễ dàng sửa đổi cho các mục đích của bạn.

Sử dụng

Ví dụ sử dụng cơ bản trong trình điều khiển xem có chứa một số chế độ xem cuộn (tất nhiên chế độ xem bảng cũng được hỗ trợ).

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

Core: khung thay đổi người quan sát

Giao thức KeyboardChangeFrameObserversẽ kích hoạt sự kiện mỗi lần thay đổi khung bàn phím (bao gồm hiển thị, ẩn, thay đổi khung).

  1. Gọi addKeyboardFrameChangesObserver()tại viewWillAppear()hoặc phương pháp tương tự.
  2. Gọi removeKeyboardFrameChangesObserver()tại viewWillDisappear()hoặc phương pháp tương tự.

Thực hiện: xem cuộn

ModifableInsetsOnKeyboardFrameChangesgiao thức thêm UIScrollViewhỗ trợ cho giao thức cốt lõi. Nó thay đổi phần trong của chế độ xem cuộn khi khung bàn phím bị thay đổi.

Lớp của bạn cần thiết lập chế độ xem cuộn, phần trong của một người sẽ được tăng / giảm khi thay đổi khung bàn phím.

var scrollViewToModify: UIScrollView { get }

3

Vì bạn có các trường văn bản trong một bảng, cách tốt nhất thực sự là thay đổi kích thước bảng - bạn cần đặt bảngView.frame có chiều cao nhỏ hơn theo kích thước của bàn phím (tôi nghĩ khoảng 165 pixel) và sau đó mở rộng lại khi bàn phím bị loại bỏ.

Bạn cũng có thể tùy ý vô hiệu hóa tương tác người dùng cho bảngView tại thời điểm đó, nếu bạn không muốn người dùng cuộn.


Tôi thứ hai này và đăng ký UIKeyboardWillShowNotification để tìm kích thước của bàn phím một cách linh hoạt.
benzado

Số được trả về bởi đối tượng thông báo không hoạt động mặc dù. Hoặc ít nhất là nó không có trong 2.2, con số được trả về là không chính xác và tôi đã phải mã hóa giá trị 165 để điều chỉnh độ cao một cách chính xác (nó bị giảm từ năm đến mười pixel)
Kendall Helmstetter Gelner

2

Điều này hoạt động hoàn hảo, và trên iPad cũng vậy.

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

Tại sao bạn lại gặp khó khăn và sử dụng các trường hợp đặc biệt cho từng trường văn bản? ID từng trường văn bản từ NS IndexPath của ô và thay đổi câu lệnh if khó chịu đó thành 2 dòng mã. Bạn thực sự muốn có một cuộc gọi cellForRowAtIndexPath và sau đó lấy textField từ ô.
Alex Zavatone

Trên thực tế, xem xét tình huống này cực kỳ tồi tệ như thế nào trên iOS, tôi nghĩ sẽ ổn khi viết mã "hoàn toàn không có căn cứ, theo nghĩa đen" cho tình huống này.
Fattie

Xem xét câu trả lời này đã được đưa ra hơn 6 năm trước.
WrightsCS

2

Tôi đã thử gần như cùng một cách tiếp cận và đưa ra một mã đơn giản và nhỏ hơn cho cùng một mã. Tôi đã tạo một iTextView IBOutlet và được liên kết với UITextView trong IB.

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

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

    }

2

Vì vậy, sau nhiều giờ làm việc mệt mỏi khi cố gắng sử dụng các giải pháp hiện tại này (và hoàn toàn thất bại) cuối cùng tôi đã có được mọi thứ hoạt động tốt, và cập nhật chúng để sử dụng các khối hoạt hình mới. Câu trả lời của tôi hoàn toàn dựa trên câu trả lời của Ortwin ở trên .

Vì vậy, vì lý do gì mà đoạn mã trên không hoạt động với tôi. Thiết lập của tôi có vẻ khá giống với những người khác, nhưng có lẽ vì tôi đang ở trên iPad hoặc 4.3 ... không biết. Nó đang làm một số phép toán kỳ quặc và bắn cái bàn của tôi ra khỏi màn hình.

Xem kết quả cuối cùng của giải pháp của tôi: http://screencast.com/t/hjBCuRrPC (Vui lòng bỏ qua ảnh. :-P)

Vì vậy, tôi đã đi theo ý chính của những gì Ortwin đang làm, nhưng đã thay đổi cách nó đang thực hiện một số phép toán để thêm nguồn gốc.y & size.eight của chế độ xem bảng của tôi với chiều cao của bàn phím. Khi tôi trừ đi chiều cao của cửa sổ từ kết quả đó, nó sẽ cho tôi biết tôi đã đi bao nhiêu giao lộ. Nếu nó lớn hơn 0 (còn có một số trùng lặp) tôi thực hiện hoạt ảnh về chiều cao khung hình.

Ngoài ra, có một số vấn đề vẽ lại đã được giải quyết bằng 1) Chờ đợi để cuộn đến ô cho đến khi hoạt ảnh được hoàn thành và 2) sử dụng tùy chọn UIViewAnimationOptionBeginFromCienState khi ẩn bàn phím.

Một vài điều cần lưu ý.

  • _top mostRowB BeforePalWasShown & _origenFrame là các biến đối tượng được khai báo trong tiêu đề.
  • self.guestEntryTableView là bảngView của tôi (Tôi đang ở trong một tệp bên ngoài)
  • IASKCGRectSwap là phương pháp của Ortwin để lật tọa độ của khung
  • Tôi chỉ cập nhật chiều cao của bảngView nếu ít nhất 50px của nó sẽ hiển thị
  • Vì tôi không ở trong UIViewContoder nên tôi không có self.view, vì vậy tôi chỉ trả bảngView về khung ban đầu của nó

Một lần nữa, tôi sẽ không nhận được câu trả lời này nếu tôi Ortwin không cung cấp mấu chốt của nó. Đây là mã:

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

Đã thêm chức năng FixOriginRotation của tôi để sửa hệ thống tọa độ của chế độ xem trước khi bạn cập nhật khung của nó, v.v ... Tôi nghĩ rằng đây là một phần lý do tại sao lúc đầu tôi gặp rắc rối. Không biết hệ thống tọa độ cửa sổ iOS được xoay với thiết bị!
Bob Spryn

2

Soluton này hoạt động với tôi, xin vui lòng lưu ý dòng

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

Bạn có thể thay đổi giá trị 160 để phù hợp với nó hoạt động với bạn

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

Chủ đề thảo luận rất thú vị, tôi cũng gặp phải vấn đề tương tự có thể tồi tệ hơn bởi vì

  1. Tôi đã sử dụng một ô tùy chỉnh và trường văn bản nằm trong đó.
  2. Tôi đã phải sử dụng UIViewControll để đáp ứng các yêu cầu của mình để không thể tận dụng UITableViewControll.
  3. Tôi đã có bộ lọc / sắp xếp tiêu chí trong ô của bảng, tức là các ô của bạn tiếp tục thay đổi và theo dõi đường dẫn và tất cả sẽ không giúp ích gì.

Vì vậy, đọc các bài ở đây và thực hiện phiên bản của tôi, giúp tôi trong việc thúc đẩy nội dung của tôi trong iPad trong bối cảnh chế độ. Đây là mã (đây không phải là bằng chứng ngu ngốc và tất cả, nhưng nó đã khắc phục vấn đề của tôi) Trước tiên, bạn cần phải có một đại biểu trong lớp ô tùy chỉnh của mình, khi bắt đầu chỉnh sửa, sẽ gửi trường văn bản tới trình điều khiển khung nhìn của bạn và đặt activefield = theTextField ở đó

// CHỈ THỰC HIỆN ĐỂ XỬ LÝ CHẾ ĐỘ CẢNH QUAN

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

    }


}

// Được gọi khi UIKeyboardWillHideNotification được gửi

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

Tôi đã tự mình giải quyết vấn đề như vậy sau khi tôi giới thiệu hàng loạt giải pháp được tìm thấy qua Google và Stack Overflow.

Trước tiên, vui lòng đảm bảo rằng bạn đã thiết lập IBOutlet của UIScrollView của mình, sau đó vui lòng xem kỹ Apple Doc: Quản lý bàn phím . Cuối cùng, nếu bạn có thể cuộn nền, nhưng bàn phím vẫn bao phủ Trường Văn bản, vui lòng xem đoạn mã này:

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

Sự khác biệt chính giữa sản phẩm này và Apple nằm ở điều kiện if. Tôi tin rằng tính toán của apple về khoảng cách cuộn và điều kiện xem trường văn bản được bao phủ bởi bàn phím có chính xác không, vì vậy tôi đã thực hiện sửa đổi của mình như trên.

Cho tôi biết nếu nó hoạt động


2

Một ví dụ trong Swift, sử dụng điểm chính xác của trường văn bản từ Get indexPath của UITextField trong UITableViewCell với 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

Một phương pháp dễ dàng khác (chỉ hoạt động với một phần)

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

Nếu UITableView của bạn được quản lý bởi một lớp con của UITableViewControll chứ không phải UITableView, và đại biểu trường văn bản là UITableViewCont kiểm soát, nó sẽ tự động quản lý tất cả các cuộn - tất cả các nhận xét khác này rất khó thực hiện trong thực tế.

Để biết ví dụ tốt, hãy xem dự án mã ví dụ apple: TaggedLocations.

Bạn có thể thấy rằng nó cuộn tự động, nhưng dường như không có bất kỳ mã nào thực hiện điều này. Dự án này cũng có các ô xem bảng tùy chỉnh, vì vậy nếu bạn xây dựng ứng dụng của mình với hướng dẫn đó, bạn sẽ nhận được kết quả mong muốn.


1

Đây là cách tôi thực hiện công việc này, đó là sự pha trộn giữa câu trả lời của Sam Ho và Marcel W, và một số sửa lỗi của riêng tôi được thực hiện theo mã ngu ngốc của tôi. Tôi đã sử dụng một UITableViewControll. Bảng bây giờ thay đổi kích thước chính xác khi bàn phím được hiển thị.

1) Trong viewDidLoadtôi đã thêm:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2) Tôi đã quên gọi các supertương đương trong viewWillAppearawakeFromNib. Tôi đã thêm những thứ này trở lại.


1

UITableViewControllerthực sự Scrolling tự động. Sự khác biệt so với việc sử dụng a UIViewControllerlà, bạn phải tạo Navbar-Buttonitems theo chương trình bằng cách sử dụngNavigationController , khi sử dụng a TableViewController.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.