Làm cách nào tôi có thể làm cho UITextField di chuyển lên khi có bàn phím - khi bắt đầu chỉnh sửa?


1691

Với SDK iOS:

Tôi có một cái UIViewvới UITextFieldbàn phím. Tôi cần nó để có thể:

  1. Cho phép cuộn các nội dung của UIScrollViewđể xem các trường văn bản khác sau khi bàn phím được đưa lên

  2. Tự động "nhảy" (bằng cách cuộn lên) hoặc rút ngắn

Tôi biết rằng tôi cần a UIScrollView. Tôi đã thử thay đổi lớp của tôi UIViewthành một UIScrollViewnhưng tôi vẫn không thể cuộn các hộp văn bản lên hoặc xuống.

Tôi có cần cả a UIViewvà a UIScrollViewkhông? Có một đi vào bên trong khác?

Những gì cần phải được thực hiện để tự động cuộn đến trường văn bản hoạt động?

Lý tưởng nhất là càng nhiều thiết lập các thành phần càng tốt sẽ được thực hiện trong Interface Builder. Tôi chỉ muốn viết mã cho những gì cần nó.

Lưu ý: UIView(hoặc UIScrollView) mà tôi đang làm việc được đưa lên bởi một thanh tab ( UITabBar), cần hoạt động như bình thường.


Chỉnh sửa: Tôi đang thêm thanh cuộn chỉ khi bàn phím xuất hiện. Mặc dù không cần thiết, tôi cảm thấy như nó cung cấp một giao diện tốt hơn bởi vì sau đó người dùng có thể cuộn và thay đổi hộp văn bản chẳng hạn.

Tôi đã làm cho nó hoạt động khi tôi thay đổi kích thước khung hình UIScrollViewkhi bàn phím lên xuống. Tôi chỉ đơn giản là sử dụng:

-(void)textFieldDidBeginEditing:(UITextField *)textField { 
    //Keyboard becomes visible
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
                     scrollView.frame.origin.y, 
scrollView.frame.size.width,
scrollView.frame.size.height - 215 + 50);   //resize
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
   //keyboard will hide
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
       scrollView.frame.origin.y, 
     scrollView.frame.size.width,
      scrollView.frame.size.height + 215 - 50); //resize
}

Tuy nhiên, điều này không tự động "di chuyển lên" hoặc tập trung vào các trường văn bản thấp hơn trong vùng hiển thị, đó là điều tôi thực sự muốn.


6
Kiểm tra này. Không có rắc rối cho bạn. TPPalAvoiding
Aruna

21
Nó được tài liệu bởi Apple, tôi nghĩ đó là cách tốt nhất: developer.apple.com/l
Library / ios / # document /StringsTextFonts / trộm

58
Sử dụng mã này. Bạn chỉ cần 1 dòng trong tệp appdelegate.m và nó hoạt động. github.com/hackiftekhar/IQPalManager
Pradeep Găngal

9
Cách tốt nhất mà tôi tìm thấy cho đến nay là TPPalAvoiding
Mongi Zaidi

2
Một cách khác là thêm các trường văn bản nội dung như vậy và tất cả trong TableViewControll và để cho chế độ xem bảng xử lý việc này.
Vicky Dhas

Câu trả lời:


1036
  1. Bạn sẽ chỉ cần một ScrollViewnếu nội dung bạn có bây giờ không vừa với màn hình iPhone. (Nếu bạn thêm phần giám ScrollViewsát của các thành phần chỉ để TextFieldcuộn lên khi bàn phím xuất hiện thì không cần thiết.)

  2. Cách tiêu chuẩn để ngăn TextFields bị che bởi bàn phím là di chuyển chế độ xem lên / xuống bất cứ khi nào bàn phím được hiển thị.

Đây là một số mã mẫu:

#define kOFFSET_FOR_KEYBOARD 80.0

-(void)keyboardWillShow {
    // Animate the current view out of the way
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)keyboardWillHide {
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:mailTf])
    {
        //move the main view, so that the keyboard does not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [self setViewMovedUp:YES];
        }
    }
}

//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3]; // if you want to slide up the view

    CGRect rect = self.view.frame;
    if (movedUp)
    {
        // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
        // 2. increase the size of the view so that the area behind the keyboard is covered up.
        rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
    }
    else
    {
        // revert back to the normal state.
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
    }
    self.view.frame = rect;

    [UIView commitAnimations];
}


- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShow)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

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

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}

3
_TextField là gì? Tôi đã sao chép nó vào mã của mình, nó nói _textField không được khai báo.
Ca cao Dev

Đó là trường mà bạn sử dụng để nói "khi người dùng đang chỉnh sửa ở đây, chế độ xem sẽ trượt lên" hoặc một cái gì đó ... Tuy nhiên, bạn có thể xóa nó nếu, nếu bạn có nhiều trường hơn.
patrick

không phải là đập để gọi - (void) setViewMovedUp: (BOOL) đã di chuyểnUp trong các sự kiện keyBoardWillSHow và KeyBoardWillHide !!
Abduliam Rehmanius

4
Không đặc biệt hữu ích nếu bạn đang hỗ trợ xoay các chế độ xem chính.
FractalDoctor

2
Để thực hiện công việc này, tôi đã phải bình luận ra textFieldDidBeginEditingphần này.
avance

445

Tôi cũng gặp nhiều vấn đề với UIScrollViewviệc soạn thảo nhiều bản UITextFields, trong đó, một hoặc nhiều trong số chúng sẽ bị che khuất bởi bàn phím khi chúng được chỉnh sửa.

Dưới đây là một số điều cần xem xét nếu bạn UIScrollViewkhông cuộn đúng cách.

1) Đảm bảo rằng UIScrollViewkích thước nội dung của bạn lớn hơn kích thước khung hình. Cách hiểu UIScrollViewsUIScrollViewgiống như một cửa sổ xem nội dung được xác định trong contentSize. Vì vậy, khi UIScrollviewđể cuộn ở bất cứ đâu, kích thước contentSize phải lớn hơn UIScrollView. Khác, không cần cuộn vì mọi thứ được xác định trong contentSize đều hiển thị. BTW, nội dung mặc địnhSize = CGSizeZero.

2) Bây giờ bạn đã hiểu rằng đó UIScrollViewthực sự là một cửa sổ vào "nội dung" của bạn, cách để đảm bảo rằng bàn phím không che khuất UIScrollView's"cửa sổ" đang xem của bạn sẽ thay đổi kích thước UIScrollViewđể khi có bàn phím, bạn có UIScrollViewcửa sổ có kích thước chỉ bằng UIScrollViewframe.size.height ban đầu trừ đi chiều cao của bàn phím. Điều này sẽ đảm bảo rằng cửa sổ của bạn chỉ là khu vực nhỏ có thể xem được.

3) Đây là một nhược điểm: Khi tôi lần đầu tiên thực hiện điều này, tôi nghĩ rằng tôi sẽ phải lấy trường CGRectvăn bản đã chỉnh sửa và gọi UIScrollView'sphương thức scrollRecToVisible. Tôi thực hiện UITextFieldDelegatephương thức textFieldDidBeginEditingvới lời gọi đến scrollRecToVisiblephương thức. Điều này thực sự đã làm việc với một tác dụng phụ lạ mà di chuyển sẽ chụp các UITextFieldvị trí. Trong thời gian dài nhất tôi không thể hiểu nó là gì. Sau đó, tôi nhận xét textFieldDidBeginEditingphương pháp Delegate và tất cả đều hoạt động !! (???). Khi nó bật ra, tôi tin rằng UIScrollViewthực sự mang ngầm đang được chỉnh sửa UITextFieldvào cửa sổ có thể xem được. Việc tôi thực hiện UITextFieldDelegatephương pháp và cuộc gọi tiếp theo scrollRecToVisiblelà không cần thiết và là nguyên nhân gây ra tác dụng phụ kỳ lạ.

Vì vậy, đây là các bước để di chuyển đúng cách của bạn UITextFieldtrong một UIScrollViewvào vị trí khi xuất hiện bàn phím.

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardWillShowNotification 
                                               object:self.view.window];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillHide:) 
                                                 name:UIKeyboardWillHideNotification 
                                               object:self.view.window];
    keyboardIsShown = NO;
    //make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
    CGSize scrollContentSize = CGSizeMake(320, 345);
    self.scrollView.contentSize = scrollContentSize;
}

- (void)keyboardWillHide:(NSNotification *)n
{
    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;


    // resize the scrollview
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height += (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];

    keyboardIsShown = NO;
}

- (void)keyboardWillShow:(NSNotification *)n
{
    // This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown.  This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`.  If we were to resize the `UIScrollView` again, it would be disastrous.  NOTE: The keyboard notification will fire even when the keyboard is already shown.
    if (keyboardIsShown) {
        return;
    }

    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;

    // resize the noteView
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];
    keyboardIsShown = YES;
}
  1. Đăng ký thông báo bàn phím tại viewDidLoad
  2. Hủy đăng ký cho các ứng dụng bàn phím tại viewDidUnload
  3. Đảm bảo rằng giá trị contentSizeđược đặt và lớn hơn UIScrollViewtạiviewDidLoad
  4. Shrink những UIScrollViewkhi bàn phím hiện diện
  5. Phục hồi trở lại những UIScrollViewkhi bàn phím sẽ biến mất.
  6. Sử dụng một Ivar để phát hiện nếu bàn phím đã được hiển thị trên màn hình kể từ khi thông báo bàn phím được gửi mỗi lần một UITextFieldlà tab ngay cả khi bàn phím đã có mặt để tránh bị thu hẹp sự UIScrollViewkhi nó đã bị thu hẹp

Một điều cần lưu ý là ý UIKeyboardWillShowNotificationchí sẽ kích hoạt ngay cả khi bàn phím đã có trên màn hình khi bạn tab trên một cái khác UITextField. Tôi đã xử lý vấn đề này bằng cách sử dụng một chiếc ngà để tránh thay đổi kích thước UIScrollViewkhi bàn phím đã có trên màn hình. Vô tình thay đổi kích thước UIScrollViewkhi bàn phím đã có sẵn sẽ là thảm họa!

Hy vọng mã này tiết kiệm một số bạn rất nhiều đau đầu.


3
Tuyệt vời, nhưng hai vấn đề: 1. UIKeyboardBoundsUserInfoKeykhông được chấp nhận. 2. keyboardSize nằm trong "tọa độ màn hình", do đó, tính toán khung nhìn của bạn sẽ thất bại nếu khung được xoay hoặc thu nhỏ.
Martin Wickman

21
@Martin Wickman - Sử dụng CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;thay vì không dùng nữaUIKeyboardBoundsUserInfoKey
s'dad

1
HI, tôi cũng làm như vậy, nhưng chế độ xem văn bản chỉ di chuyển lên khi người dùng bắt đầu nhập? Đó có phải là hành vi dự kiến ​​hoặc tôi đang thiếu một cái gì đó?

3
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].sizenên [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size. Giải pháp tuyệt vời mặc dù!
j7nn7k

1
Tôi thích giải pháp của bạn nhưng tôi nghĩ tôi có thể làm cho nó đơn giản hơn nữa: đừng bận tâm với công cụ Thông báo quan sát; thay vào đó, hãy gọi các thói quen hoạt hình phù hợp bên trong các phương thức ủy nhiệm thích hợp - đối với UITextView, chúng là textViewDidBeginEditing và textViewDidEndEditing.
AlexChaffee

270

Thực sự tốt nhất chỉ là sử dụng triển khai của Apple, như được cung cấp trong các tài liệu . Tuy nhiên, mã họ cung cấp bị lỗi. Thay thế phần được tìm thấy keyboardWasShown:ngay bên dưới các ý kiến ​​như sau:

NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);

CGPoint origin =  activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
    CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
    [backScrollView setContentOffset:scrollPoint animated:YES];
}

Các vấn đề với mã của Apple là: (1) Họ luôn tính toán nếu điểm nằm trong khung của chế độ xem, nhưng đó là một điểm ScrollView, vì vậy nó có thể đã bị cuộn và bạn cần tính đến phần bù đó:

origin.y -= scrollView.contentOffset.y

(2) Họ thay đổi nội dung Gói theo chiều cao của bàn phím, nhưng chúng tôi muốn ngược lại (chúng tôi muốn thay đổi contentOffsettheo chiều cao hiển thị trên màn hình, chứ không phải những gì không phải):

activeField.frame.origin.y-(aRect.size.height)

1
Trong trường hợp chế độ xem cuộn không lấp đầy màn hình, aRect phải được đặt thành khung của chế độ xem cuộn
mblackwell8

2
Bạn không nên muốn CGPoint origin = activeField.frame.origin + activeField.frame.size.height ?, Vì bạn muốn hiển thị toàn bộ trường văn bản và nếu nó chỉ hiển thị một số pixel thì mã sẽ không được nhập tình trạng.
htafoya

1
Giải pháp này không hoạt động theo hướng Cảnh - trường văn bản bay khỏi đỉnh của cổng xem. iPad với iOS 7.1.
Andrew

4
Để hỗ trợ iOS 8 tốt hơn, tôi khuyên bạn nên sử dụng UIKeyboardFrameEndUserInfoKeythay vì UIKeyboardFrameBeginUserInfoKeykhi lấy kích thước bàn phím, vì điều này sẽ chọn những thứ như thay đổi bàn phím tùy chỉnh và bật / tắt văn bản dự đoán.
Kết thúc

1
@Egor: Khắc phục của bạn làm cho nó hoạt động tốt hơn - nhưng dòng cuối cùng phải ngược lại:self.scrollView.contentOffset = self.currentSVoffset;
Morten Holmgaard

244

Trong textFieldDidBeginEdittingvà trong textFieldDidEndEditingchức năng gọi [self animateTextField:textField up:YES]như vậy:

-(void)textFieldDidBeginEditing:(UITextField *)textField 
{ 
    [self animateTextField:textField up:YES]; 
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField:textField up:NO];
}

-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
    const int movementDistance = -130; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? movementDistance : -movementDistance); 

    [UIView beginAnimations: @"animateTextField" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

Tôi hy vọng mã này sẽ giúp bạn.

Trong Swift 2

func animateTextField(textField: UITextField, up: Bool) 
{
     let movementDistance:CGFloat = -130
     let movementDuration: Double = 0.3

     var movement:CGFloat = 0
     if up 
     {
         movement = movementDistance
     }
     else 
     {
         movement = -movementDistance
     }
     UIView.beginAnimations("animateTextField", context: nil)
     UIView.setAnimationBeginsFromCurrentState(true)
     UIView.setAnimationDuration(movementDuration)
     self.view.frame = CGRectOffset(self.view.frame, 0, movement)
     UIView.commitAnimations()
}


func textFieldDidBeginEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:true)
}

func textFieldDidEndEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:false)
}

Chuyển 3

 func animateTextField(textField: UITextField, up: Bool)
    {
        let movementDistance:CGFloat = -130
        let movementDuration: Double = 0.3

        var movement:CGFloat = 0
        if up
        {
            movement = movementDistance
        }
        else
        {
            movement = -movementDistance
        }
        UIView.beginAnimations("animateTextField", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration)
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }


    func textFieldDidBeginEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:true)
    }

    func textFieldDidEndEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:false)
    }

1
tại sao không sử dụng [UIView animateWithDuration: animations:^{ }];?
Andre Cytryn

2
điều này hoạt động tốt, mặc dù const int MovementDistance = -130; // chỉnh sửa khi cần thiết để được thay đổi thành linh hoạt hơn
Búa

7
Cực kỳ đơn giản trên các triển khai nhỏ. Không có vấn đề xung quanh với ScrollViews và các vấn đề bố cục tự động mơ hồ.
James Perih

4
Erm ... bạn không sử dụng tham số textField. Tại sao sau đó có nó như là một tham số chức năng? Ngoài ra, bạn có thể sử dụng toán tử ternary trong Swift. Làm cho mã ít nói hơn.
stk

1
Nếu màu nền của Chế độ xem khác với màu đen, hãy đảm bảo bạn đặt màu của Cửa sổ khớp với chế độ xem của mình để người dùng không nhìn thấy phía sau nó. tức là self.window.backgroundColor = [UIColor whiteColor];
bvmobil xuất hiện lại vào

134

Chỉ cần sử dụng TextFields:

1a) Sử dụng Interface Builder: Chọn Tất cả TextFields => Chỉnh sửa => Nhúng vào => ScrollView

1b) TextField nhúng thủ công trong UIScrollView được gọi là scrollView

2 bộ UITextFieldDelegate

3) Đặt từng textField.delegate = self;(hoặc tạo kết nối trong Interface Builder)

4) Sao chép / Dán:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
    [scrollView setContentOffset:scrollPoint animated:YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    [scrollView setContentOffset:CGPointZero animated:YES];
}

8
Nhưng nó cũng di chuyển lên xem khi textFieldđã được nhìn thấy.
TheTiger

1
Cần đổi CGPointMake(0, textField.frame.origin.y);thànhCGPointMake(0, textField.frame.origin.y + scrollView.contentInset.top);
Fury

@Egor Ngay cả sau khi nhận xét của bạn, nó không hoạt động. Giống như "TheTiger" đã đề cập, nó di chuyển lên chế độ xem ngay cả sau khi hiển thị trường văn bản.
rak appdev

Thay đổi cho XCode 10: "Chọn tất cả TextFields => Trình chỉnh sửa => Nhúng vào => Chế độ xem cuộn"
tibalt

116

Đối với Giải pháp phổ quát , đây là cách tiếp cận của tôi để triển khai IQPalManager .

nhập mô tả hình ảnh ở đây

Bước 1: - Tôi Added thông báo toàn cầu về UITextField, UITextViewUIKeyboardtrong một lớp singleton. Tôi gọi nó là IQPalManager .

Bước 2: - Nếu tìm thấy UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationhoặc UITextViewTextDidBeginEditingNotificationthông báo, tôi cố gắng để có được topMostViewControllerdụ từ UIWindow.rootViewControllerhệ thống cấp bậc. Để phát hiện UITextField/ UITextViewbật nó đúng cách , topMostViewController.viewkhung của cần phải được điều chỉnh.

Bước 3: - Tôi đã tính khoảng cách di chuyển dự kiến topMostViewController.viewđối với phản hồi đầu tiên UITextField/ UITextView.

Bước 4: - Tôi di chuyển topMostViewController.view.framelên / xuống theo khoảng cách di chuyển dự kiến.

Bước 5: - Nếu tìm thấy UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotificationhoặc UITextViewTextDidEndEditingNotificationthông báo, tôi lại cố gắng để có được topMostViewControllerdụ từ UIWindow.rootViewControllerhệ thống cấp bậc.

Bước 6: - Tôi đã tính toán khoảng cách bị xáo trộn topMostViewController.viewcần được khôi phục về vị trí ban đầu.

Bước 7: - Tôi khôi phục topMostViewController.view.framexuống theo khoảng cách bị xáo trộn.

Bước 8: - Tôi đã khởi tạo cá thể lớp IQPalManager ngay lập tức khi tải ứng dụng, vì vậy mọi UITextField/ UITextViewtrong ứng dụng sẽ tự động điều chỉnh theo khoảng cách di chuyển dự kiến.

Đó là tất cả IQPalManager làm cho bạn mà KHÔNG CÓ MÃ SỐ MÃ nào !! chỉ cần kéo và thả tập tin nguồn liên quan đến dự án. IQPalManager cũng hỗ trợ Định hướng thiết bị , Quản lý UIToolbar tự động , KeybkeyboardDistanceFromTextField và nhiều hơn bạn nghĩ.


Thêm thư mục IQKeyBoardManagerSwift vào dự án của tôi và không hoạt động. Không thể kích hoạt vì nó không nhận ra trong AppDelegate ...
user3722523

2
cảm giác này giống như lừa đảo, giải pháp thực tế không được hiển thị mà thay vào đó chúng ta thấy một quảng cáo cho tài khoản GitHub của kẻ này.
Brian

101

Tôi đã đặt cùng một phổ quát, thả vào UIScrollView, UITableViewvà thậm chí cả UICollectionViewlớp con rằng sẽ chăm sóc của di chuyển tất cả các lĩnh vực văn bản bên trong nó ra khỏi con đường của bàn phím.

Khi bàn phím sắp xuất hiện, lớp con sẽ tìm thấy khung nhìn phụ sắp được chỉnh sửa và điều chỉnh khung và nội dung của nó để đảm bảo rằng chế độ xem đó hiển thị, với hình động để khớp với cửa sổ bật lên. Khi bàn phím biến mất, nó sẽ khôi phục kích thước trước đó.

Về cơ bản, nó sẽ hoạt động với bất kỳ thiết lập nào, UITableViewgiao diện dựa trên cơ sở hoặc giao diện bao gồm các chế độ xem được đặt thủ công.

Đây là giải pháp cho việc di chuyển các trường văn bản ra khỏi bàn phím


Đây là nó! Đây là giải pháp tốt nhất, hiệu quả nhất và hoàn hảo! Nó cũng xử lý xoay đúng cách để xem cuộn. Nếu xoay hãy chắc chắn để tự động theo chiều dọc nhưng không neo ở phía dưới. Tôi đã thêm một UITextView vào chế độ xem cuộn trong trường hợp của mình. Cảm ơn bó!
Christopher

Công việc rất tốt! Chắc chắn, tôi đang lười sử dụng giải pháp của bạn thay vì DIY, nhưng ông chủ của tôi hạnh phúc hơn, vì vậy, yeah! Ngay cả khi ai đó muốn tự làm, tôi thích cách tiếp cận lớp con của bạn, thay vì thêm mã vào mỗi bộ điều khiển. Tôi đã bị sốc khi iOS không làm điều này theo mặc định như Android đã làm - một lần nữa, tôi đang tìm thấy rất nhiều thứ thiếu trong iOS và MacOS :(
eselk

Có phải những thứ kỳ lạ như scrollview của tôi đều vừa vặn trong màn hình, vì vậy nó không thể cuộn được. Sau khi mở và đóng bàn phím, nội dung bây giờ lớn hơn (trông giống như một cái gì đó vô hình đã được thêm vào và không bị xóa ở cuối trang) và có thể được cuộn.
Almo

91

Dành cho lập trình viên Swift :

Điều này sẽ làm mọi thứ cho bạn, chỉ cần đặt những thứ này trong lớp trình điều khiển khung nhìn của bạn và triển khai trình UITextFieldDelegateđiều khiển khung nhìn của bạn và đặt đại biểu của textField thànhself

textField.delegate = self // Setting delegate of your UITextField to self

Thực hiện các phương thức gọi lại của đại biểu:

func textFieldDidBeginEditing(textField: UITextField) {
    animateViewMoving(true, moveValue: 100)
}

func textFieldDidEndEditing(textField: UITextField) {
    animateViewMoving(false, moveValue: 100)
}

// Lifting the view up
func animateViewMoving (up:Bool, moveValue :CGFloat){
    let movementDuration:NSTimeInterval = 0.3
    let movement:CGFloat = ( up ? -moveValue : moveValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration )
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}

Đối với Swift 4, 4.2, 5: Thay đổi

self.view.frame = CGRectOffset(self.view.frame, 0,  movement)

đến

self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)

Lưu ý cuối cùng về việc triển khai này: Nếu bạn đẩy một bộ điều khiển chế độ xem khác lên ngăn xếp trong khi bàn phím được hiển thị, điều này sẽ tạo ra lỗi trong đó chế độ xem được đưa trở lại khung trung tâm của nó nhưng bù lại bàn phím không được đặt lại. Ví dụ: bàn phím của bạn là bộ phản hồi đầu tiên cho nameField, nhưng sau đó bạn nhấn một nút đẩy Bộ điều khiển xem trợ giúp của bạn lên ngăn xếp của bạn. Để sửa lỗi bù, hãy đảm bảo gọi nameField.resignFirstResponder () trước khi rời khỏi bộ điều khiển xem, đảm bảo rằng phương thức ủy nhiệm textFieldDidEndEditing cũng được gọi. Tôi làm điều này trong phương thức viewWillDisappear.


3
SwiftLint không thích self.view.frame = CGRectOffset(self.view.frame, 0, movement)nên tôi đã đổi dòng đó thànhself.view.frame.offsetInPlace(dx: 0, dy: movement)
levibostian

2
Swift 4 thay đổi self.view.frame = CGRect Offerset (self.view.frame, 0, Movement) thành self.view.frame.offsetBy (dx: 0, dy: Movement)
Asinox

FYI, để làm việc này, bạn phải đặt. self.view.frame = self.view.frame.offsetBy (dx: 0, dy: Movement)
Josh Wolff

64

Hiện đã có rất nhiều câu trả lời, nhưng vẫn chưa có giải pháp nào ở trên có tất cả các công cụ định vị ưa thích cần thiết cho một hình ảnh động "hoàn hảo", không tương thích ngược và không nhấp nháy. (lỗi khi hoạt hình khung / giới hạn và nội dung cùng nhau, các hướng giao diện khác nhau, bàn phím tách iPad, ...)
Hãy để tôi chia sẻ giải pháp của tôi:
(giả sử bạn đã thiết lập UIKeyboardWill(Show|Hide)Notification)

// Called when UIKeyboardWillShowNotification is sent
- (void)keyboardWillShow:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = [notification userInfo];

    CGRect keyboardFrameInWindow;
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];

    // the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
    CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil];

    CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView);
    UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);

    // this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    _scrollView.contentInset = newContentInsets;
    _scrollView.scrollIndicatorInsets = newContentInsets;

    /*
     * Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element
     * that should be visible, e.g. a purchase button below an amount text field
     * it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
     */
    if (_focusedControl) {
        CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
        controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.

        CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y;
        CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;

        // this is the visible part of the scroll view that is not hidden by the keyboard
        CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;

        if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
            // scroll up until the control is in place
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);

            // make sure we don't set an impossible offset caused by the "nice visual offset"
            // if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
            newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight);

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        } else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) {
            // if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y = controlFrameInScrollView.origin.y;

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        }
    }

    [UIView commitAnimations];
}


// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillHide:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = notification.userInfo;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    // undo all that keyboardWillShow-magic
    // the scroll view will adjust its contentOffset apropriately
    _scrollView.contentInset = UIEdgeInsetsZero;
    _scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;

    [UIView commitAnimations];
}

Những cải tiến tuyệt vời của câu trả lời @Shiun. Nhưng sau khi hết bàn phím, chế độ xem không quay lại vị trí số 1. Đây vẫn là một công việc tuyệt vời :)
Lucien

2
Cảm ơn, đây là giải pháp tốt nhất cho tôi trong năm 2017. Lưu ý rằng bạn không cần phải tự mình theo dõi tập trungControl, bạn có thể xác định điều đó với UIApplication.shared.sendAction(...). Đây là phiên bản Swift 3 cho câu trả lời của bạn (trừ phần willHide), với phần được sendActiontriển khai: gist.github.com/xaphod/7aab1302004f6e933593a11ad8f5a72d
xaphod

@xaphod trong trường hợp của tôi, tôi cần tập trung nhiều điều khiển hơn - ví dụ: nút bên dưới trường nhập. nhưng vâng, mã đó đã được 4 năm tuổi và có thể được hưởng lợi từ các cải tiến.
Martin Ullrich

Đây có lẽ là giải pháp thích hợp. Thông báo bàn phím mang dữ liệu hoạt hình, các đoàn văn bản trường không biết về thời lượng hoạt hình, nó sẽ chỉ là một công việc đoán.
XY

62

Shiun nói "Khi nó bật ra, tôi tin rằng UIScrollView thực sự mang UITextField hiện đang được chỉnh sửa vào cửa sổ có thể xem được" Điều này có vẻ đúng với iOS 3.1.3, nhưng không phải là 3.2, 4.0 hoặc 4.1. Tôi đã phải thêm một cuộn rõ ràngRectToVisible để hiển thị UITextField trên iOS> = 3.2.


Đây không phải là UIScrollView cuộn ngầm UITextField đã chỉnh sửa vào chế độ xem, đó là UITextField gọi một [UITextField scrollTextFieldToVisibleIfNecessary]phương thức riêng mà lần lượt gọi [UIScrollView scrollRectToVisible]khi [UITextField becomeFirstResponder]được gọi. Xem github.com/leopatras/ios lòngfields_on_scrollview . Nếu các ràng buộc và bộ điều khiển xem được thiết lập đúng, thực sự không cần phải gọi scrollRectToVisiblemột cách rõ ràng (ít nhất là kể từ iOS 11).
Leo

48

Một điều cần xem xét là liệu bạn có muốn tự mình sử dụng hay không UITextField. Tôi chưa bắt gặp bất kỳ ứng dụng iPhone nào được thiết kế tốt thực sự sử dụng UITextFieldsbên ngoài UITableViewCells.

Nó sẽ là một số công việc bổ sung, nhưng tôi khuyên bạn nên thực hiện tất cả các chế độ xem nhập dữ liệu một chế độ xem bảng. Thêm một UITextViewvào của bạn UITableViewCells.


1
Một trong những ứng dụng của tôi cần cho phép người dùng thêm ghi chú dạng tự do - vì vậy, đôi khi sử dụng UITextField rất hữu ích.
Peter Johnson

1
Tôi đồng ý với phương pháp này. Không làm việc hoặc mã theo cách này. Ngay cả khi bạn cần một ghi chú biểu mẫu miễn phí, bạn vẫn có thể với một ô bảng
RJH

UITableViewthật đáng buồn là con đường duy nhất để đi Thông báo bàn phím dễ vỡ và đã thay đổi thêm giờ. Mã mẫu trên Stack Overflow: stackoverflow.com/a/32390936/218152
SwiftArchitect 9/2/2016

Câu trả lời này là một số năm năm lỗi thời. Giải pháp hiện đại duy nhất là một cái gì đó như thế này ... stackoverflow.com/a/41808338/294884
Fattie

47

Điều này liệu chi tiết một giải pháp cho vấn đề này. Nhìn vào mã nguồn trong phần 'Di chuyển nội dung được đặt dưới bàn phím'. Nó khá đơn giản.

EDIT: Nhận thấy có một trục trặc nhỏ trong ví dụ. Bạn có thể sẽ muốn lắng nghe UIKeyboardWillHideNotificationthay vì UIKeyboardDidHideNotification. Nếu không, chế độ xem cuộn phía sau bàn phím sẽ được cắt bớt trong suốt thời gian hoạt hình đóng bàn phím.


32

Giải pháp dễ nhất được tìm thấy

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}


- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = 80; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

Màn hình di chuyển lên ngay cả khi nó không ở phía dưới. tức là, nếu trường văn bản ở trên cùng, nó sẽ di chuyển ra khỏi màn hình. Làm thế nào để kiểm soát trường hợp đó?
MELWIN

@MELWIN Chỉ cần thêm sau dòng này: int movement = (up ? -movementDistance : movementDistance); if (textField.frame.origin.y < self.view.frame.size.height - keyboard.height) { movementDistance = 0 }Xin lưu ý rằng keyboardbiến đó là CGRect của bàn phím bật lên mà bạn nhận được bằng cách thực hiện:let keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
CapturedTree

31

Một ít sửa chữa hoạt động cho nhiều UITextFields

#pragma mark UIKeyboard handling

#define kMin 150

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
   if (currTextField) {
      [currTextField release];
   }
   currTextField = [sender retain];
   //move the main view, so that the keyboard does not hide it.
   if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) {
        [self setViewMovedUp:YES]; 
   }
}



//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
   [UIView beginAnimations:nil context:NULL];
   [UIView setAnimationDuration:0.3]; // if you want to slide up the view

   CGRect rect = self.view.frame;
   if (movedUp)
   {
      // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
      // 2. increase the size of the view so that the area behind the keyboard is covered up.
      rect.origin.y = kMin - currTextField.frame.origin.y ;
   }
   else
   {
      // revert back to the normal state.
      rect.origin.y = 0;
   }
   self.view.frame = rect;

   [UIView commitAnimations];
}


- (void)keyboardWillShow:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately

   if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin)
   {
      [self setViewMovedUp:YES];
   }
   else if (![currTextField isFirstResponder] && currTextField.frame.origin.y  + self.view.frame.origin.y < kMin)
   {
      [self setViewMovedUp:NO];
   }
}

- (void)keyboardWillHide:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately
   if (self.view.frame.origin.y < 0 ) {
      [self setViewMovedUp:NO];
   }

}


- (void)viewWillAppear:(BOOL)animated
{
   // register for keyboard notifications
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) 
                                                name:UIKeyboardWillShowNotification object:self.view.window]; 
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) 
                                                name:UIKeyboardWillHideNotification object:self.view.window]; 
}

- (void)viewWillDisappear:(BOOL)animated
{
   // unregister for keyboard notifications while not visible.
   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 
}

rect.origin.y=+currTextField.frame.origin.ylàm việc tốt cảm ơn bạn
u.gen

30

Mã của RPDP di chuyển thành công trường văn bản ra khỏi bàn phím. Nhưng khi bạn cuộn lên trên cùng sau khi sử dụng và tắt bàn phím, phần trên cùng đã được cuộn lên khỏi tầm nhìn. Điều này đúng với Trình mô phỏng và thiết bị. Để đọc nội dung ở đầu chế độ xem đó, người ta phải tải lại chế độ xem.

Không phải mã sau đây của anh ta có nghĩa là đưa tầm nhìn trở lại?

else
{
    // revert back to the normal state.
    rect.origin.y += kOFFSET_FOR_KEYBOARD;
    rect.size.height -= kOFFSET_FOR_KEYBOARD;
}

23

Tôi không chắc chắn nếu di chuyển chế độ xem lên là cách tiếp cận chính xác, tôi đã thực hiện theo cách khác, thay đổi kích thước UIScrollView. Tôi đã giải thích chi tiết về một bài báo nhỏ


Liên kết đến bài viết đã chết.
Teo

22

Để trở về trạng thái xem ban đầu, hãy thêm:

-(void)textFieldDidEndEditing:(UITextField *)sender

{
    //move the main view, so that the keyboard does not hide it.
    if  (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

20

Hãy thử mẹo ngắn này.

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = textField.frame.origin.y / 2; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

19

Có rất nhiều giải pháp, nhưng tôi đã dành vài giờ trước khi nó bắt đầu hoạt động. Vì vậy, tôi đặt mã này ở đây (chỉ cần dán vào dự án, không cần sửa đổi gì):

@interface RegistrationViewController : UIViewController <UITextFieldDelegate>{
    UITextField* activeField;
    UIScrollView *scrollView;
}
@end

- (void)viewDidLoad
{
    [super viewDidLoad];

    scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];

    //scrool view must be under main view - swap it
    UIView* natView = self.view;
    [self setView:scrollView];
    [self.view addSubview:natView];

    CGSize scrollViewContentSize = self.view.frame.size;
    [scrollView setContentSize:scrollViewContentSize];

    [self registerForKeyboardNotifications];
}

- (void)viewDidUnload {
    activeField = nil;
    scrollView = nil;
    [self unregisterForKeyboardNotifications];
    [super viewDidUnload];
}

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShown:)
                                                 name:UIKeyboardWillShowNotification object:nil];

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

}

-(void)unregisterForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillShowNotification
                                                  object:nil];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillHideNotification
                                                  object:nil];
}

- (void)keyboardWillShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    CGRect frame = self.view.frame;
    frame.size.height -= kbSize.height;
    CGPoint fOrigin = activeField.frame.origin;
    fOrigin.y -= scrollView.contentOffset.y;
    fOrigin.y += activeField.frame.size.height;
    if (!CGRectContainsPoint(frame, fOrigin) ) {
        CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y + activeField.frame.size.height - frame.size.height);
        [scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
     [scrollView setContentOffset:CGPointZero animated:YES];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    activeField = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    activeField = nil;
}

-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

PS: Tôi hy vọng mã giúp ai đó tạo hiệu ứng mong muốn nhanh chóng. (Xcode 4.5)


Xin chào Hotjard, tôi đang nhận được EXE_BAD_ACCESS trong [self.view addSubview: natView];
Bala

18

@ người dùng271753

Để có được cái nhìn của bạn trở lại ban đầu thêm:

-(BOOL)textFieldShouldReturn:(UITextField *)textField{
   [textField resignFirstResponder];
   [self setViewMovedUp:NO];
   return YES;
}

16

Nó không yêu cầu chế độ xem cuộn để có thể di chuyển khung nhìn. Bạn có thể thay đổi khung của viewcontroller'schế độ xem để toàn bộ chế độ xem di chuyển lên vừa đủ để đặt trường văn bản phản xạ phía trên bàn phím. Khi tôi gặp vấn đề này, tôi đã tạo một lớp conUIViewController thực hiện điều này. Nó quan sát cho bàn phím sẽ xuất hiện thông báo và tìm thấy phần phụ phản hồi đầu tiên và (nếu cần) nó hoạt hình cho chế độ xem chính hướng lên trên vừa đủ để phần trả lời đầu tiên ở phía trên bàn phím. Khi bàn phím ẩn, nó sẽ kích hoạt chế độ xem lại vị trí của nó.

Để sử dụng lớp con này, hãy biến trình điều khiển chế độ xem tùy chỉnh của bạn thành một lớp con của GMPalVC và nó thừa hưởng tính năng này (chỉ cần chắc chắn nếu bạn triển khai viewWillAppearviewWillDisappearhọ phải gọi siêu). Lớp học trên github .


Giấy phép gì? Một số tệp của bạn có giấy phép nguồn mở và một số thì không.
jaime

Cảnh báo: mã này không thân thiện với các dự án ARC.
Almo

Bạn chỉ cần thêm tùy chọn xây dựng để chỉ định rằng đó là các tệp không phải ARC hoặc hoan nghênh chuyển đổi nó thành ARC và gửi yêu cầu kéo.
progrmr

14

Swift 4 .

Bạn có thể dễ dàng di chuyển lên xuống UITextFieldHoặc UIViewVới UIKeyBoardVớiAnimation nhập mô tả hình ảnh ở đây

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var textField: UITextField!
    @IBOutlet var chatView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textField.resignFirstResponder()
    }

    @objc func keyboardWillChange(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y
        print("deltaY",deltaY)

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.chatView.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
        },completion: nil)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }

}

2
Gân như hoan hảo. Trên iPhone X mặc dù bạn có một khoảng cách lạ giữa bàn phím và trường văn bản.
Houman

12

Đây là giải pháp hack tôi đã đưa ra cho một bố cục cụ thể. Giải pháp này tương tự như giải pháp Matt Gallagher ở chỗ cuộn một phần vào chế độ xem. Tôi vẫn chưa quen với việc phát triển iPhone và chưa quen với cách bố trí hoạt động. Do đó, hack này.

Việc triển khai của tôi cần có để hỗ trợ cuộn khi nhấp vào một trường và cũng cuộn khi người dùng chọn tiếp theo trên bàn phím.

Tôi đã có một UIView với chiều cao 775. Các điều khiển được trải rộng về cơ bản theo nhóm 3 trên một không gian rộng. Tôi đã kết thúc với bố trí IB sau đây.

UIView -> UIScrollView -> [UI Components]

Đây là bản hack

Tôi đặt chiều cao UIScrollView thành 500 đơn vị lớn hơn bố cục thực tế (1250). Sau đó, tôi đã tạo ra một mảng với các vị trí tuyệt đối tôi cần cuộn và một hàm đơn giản để có được chúng dựa trên số Thẻ IB.

static NSInteger stepRange[] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410
};

NSInteger getScrollPos(NSInteger i) {
    if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) {
        return 0 ;
    return stepRange[i] ;
}

Bây giờ tất cả những gì bạn cần làm là sử dụng hai dòng mã sau trong textFieldDidBeginEditing và textFieldShouldReturn (dòng sau nếu bạn đang tạo điều hướng trường tiếp theo)

CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;

Một ví dụ.

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
    [self.scrollView setContentOffset:point animated:YES] ;
}


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

    NSInteger nextTag = textField.tag + 1;
    UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];

    if (nextResponder) {
        [nextResponder becomeFirstResponder];
        CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ;
        [self.scrollView setContentOffset:point animated:YES] ;
    }
    else{
        [textField resignFirstResponder];
    }

    return YES ;
}

Phương pháp này không "cuộn lại" như các phương pháp khác làm. Đây không phải là một yêu cầu. Một lần nữa, đây là một UIView khá 'cao' và tôi không có nhiều ngày để tìm hiểu các công cụ bố trí bên trong.


12

Theo tài liệu , kể từ iOS 3.0, UITableViewControllerlớp sẽ tự động thay đổi kích thước và định vị lại chế độ xem bảng của nó khi có chỉnh sửa nội tuyến của các trường văn bản. Tôi nghĩ rằng không đủ để đặt trường văn bản trong mộtUITableViewCell như một số đã chỉ ra.

Từ các tài liệu :

Trình điều khiển xem bảng hỗ trợ chỉnh sửa nội tuyến các hàng xem bảng; ví dụ, nếu các hàng có các trường văn bản được nhúng trong chế độ chỉnh sửa, nó sẽ cuộn hàng đang được chỉnh sửa phía trên bàn phím ảo được hiển thị.


Tôi tìm thấy cùng một nhận xét. Vâng, đó là sự thật. Điều kỳ lạ là, nó đang hoạt động trong một UITabelViewControll và trong cái thứ hai thì không. Nhưng tôi không thể tìm thấy bất kỳ sự khác biệt trong việc thực hiện của tôi.
Morpheus78

11

Ở đây tôi tìm thấy giải pháp đơn giản nhất để xử lý bàn phím.

Bạn chỉ cần sao chép-dán bên dưới mã mẫu và thay đổi trường văn bản của bạn hoặc bất kỳ chế độ xem nào bạn muốn di chuyển lên.

Bước 1

Chỉ cần sao chép-dán bên dưới hai phương thức trong bộ điều khiển của bạn

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];
}

- (void)deregisterFromKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Bước 2

đăng ký & xoá đăng ký Bàn phím Notifications trong viewWillAppearviewWillDisappear phương pháp tương ứng.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self registerForKeyboardNotifications];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [self deregisterFromKeyboardNotifications];
    [super viewWillDisappear:animated];
}

Bước 3

Ở đây có phần linh hồn, Chỉ cần thay thế trường văn bản của bạn và thay đổi chiều cao bao nhiêu bạn muốn di chuyển lên.

- (void)keyboardWasShown:(NSNotification *)notification
{
    NSDictionary* info = [notification userInfo];
    CGSize currentKeyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    //you need replace your textfield instance here
    CGPoint textFieldOrigin = self.tokenForPlaceField.frame.origin;
    CGFloat textFieldHeight = self.tokenForPlaceField.frame.size.height;

    CGRect visibleRect = self.view.frame;
    visibleRect.size.height -= currentKeyboardSize.height;

    if (!CGRectContainsPoint(visibleRect, textFieldOrigin))
    {
        //you can add yor desired height how much you want move keypad up, by replacing "textFieldHeight" below

        CGPoint scrollPoint = CGPointMake(0.0, textFieldOrigin.y - visibleRect.size.height  + textFieldHeight); //replace textFieldHeight to currentKeyboardSize.height, if you want to move up with more height
        [self.scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification *)notification
{
    [self.scrollView setContentOffset:CGPointZero animated:YES];
}

Tham khảo : tốt, xin vui lòng đánh giá cao anh chàng này , người đã chia sẻ mã snip đẹp này, giải pháp sạch.

Hy vọng điều này sẽ rất hữu ích cho ai đó ngoài kia.


Tôi không nghĩ rằng đây là tốt nhất. Ithink @Dheeraj VS đã đúng: Nó có thể được thực hiện dễ dàng & tự động nếu trường văn bản đó nằm trong ô của bảng (ngay cả khi bảng.scrollable = NO). LƯU Ý rằng: vị trí và kích thước của bảng phải hợp lý. ví dụ: - nếu vị trí y của bảng là 100 được tính từ dưới cùng của chế độ xem, thì bàn phím chiều cao 300 sẽ chồng lên toàn bộ bảng. - nếu chiều cao của bảng = 10 và trường văn bản trong đó phải được cuộn lên 100 khi bàn phím xuất hiện để hiển thị, thì trường văn bản đó sẽ nằm ngoài giới hạn của bảng.
samthui7

@ samthui7 Câu trả lời của Dheeraj chỉ hoạt động nếu bạn đang sử dụng Trình xem bảng, không chỉ là chế độ xem bảng. Nó làm cho nó trở thành một ràng buộc mà đôi khi không phù hợp.
Ben G

10

Đã tìm kiếm một hướng dẫn tốt cho người mới bắt đầu về chủ đề này, tìm thấy hướng dẫn tốt nhất ở đây .

Trong MIScrollView.hví dụ ở cuối hướng dẫn, hãy chắc chắn đặt một khoảng trắng tại

@property (nonatomic, retain) id backgroundTapDelegate;

như bạn thấy đấy.


Xin chào savagenoob, cảm ơn vì liên kết được cung cấp và chào mừng bạn đến với stackoverflow. Vui lòng thử và cung cấp càng nhiều thông tin càng tốt khi trả lời các câu hỏi (tương lai) - các liên kết đơn giản có một chút dễ vỡ. Điều đó nói rằng, nếu câu trả lời là một liên kết đến một hướng dẫn tốt có thể bị bỏ qua.
Maarten Bodewes

10

Khi UITextFieldở trong một UITableViewCellcuộn nên được thiết lập tự động.

Nếu không, có lẽ là do mã / thiết lập của chế độ xem bảng không chính xác.

Ví dụ: khi tôi tải lại bảng dài của mình với một bảng UITextFieldở dưới cùng như sau,

-(void) viewWillAppear:(BOOL)animated
{
   [self.tableview reloadData];
}

sau đó trường văn bản của tôi ở phía dưới bị che khuất bởi bàn phím xuất hiện khi tôi nhấp vào bên trong trường văn bản.

Để khắc phục điều này tôi đã phải làm điều này -

-(void) viewWillAppear:(BOOL)animated
{
    //add the following line to fix issue
    [super viewWillAppear:animated];
    [self.tableview reloadData];
}

Tôi bối rối không biết mã này dùng để làm gì? Khi bàn phím được hiển thị, viewWillAppearkhông được gọi. Và reloadDatakhông làm cho các hàng bị che khuất trở nên hữu hình.
Adam Johns

10

Sử dụng bên thứ ba này, bạn không cần phải viết dù chỉ một dòng

https://github.com/hackiftekhar/IQPalManager

tải về dự án và kéo và thả IQKeyboardManagertrong dự án của bạn. Nếu bạn tìm thấy bất kỳ vấn đề xin vui lòng đọc READMEtài liệu.

Các chàng trai thực sự loại bỏ đau đầu để quản lý bàn phím.


8

Ghi chú : câu trả lời này giả sử textField của bạn nằm trong scrollView.

Tôi thích giải quyết vấn đề này bằng cách sử dụng scrollContentInset và scrollContent Offerset thay vì làm rối với các khung nhìn của tôi.

Trước tiên hãy lắng nghe thông báo bàn phím

//call this from viewWillAppear
-(void)addKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}
//call this from viewWillDisappear
-(void)removeKeyboardNotifications{
    [[NSNotificationCenter default
    Center] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Bước tiếp theo là giữ một thuộc tính đại diện cho phản hồi đầu tiên hiện tại (UITextfield / UITextVIew hiện có bàn phím).

Chúng tôi sử dụng các phương thức đại biểu để đặt thuộc tính này. Nếu bạn đang sử dụng một thành phần khác, bạn sẽ cần một cái gì đó tương tự.

Lưu ý rằng đối với trường văn bản, chúng tôi đặt nó trong didBeginEditing và cho textView trong ShouldBeginEditing. Điều này là do textViewDidBeginEditing được gọi sau UIKeyboardWillShowNotification vì một số lý do.

-(BOOL)textViewShouldBeginEditing:(UITextView * )textView{
    self.currentFirstResponder = textView;
    return YES;
}

-(void)textFieldDidBeginEditing:(UITextField *)textField{
    self.currentFirstResponder = textField;
}

Cuối cùng, đây là phép màu

- (void)keyboardWillShow:(NSNotification*)aNotification{
    NSDictionary* info = [aNotification userInfo];
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];


    /*if currentFirstResponder is overlayed by the keyboard, move it so it bottom ends where the keyboard begins*/
    if(self.currentFirstResponder){

        //keyboard origin in currentFirstResponderFrame
        CGPoint keyboardOrigin = [self.currentFirstResponder convertPoint:kbFrame.origin fromView:nil];

        float spaceBetweenFirstResponderAndKeyboard = abs(self.currentFirstResponder.frame.size.height-keyboardOrigin.y);

        //only scroll the scrollview if keyboard overlays the first responder
        if(spaceBetweenFirstResponderAndKeyboard>0){
            //if i call setContentOffset:animate:YES it behaves differently, not sure why
            [UIView animateWithDuration:0.25 animations:^{
                [self.scrollView setContentOffset:CGPointMake(0,self.scrollView.contentOffset.y+spaceBetweenFirstResponderAndKeyboard)];
            }];
        }
    }

    //set bottom inset to the keyboard height so you can still scroll the whole content

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbFrame.size.height, 0.0);
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;

}

- (void)keyboardWillHide:(NSNotification*)aNotification{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;
}

8

Đây là giải pháp sử dụng Swift.

import UIKit

class ExampleViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var scrollView: UIScrollView!

    @IBOutlet var textField1: UITextField!
    @IBOutlet var textField2: UITextField!
    @IBOutlet var textField3: UITextField!
    @IBOutlet var textField4: UITextField!
    @IBOutlet var textField5: UITextField!

    var activeTextField: UITextField!

    // MARK: - View
    override func viewDidLoad() {
        super.viewDidLoad()
        self.textField1.delegate = self
        self.textField2.delegate = self
        self.textField3.delegate = self
        self.textField4.delegate = self
        self.textField5.delegate = self
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.registerForKeyboardNotifications()
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        self.unregisterFromKeyboardNotifications()
    }

    // MARK: - Keyboard

    // Call this method somewhere in your view controller setup code.
    func registerForKeyboardNotifications() {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
        center.addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterFromKeyboardNotifications () {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.removeObserver(self, name: UIKeyboardDidShowNotification, object: nil)
        center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }

    // Called when the UIKeyboardDidShowNotification is sent.
    func keyboardWasShown (notification: NSNotification) {
        let info : NSDictionary = notification.userInfo!
        let kbSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue() as CGRect!).size

        let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;

        // If active text field is hidden by keyboard, scroll it so it's visible
        // Your app might not need or want this behavior.
        var aRect = self.view.frame
        aRect.size.height -= kbSize.height;
        if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) {
            self.scrollView.scrollRectToVisible(self.activeTextField.frame, animated: true)
        }
    }

    // Called when the UIKeyboardWillHideNotification is sent
    func keyboardWillBeHidden (notification: NSNotification) {
        let contentInsets = UIEdgeInsetsZero;
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;
    }

    // MARK: -  Text Field

    func textFieldDidBeginEditing(textField: UITextField) {
        self.activeTextField = textField
    }

    func textFieldDidEndEditing(textField: UITextField) {
        self.activeTextField = nil
    }

}

Câu trả lời đúng, nhưng tôi gặp vấn đề không khi sử dụng cả TextField và TextView. Có ai giúp đỡ không?
Thiha Aung

@Thiha Aung, Các biến IBOutlet của bạn trong mã nguồn của bạn có được kết nối với IB không?
Homam

Vâng, chúng cũng được kết nối. Bạn cũng gặp phải lỗi đó khi sử dụng UITextView tại dòng đó: if (! CGRectContainsPoint (aRect, self.activeTextField.frame.origin)) {
Thiha Aung

Có nghĩa là self.activeTextField là nil
Thiha Aung
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.