ฉันจะทำให้ UITextField เลื่อนขึ้นเมื่อมีแป้นพิมพ์อยู่ - เมื่อเริ่มแก้ไข


1691

ด้วย iOS SDK:

ฉันมีUIViewกับUITextFieldที่นำขึ้นแป้นพิมพ์ ฉันต้องการมันเพื่อให้สามารถ:

  1. อนุญาตให้เลื่อนเนื้อหาของUIScrollViewเพื่อดูฟิลด์ข้อความอื่น ๆ เมื่อคีย์บอร์ดปรากฏขึ้น

  2. "กระโดด" โดยอัตโนมัติ (โดยเลื่อนขึ้น) หรือย่อ

UIScrollViewฉันรู้ว่าฉันต้องมี ฉันได้ลองเปลี่ยนคลาสของฉันUIViewเป็นUIScrollViewแต่ฉันยังไม่สามารถเลื่อนกล่องข้อความขึ้นหรือลง

ฉันต้องการทั้ง a UIViewและ a UIScrollViewหรือไม่? ไม่มีใครเข้าไปข้างในอีกหรือไม่

สิ่งที่ต้องดำเนินการเพื่อเลื่อนไปยังฟิลด์ข้อความที่ใช้งานอยู่โดยอัตโนมัติ

จะเป็นการดีที่สุดสำหรับการตั้งค่าส่วนประกอบที่จะทำได้ในตัวสร้างส่วนต่อประสาน ฉันต้องการเขียนโค้ดเฉพาะสิ่งที่จำเป็นเท่านั้น

หมายเหตุ: UIView(หรือUIScrollView) ที่ฉันกำลังทำงานด้วยถูกนำขึ้นมาโดย tabbar ( UITabBar) ซึ่งต้องทำงานตามปกติ


แก้ไข: ฉันกำลังเพิ่มแถบเลื่อนเฉพาะเมื่อแป้นพิมพ์ปรากฏขึ้น แม้ว่าจะไม่จำเป็น แต่ฉันรู้สึกว่ามันมีส่วนต่อประสานที่ดีกว่าเพราะผู้ใช้สามารถเลื่อนและเปลี่ยนกล่องข้อความได้

ฉันได้มันทำงานที่ฉันเปลี่ยนขนาดเฟรมของUIScrollViewเมื่อแป้นพิมพ์ขึ้นและลง ฉันแค่ใช้:

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

อย่างไรก็ตามนี่ไม่ใช่ "เลื่อนขึ้น" โดยอัตโนมัติหรือจัดกึ่งกลางของช่องข้อความด้านล่างในพื้นที่ที่มองเห็นได้ซึ่งเป็นสิ่งที่ฉันต้องการจริงๆ


6
ลองดู. ไม่ยุ่งยากสำหรับคุณ TPKeyboardAvoiding
Aruna

21
เป็นเอกสารของ Apple ฉันคิดว่ามันเป็นวิธีที่ดีที่สุด: developer.apple.com/library/ios/#documentation/StringsTextFonts/
......

58
ใช้รหัสนี้คุณต้องมี 1 บรรทัดในไฟล์ appdelegate.m และใช้งานได้ github.com/hackiftekhar/IQKeyboardManager
Pradeep Mittal

9
วิธีที่ดีที่สุดที่ฉันพบจนถึงตอนนี้คือโอเพ่นซอร์สTPKeyboardAvoiding
Mongi Zaidi

2
อีกวิธีหนึ่งคือการเพิ่มฟิลด์ข้อความเนื้อหาและทั้งหมดใน TableViewController และให้ tableview จัดการสิ่งนี้
Vicky Dhas

คำตอบ:


1036
  1. คุณจะต้อง a ScrollViewถ้าเนื้อหาที่คุณมีตอนนี้ไม่พอดีกับหน้าจอ iPhone (หากคุณกำลังเพิ่มสิ่งที่อยู่ในScrollViewฐานะผู้ดูแลของส่วนประกอบเพียงเพื่อให้TextFieldเลื่อนขึ้นเมื่อคีย์บอร์ดปรากฏขึ้นก็ไม่จำเป็นต้องใช้)

  2. วิธีมาตรฐานในการป้องกันTextFields ถูกปกคลุมด้วยแป้นพิมพ์คือการเลื่อนมุมมองขึ้น / ลงทุกครั้งที่มีการแสดงแป้นพิมพ์

นี่คือตัวอย่างรหัสบางส่วน:

#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 คืออะไร ฉันคัดลอกไปยังรหัสของฉันแล้วมันบอกว่า _textField ไม่มีการประกาศ
โกโก้ Dev

เป็นเขตข้อมูลที่คุณใช้ในการพูดว่า "เมื่อผู้ใช้กำลังแก้ไขที่นี่มุมมองควรเลื่อนขึ้น" หรือบางอย่างดังนั้น ... อย่างไรก็ตามคุณสามารถลบได้ถ้าหากคุณมีเขตข้อมูลมากขึ้น
แพทริค

มันไม่ปะทะที่จะโทร - (เป็นโมฆะ) setViewMovedUp: (BOOL) ย้ายขึ้นใน keyBoardWillSHow และ KeyBoardWillHide !!
Abduliam Rehmanius

4
ไม่มีประโยชน์อย่างยิ่งหากคุณสนับสนุนการหมุนของมุมมองหลัก
FractalDoctor

2
เพื่อให้งานนี้ฉันต้องแสดงความคิดเห็นในtextFieldDidBeginEditingส่วน
avance

445

ฉันยังมีปัญหามากมายกับการUIScrollViewแต่งหลายUITextFieldsอย่างซึ่งหนึ่งหรือมากกว่านั้นจะถูกบดบังด้วยแป้นพิมพ์เมื่อพวกเขากำลังแก้ไข

ต่อไปนี้เป็นสิ่งที่ควรพิจารณาหากการUIScrollViewเลื่อนของคุณไม่ถูกต้อง

1) ตรวจสอบให้แน่ใจว่าเนื้อหาของคุณมีขนาดใหญ่กว่าUIScrollViewขนาดเฟรม วิธีที่จะเข้าใจUIScrollViewsคือการUIScrollViewเป็นเหมือนหน้าต่างการดูเนื้อหาที่กำหนดใน contentSize ดังนั้นเมื่อเพื่อให้UIScrollviewเพื่อเลื่อนได้ทุกที่ contentSize UIScrollViewต้องมากกว่า อย่างอื่นไม่จำเป็นต้องมีการเลื่อนเนื่องจากทุกอย่างที่กำหนดใน contentSize สามารถมองเห็นได้แล้ว BTW เริ่มต้น contentSize CGSizeZero=

2) ตอนนี้คุณเข้าใจแล้วว่าUIScrollViewมันเป็นหน้าต่างหนึ่งใน "เนื้อหา" ของคุณวิธีที่จะทำให้แน่ใจว่าแป้นพิมพ์ไม่ได้บดบังUIScrollView'sการดู "หน้าต่าง" ของคุณก็คือการปรับขนาดUIScrollViewเพื่อให้เมื่อแป้นพิมพ์ปรากฏขึ้นคุณมีUIScrollViewหน้าต่าง ขนาดUIScrollViewเท่ากับเฟรมดั้งเดิมขนาดความสูงลบความสูงของแป้นพิมพ์ สิ่งนี้จะช่วยให้มั่นใจได้ว่าหน้าต่างของคุณเป็นเพียงพื้นที่เล็ก ๆ

3) นี่คือสิ่งที่จับได้: เมื่อฉันใช้งานครั้งแรกฉันคิดว่าฉันจะต้องได้รับCGRectของฟิลด์ข้อความที่แก้ไขแล้วและเรียกUIScrollView'sวิธีการ ฉันใช้UITextFieldDelegateวิธีtextFieldDidBeginEditingด้วยการเรียกใช้scrollRecToVisibleวิธีการ นี้จริงทำงานร่วมกับผลข้างเคียงที่แปลกที่การเลื่อนจะตะครุบUITextFieldในตำแหน่ง เป็นเวลานานที่สุดที่ฉันไม่สามารถเข้าใจได้ว่ามันคืออะไร จากนั้นฉันก็แสดงความคิดเห็นเกี่ยวกับtextFieldDidBeginEditingวิธีการมอบสิทธิ์และมันใช้ได้ทั้งหมด !! (???) ตามที่ปรากฏออกมาฉันเชื่อว่าUIScrollViewจริง ๆ แล้วนำการแก้ไขUITextFieldในปัจจุบันไปยังหน้าต่างที่เปิดดูได้โดยปริยาย การใช้UITextFieldDelegateวิธีการของฉันและการโทรไปยังลำดับต่อมาscrollRecToVisibleซ้ำซ้อนและเป็นสาเหตุของผลข้างเคียงที่แปลกประหลาด

ดังนั้นนี่คือขั้นตอนที่จะต้องเลื่อนของคุณUITextFieldในUIScrollViewเข้าไปในสถานที่เมื่อแป้นพิมพ์จะปรากฏขึ้น

// 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. ลงทะเบียนเพื่อรับการแจ้งเตือนคีย์บอร์ดที่ viewDidLoad
  2. ยกเลิกการลงทะเบียนสำหรับ nofitications ของแป้นพิมพ์ที่ viewDidUnload
  3. ตรวจสอบให้แน่ใจว่าcontentSizeมีการตั้งค่าและมากกว่าของคุณUIScrollViewที่viewDidLoad
  4. หดUIScrollViewเมื่อแป้นพิมพ์เป็นปัจจุบัน
  5. กลับUIScrollViewเมื่อแป้นพิมพ์ออกไป
  6. ใช้ Ivar การตรวจสอบถ้าแป้นพิมพ์จะแสดงให้เห็นแล้วบนหน้าจอตั้งแต่การแจ้งเตือนแป้นพิมพ์จะถูกส่งในแต่ละครั้งที่UITextFieldมีการแท็บแม้ว่าแป้นพิมพ์ที่มีอยู่แล้วในปัจจุบันที่จะหลีกเลี่ยงการหดตัวUIScrollViewเมื่อมันมีอยู่แล้วหด

สิ่งหนึ่งที่ควรทราบก็คือว่าจะยิงแม้ในขณะที่แป้นพิมพ์ที่มีอยู่แล้วบนหน้าจอเมื่อคุณแท็บอีกUIKeyboardWillShowNotification UITextFieldฉันดูแลเรื่องนี้โดยใช้ ivar เพื่อหลีกเลี่ยงการปรับขนาดUIScrollViewเมื่อแป้นพิมพ์อยู่บนหน้าจอแล้ว การปรับขนาดโดยไม่ตั้งใจUIScrollViewเมื่อแป้นพิมพ์มีอยู่แล้วจะมีหายนะ!

หวังว่ารหัสนี้จะช่วยให้คุณปวดหัวมาก


3
ยอดเยี่ยม แต่มีปัญหาสองข้อ: 1. UIKeyboardBoundsUserInfoKeyเลิกใช้แล้ว 2. keyboardSize อยู่ใน "พิกัดหน้าจอ" ดังนั้นการคำนวณ viewFrame ของคุณจะล้มเหลวหากเฟรมหมุนหรือปรับขนาด
Martin Wickman

21
@Martin Wickman - ใช้CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;แทนเลิกใช้แล้วUIKeyboardBoundsUserInfoKey
sottenad

1
สวัสดีฉันทำแบบเดียวกัน แต่มุมมองข้อความจะขยับขึ้นเมื่อผู้ใช้เริ่มพิมพ์เท่านั้น มันเป็นพฤติกรรมที่คาดหวังหรือฉันขาดอะไรไป?

3
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size[[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].sizeควรจะเป็น ทางออกที่ดีแม้ว่า!
j7nn7k

1
ฉันชอบโซลูชันของคุณ แต่ฉันคิดว่าฉันทำได้ง่ายกว่า: ไม่ต้องกังวลกับสิ่งที่ผู้สังเกตการณ์แจ้งเตือน แทนที่จะเรียกรูทีนการเคลื่อนไหวที่ถูกต้องภายในวิธีการมอบสิทธิ์ที่เหมาะสมแทน - สำหรับ UITextView พวกเขากำลัง textViewDidBeginEditing และ textViewDidEndEditing
AlexChaffee

270

มันเป็นเรื่องจริงที่ดีที่สุดเพียงแค่ใช้การดำเนินการของแอปเปิ้ลให้เป็นไปในเอกสาร อย่างไรก็ตามรหัสที่พวกเขาให้นั้นมีข้อบกพร่อง แทนที่ส่วนที่พบในkeyboardWasShown:ความคิดเห็นด้านล่างเป็นต่อไปนี้:

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

ปัญหาเกี่ยวกับรหัสของ Apple คือ (1) พวกเขาคำนวณเสมอว่าจุดนั้นอยู่ในกรอบของมุมมอง แต่เป็น a ScrollViewดังนั้นมันอาจจะเลื่อนไปแล้วและคุณจำเป็นต้องพิจารณาบัญชีออฟเซ็ตนั้น:

origin.y -= scrollView.contentOffset.y

(2) พวกเขาเลื่อนเนื้อหาเปิดโดยความสูงของแป้นพิมพ์ แต่เราต้องการตรงกันข้าม (เราต้องการเลื่อนcontentOffsetตามความสูงที่ปรากฏบนหน้าจอไม่ใช่สิ่งที่ไม่ใช่):

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

1
ในสถานการณ์ที่มุมมองการเลื่อนไม่เต็มหน้าจอ aRect ควรถูกตั้งค่าเป็นเฟรมของมุมมองการเลื่อน
mblackwell8

2
คุณไม่ต้องการให้ CGPoint origin = activeField.frame.origin + activeField.frame.size.height ใช่ไหม? เพราะคุณต้องการให้ฟิลด์ข้อความทั้งหมดถูกแสดงและถ้ามันมีพิกเซลที่มองเห็นได้บางส่วนรหัสก็จะไม่ใส่ เงื่อนไข.
htafoya

1
วิธีนี้ไม่ทำงานในการวางแนวแนวนอนช่องข้อความหลุดออกจากด้านบนของพอร์ตมุมมอง iPad ที่มี iOS 7.1
แอนดรู

4
เพื่อการรองรับ iOS 8 ที่ดีขึ้นฉันขอแนะนำให้ใช้UIKeyboardFrameEndUserInfoKeyแทนUIKeyboardFrameBeginUserInfoKeyเมื่อใช้ขนาดแป้นพิมพ์เนื่องจากจะเป็นการเลือกสิ่งต่าง ๆ เช่นการเปลี่ยนคีย์บอร์ดแบบกำหนดเองและการสลับเปิด / ปิดข้อความทำนายผล
Endareth

1
@Egor: การแก้ไขของคุณทำให้การทำงานดีขึ้น - แต่บรรทัดสุดท้ายจะต้องตรงกันข้าม:self.scrollView.contentOffset = self.currentSVoffset;
Morten Holmgaard

244

ฟังก์ชั่นIn textFieldDidBeginEdittingand in textFieldDidEndEditingcall [self animateTextField:textField up:YES]:

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

ฉันหวังว่ารหัสนี้จะช่วยคุณ

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

สวิฟท์ 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
ทำไมไม่ใช้ [UIView animateWithDuration: animations:^{ }];?
Andre Cytryn

2
มันใช้งานได้ดีแม้ว่า const int MovementDistance = -130; // tweak ตามต้องการจะต้องเปลี่ยนให้ยืดหยุ่นมากขึ้น
Hammer

7
ง่ายอย่างไม่น่าเชื่อในการใช้งานขนาดเล็ก ไม่มีปัญหากับ ScrollViews และปัญหาเลย์เอาต์อัตโนมัติ
James Perih

4
เอ่อ ... คุณไม่ต้องใช้พารามิเตอร์ textField เลย ทำไมจึงเป็นพารามิเตอร์ฟังก์ชั่น? นอกจากนี้คุณสามารถใช้ผู้ประกอบการที่ประกอบไปด้วยใน Swift ทำให้รหัสพูดน้อยลง
stk

1
หากสีพื้นหลังของมุมมองเป็นอย่างอื่นที่ไม่ใช่สีดำตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่าสีของหน้าต่างให้ตรงกับมุมมองของคุณเพื่อที่ผู้ใช้จะไม่เห็นด้านหลัง ie self.window.backgroundColor = [UIColor whiteColor];
bvmobileapps

134

เพียงแค่ใช้ TextFields:

1a) การใช้Interface Builder: เลือก All TextFields => Edit => ฝังใน => ScrollView

1b) ฝัง TextFields ด้วยตนเองใน UIScrollView ชื่อ scrollView

2) ตั้งค่า UITextFieldDelegate

3) ตั้งค่าแต่ละรายการtextField.delegate = self;(หรือทำการเชื่อมต่อInterface Builder)

4) คัดลอก / วาง:

- (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
แต่มันยังเลื่อนมุมมองเมื่อtextFieldมองเห็นได้แล้ว
TheTiger

1
ต้องเปลี่ยนCGPointMake(0, textField.frame.origin.y);เป็นCGPointMake(0, textField.frame.origin.y + scrollView.contentInset.top);
Fury

@Egor แม้หลังจากความคิดเห็นของคุณมันไม่ทำงาน เช่นเดียวกับ "TheTiger" ที่กล่าวถึงมันเลื่อนมุมมองขึ้นแม้จะมองเห็นฟิลด์ข้อความ
rak appdev

เปลี่ยนสำหรับ XCode 10: "เลือก TextFields ทั้งหมด => Editor => ฝังใน => เลื่อนดู"
tibalt

116

สำหรับการแก้ปัญหาสากลที่นี่เป็นวิธีการของฉันสำหรับการดำเนินการIQKeyboardManager

ป้อนคำอธิบายรูปภาพที่นี่

ขั้นที่ 1: -ฉันเพิ่มการแจ้งเตือนทั่วโลกของUITextField, UITextViewและUIKeyboardในชั้นเรียนเดี่ยว ผมเรียกมันว่าIQKeyboardManager

ขั้นที่ 2: -หากพบว่าUIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationหรือUITextViewTextDidBeginEditingNotificationการแจ้งเตือนฉันพยายามที่จะได้รับtopMostViewControllerตัวอย่างจากUIWindow.rootViewControllerลำดับชั้น เพื่อให้ถูกต้องเปิดเผยUITextField/ UITextViewมันtopMostViewController.view's กรอบจะต้องปรับ

ขั้นที่ 3: -ผมคำนวณระยะย้ายที่คาดว่าจะtopMostViewController.viewเกี่ยวกับการตอบสนองแรก/UITextFieldUITextView

ขั้นตอนที่ 4: -ฉันเลื่อนtopMostViewController.view.frameขึ้น / ลงตามระยะทางที่คาดไว้

ขั้นที่ 5: -หากพบว่าUIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotificationหรือUITextViewTextDidEndEditingNotificationการแจ้งเตือนอีกครั้งผมพยายามที่จะได้รับtopMostViewControllerตัวอย่างจากUIWindow.rootViewControllerลำดับชั้น

ขั้นตอนที่ 6: -ฉันคำนวณระยะทางที่ถูกรบกวนtopMostViewController.viewซึ่งจะต้องเรียกคืนสู่ตำแหน่งเดิม

ขั้นที่ 7: -ฉันกู้คืนtopMostViewController.view.frameตามระยะทางที่ถูกรบกวน

Step8: -ฉัน instantiated เดี่ยวIQKeyboardManagerเช่นชั้นในการโหลดแอปเพื่อให้ทุกUITextField/ UITextViewใน app จะปรับอัตโนมัติตามระยะทางที่ย้ายคาดว่า

นั่นคือทั้งหมดที่IQKeyboardManagerทำเพื่อคุณโดยไม่ต้องมีไลน์โค้ดจริงๆ !! จำเป็นต้องลากแล้วปล่อยไฟล์ต้นฉบับที่เกี่ยวข้องกับโครงการ IQKeyboardManagerยังรองรับการวางแนวอุปกรณ์ , การจัดการ UIToolbar โดยอัตโนมัติ , คีย์บอร์ดคีย์บอร์ดระยะทางจากฟิลด์ข้อความและอื่น ๆ อีกมากมายที่คุณคิด


เพิ่มไดเรกทอรี IQKeyBoardManagerSwift ในโครงการของฉันและไม่ทำงาน ไม่สามารถเปิดใช้ cuz เพราะไม่รู้จักใน AppDelegate ...
user3722523

2
นี่รู้สึกเหมือนฟิชชิ่งโซลูชันที่แท้จริงไม่แสดง แต่เราเห็นโฆษณาของบัญชี GitHub นี้แทน
Brian

101

ฉันได้ใส่กันสากลหล่นในUIScrollView, UITableViewและแม้กระทั่งUICollectionViewคลาสย่อยที่ดูแลการเคลื่อนย้ายช่องข้อความทั้งหมดที่อยู่ภายในออกจากทางของแป้นพิมพ์

เมื่อคีย์บอร์ดกำลังจะปรากฏขึ้นคลาสย่อยจะค้นหามุมมองย่อยที่จะแก้ไขและปรับออฟเซ็ตเฟรมและเนื้อหาเพื่อให้แน่ใจว่ามุมมองนั้นมองเห็นได้พร้อมกับภาพเคลื่อนไหวเพื่อจับคู่ป๊อปอัปคีย์บอร์ด เมื่อคีย์บอร์ดหายไปมันจะคืนค่าขนาดก่อนหน้า

มันควรจะทำงานกับการตั้งค่าใด ๆ โดยพื้นฐานไม่ว่าจะเป็นUITableViewอินเทอร์เฟซตามหรือหนึ่งประกอบด้วยมุมมองที่วางไว้ด้วยตนเอง

นี่คือวิธีการแก้ปัญหาสำหรับการย้ายฟิลด์ข้อความออกนอกแป้นพิมพ์


นี่ไง! นี่คือทางออกที่ดีที่สุดมีประสิทธิภาพมากที่สุดและสมบูรณ์แบบ! นอกจากนี้ยังจัดการการหมุนอย่างถูกต้องสำหรับมุมมองเลื่อน ถ้าหมุนให้แน่ใจว่าได้ทำการตั้งขนาดอัตโนมัติในแนวตั้ง แต่อย่ายึดที่ด้านล่าง ฉันเพิ่ม UITextView ลงในมุมมองเลื่อนในกรณีของฉัน ขอบคุณมาก!
Christopher

เป็นงานที่ดีมาก! แน่นอนว่าฉันขี้เกียจใช้โซลูชันของคุณแทนที่จะเป็น DIY แต่เจ้านายของฉันมีความสุขกว่าใช่แล้ว! แม้ว่าใครบางคนต้องการที่จะทำด้วยตัวเองฉันชอบวิธีการคลาสย่อยของคุณแทนที่จะเพิ่มรหัสให้กับคอนโทรลเลอร์แต่ละตัว ฉันตกใจที่ iOS ไม่ได้ทำสิ่งนี้ตามค่าเริ่มต้นเหมือนกับที่ Android ทำ - แล้วฉันก็พบว่ามีหลายสิ่งที่ขาดใน iOS และ MacOS :(
eselk

สิ่งที่แปลกอย่างเช่น scrollview ของฉันทั้งหมดเข้ากับหน้าจอหรือไม่ดังนั้นจึงไม่สามารถเลื่อนได้ หลังจากเปิดและปิดคีย์บอร์ดตอนนี้เนื้อหาจะมีขนาดใหญ่ขึ้น (ดูเหมือนว่ามีบางสิ่งที่มองไม่เห็นถูกเพิ่มและไม่ถูกลบที่ด้านล่างของหน้า) และสามารถเลื่อนได้
Almo

90

สำหรับโปรแกรมเมอร์Swift :

สิ่งนี้จะทำทุกอย่างให้คุณเพียงแค่ใส่สิ่งเหล่านี้ลงในคลาสตัวควบคุมมุมมองของคุณและนำไปใช้UITextFieldDelegateกับตัวควบคุมมุมมองของคุณและตั้งค่าตัวแทนของฟิลด์ข้อความเป็นself

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

ใช้วิธีการติดต่อกลับของผู้รับมอบสิทธิ์:

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

สำหรับ Swift 4, 4.2, 5: Change

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

ถึง

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

บันทึกล่าสุดเกี่ยวกับการใช้งานนี้: หากคุณผลักดันตัวควบคุมมุมมองอื่นลงบนสแต็กในขณะที่แป้นพิมพ์ถูกแสดงสิ่งนี้จะสร้างข้อผิดพลาดที่มุมมองจะถูกส่งกลับไปยังเฟรมกลาง ตัวอย่างเช่นแป้นพิมพ์ของคุณเป็นคำตอบแรกสำหรับ nameField แต่จากนั้นคุณกดปุ่มที่ดัน Help View Controller ลงในสแต็กของคุณ ในการแก้ไขข้อผิดพลาดออฟเซตตรวจสอบให้แน่ใจว่าได้เรียก nameField.resignFirstResponder () ก่อนออกจากตัวควบคุมมุมมองเพื่อให้แน่ใจว่าเมธอดผู้รับมอบสิทธิ์ textFieldDidEndEditing นั้นถูกเรียกเช่นกัน ฉันทำสิ่งนี้ในวิธี viewWillDisappear


3
SwiftLint ไม่ชอบself.view.frame = CGRectOffset(self.view.frame, 0, movement)ดังนั้นฉันจึงเปลี่ยนบรรทัดเป็นself.view.frame.offsetInPlace(dx: 0, dy: movement)
levibostian

2
Swift 4 change self.view.frame = CGRectOffset (self.view.frame, 0, การเคลื่อนไหว) เป็น self.view.frame.offsetBy (dx: 0, dy: Movement)
Asinox

FYI สำหรับการทำงานคุณต้องใส่ self.view.frame = self.view.frame.offsetBy (dx: 0, dy: Movement)
Josh Wolff

64

มีคำตอบอยู่แล้วจำนวนมาก แต่ก็ยังไม่มีวิธีแก้ปัญหาใดที่ได้รับการจัดวางตำแหน่งที่ต้องการสำหรับภาพเคลื่อนไหว "สมบูรณ์แบบ" ปราศจากข้อผิดพลาดเข้ากันได้กับภาพเคลื่อนไหวที่ปราศจากการสั่นไหว (ข้อผิดพลาดเมื่อเคลื่อนไหวเฟรม / ขอบเขตและ contentOffset เข้าด้วยกันแนวการใช้งานที่แตกต่างกันคีย์บอร์ดแยก iPad, ... )
ให้ฉันแบ่งปันโซลูชันของฉัน:
(สมมติว่าคุณได้ตั้งค่า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];
}

การปรับปรุงที่ยอดเยี่ยมของคำตอบ @Shiun แต่หลังจากคีย์บอร์ดหายไปมุมมองจะไม่กลับมาอยู่ในตำแหน่งที่ 1 มันยังคงเป็นงานที่ยอดเยี่ยม :)
ลูเชียน

2
ขอบคุณนี้เป็นทางออกที่ดีที่สุดสำหรับผมในปี 2017 ทราบว่าคุณไม่จำเป็นต้องติดตาม focusedControl UIApplication.shared.sendAction(...)ตัวคุณเองคุณสามารถตรวจสอบว่ามีการ นี่คือคำตอบของคุณใน Swift 3 (ลบส่วนที่ซ่อนไว้) โดยมีการsendActionใช้งาน: gist.github.com/xaphod/7aab1302004f6e933593a11ad8f5a72d
xaphod

@xaphod ในกรณีของฉันฉันต้องการที่จะมุ่งเน้นการควบคุมมากขึ้น - เช่นปุ่มด้านล่างของช่องใส่ แต่ใช่ว่าตอนนี้รหัสอายุ 4 ปีและอาจได้รับประโยชน์จากการปรับปรุง
Martin Ullrich

นี่อาจเป็นทางออกที่เหมาะสม การแจ้งเตือนของแป้นพิมพ์มีข้อมูลภาพเคลื่อนไหวการมอบหมายเขตข้อมูลข้อความไม่ทราบระยะเวลาของภาพเคลื่อนไหวมันเป็นเพียงการเดา
XY

62

Shiun กล่าวว่า "ตามที่ปรากฏออกมาฉันเชื่อว่า UIScrollView จริง ๆ แล้วนำ UITextField ที่แก้ไขในขณะนี้เข้าสู่หน้าต่างที่เปิดดูได้โดยปริยาย" นี่น่าจะเป็นจริงสำหรับ iOS 3.1.3 แต่ไม่ใช่ 3.2, 4.0 หรือ 4.1 ฉันต้องเพิ่ม scrollRectToVisible อย่างชัดเจนเพื่อให้ UITextField ปรากฏบน iOS> = 3.2


ไม่ใช่ UIScrollView ซึ่งเลื่อนโดยปริยาย UITextField ที่แก้ไขลงในมุมมอง UITextField ของมันซึ่งเรียก[UITextField scrollTextFieldToVisibleIfNecessary]เมธอดไพรเวตซึ่งจะเรียก[UIScrollView scrollRectToVisible]เมื่อ[UITextField becomeFirstResponder]มีการเรียก ดูgithub.com/leopatras/ios_textfields_on_scrollview หากมีการตั้งค่าข้อ จำกัด และตัวควบคุมมุมมองอย่างถูกต้องก็ไม่จำเป็นต้องโทรscrollRectToVisibleอย่างชัดเจน (อย่างน้อยก็ตั้งแต่ IOS 11)
Leo

48

สิ่งหนึ่งที่ต้องพิจารณาคือว่าคุณต้องการใช้ a UITextFieldด้วยตัวเองหรือไม่ ผมยังไม่ได้เจอ iPhone ใด ๆ การออกแบบที่ดีปพลิเคชันที่ใช้งานจริงนอกUITextFieldsUITableViewCells

มันจะเป็นงานพิเศษ แต่ฉันขอแนะนำให้คุณใช้มุมมองตารางทั้งหมดของการป้อนข้อมูล เพิ่มที่คุณUITextViewUITableViewCells


1
หนึ่งในแอพของฉันต้องอนุญาตให้ผู้ใช้เพิ่มบันทึกแบบอิสระดังนั้นใช่บางครั้งมันก็มีประโยชน์ในการใช้ UITextField
ปีเตอร์จอห์นสัน

1
ฉันเห็นด้วยกับวิธีนี้ ทำงานเป็นศูนย์หรือรหัสด้วยวิธีนี้ แม้ว่าคุณจะต้องการโน้ตแบบฟรี แต่คุณก็สามารถใช้เซลล์ตารางได้
RJH

UITableViewเป็นหนทางเดียวที่น่าเศร้า การแจ้งเตือนของคีย์บอร์ดเปราะและเปลี่ยนการทำงานล่วงเวลา โค้ดตัวอย่างบน Stack Overflow: stackoverflow.com/a/32390936/218152
SwiftArchitect

คำตอบนี้ล้าสมัยไปแล้วห้าปี ทางแก้ปัญหาที่ทันสมัยเพียงอย่างเดียวคือ ... stackoverflow.com/a/41808338/294884
Fattie

47

เอกสารนี้ให้รายละเอียดการแก้ไขปัญหานี้ ดูซอร์สโค้ดใต้ 'การย้ายเนื้อหาที่อยู่ใต้แป้นพิมพ์' มันค่อนข้างตรงไปตรงมา

แก้ไข: สังเกตเห็นว่ามีข้อผิดพลาดเล็กน้อยในตัวอย่าง คุณอาจจะต้องการที่จะฟังแทนUIKeyboardWillHideNotification UIKeyboardDidHideNotificationมิฉะนั้นมุมมองเลื่อนด้านหลังของแป้นพิมพ์จะถูกตัดในช่วงระยะเวลาของภาพเคลื่อนไหวการปิดแป้นพิมพ์


32

พบทางออกที่ง่ายที่สุด

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

หน้าจอขยับขึ้นแม้ว่าจะไม่ได้อยู่ที่ด้านล่าง นั่นคือถ้าช่องข้อความอยู่ด้านบนมันจะย้ายออกจากหน้าจอ วิธีควบคุมกรณีนั้น
MELWIN

@ เมลวินเพิ่งเพิ่มหลังจากบรรทัดนี้: int movement = (up ? -movementDistance : movementDistance); if (textField.frame.origin.y < self.view.frame.size.height - keyboard.height) { movementDistance = 0 }โปรดอย่าว่าkeyboardตัวแปรคือ CGRect ของแป้นพิมพ์ที่ปรากฏขึ้นที่คุณได้รับโดยทำ:let keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
CapturedTree

31

แก้ไขเล็กน้อยที่ใช้งานได้กับ 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.yทำงานได้ดีขอบคุณ
u.gen

30

รหัสของ RPDP จะย้ายช่องข้อความออกไปให้พ้นจากคีย์บอร์ด แต่เมื่อคุณเลื่อนไปที่ด้านบนหลังจากใช้และยกเลิกคีย์บอร์ดด้านบนจะถูกเลื่อนขึ้นจากมุมมอง สิ่งนี้เป็นจริงสำหรับเครื่องมือจำลองและอุปกรณ์ หากต้องการอ่านเนื้อหาที่ด้านบนของมุมมองนั้นต้องโหลดอีกครั้ง

รหัสต่อไปนี้ของเขาไม่ควรที่จะนำมุมมองกลับมา?

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

23

ฉันไม่แน่ใจว่าการเลื่อนมุมมองขึ้นเป็นวิธีที่ถูกต้องหรือไม่ฉันทำในลักษณะที่แตกต่างโดยปรับขนาด UIScrollView ฉันอธิบายรายละเอียดในบทความเล็กน้อย


ลิงก์ไปยังบทความนั้นตายแล้ว
Teo

22

หากต้องการนำกลับสู่สถานะมุมมองดั้งเดิมให้เพิ่ม:

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

ลองเคล็ดลับสั้น ๆ นี้

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

มีวิธีแก้ปัญหามากมาย แต่ฉันใช้เวลาหลายชั่วโมงก่อนที่มันจะเริ่มทำงาน ดังนั้นฉันใส่รหัสนี้ที่นี่ (เพียงวางโครงการแก้ไขใด ๆ ที่ไม่จำเป็น):

@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: ฉันหวังว่ารหัสช่วยให้ใครบางคนทำให้ผลที่ต้องการได้อย่างรวดเร็ว (Xcode 4.5)


สวัสดี Hotjard ฉันได้รับ EXE_BAD_ACCESS ใน [self.view addSubview: natView];
บาลา

18

@ user271753

ในการทำให้มุมมองของคุณกลับไปเป็นแบบดั้งเดิมเพิ่ม:

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

16

ไม่จำเป็นต้องมีมุมมองแบบเลื่อนเพื่อให้สามารถเลื่อนกรอบมุมมองได้ คุณสามารถเปลี่ยนเฟรมของviewcontroller'sมุมมองเพื่อให้มุมมองทั้งหมดขยับขึ้นมาเพียงพอที่จะวางฟิลด์ข้อความตอบรับอัตโนมัติที่ด้านบนของคีย์บอร์ด เมื่อฉันพบปัญหาฉันสร้าง subclass ขึ้นมาUIViewControllerเพื่อทำสิ่งนี้ มันจะสังเกตเห็นว่าแป้นพิมพ์จะปรากฏการแจ้งเตือนและค้นหามุมมองย่อยการตอบกลับแรกและ (หากจำเป็น) มันจะทำให้ภาพเคลื่อนไหวของมุมมองหลักสูงขึ้นพอเพียงเพื่อให้การตอบกลับแรกอยู่เหนือแป้นพิมพ์ เมื่อคีย์บอร์ดซ่อนอยู่มันจะทำให้ภาพเคลื่อนไหวกลับมาเหมือนเดิม

หากต้องการใช้คลาสย่อยนี้ให้ตัวควบคุมมุมมองที่กำหนดเองของคุณเป็นคลาสย่อยของGMKeyboardVCและสืบทอดคุณสมบัตินี้ (ตรวจสอบให้แน่ใจว่าคุณใช้งานviewWillAppearและviewWillDisappearต้องเรียกใช้ super) ชั้นอยู่บนGitHub


ใบอนุญาตอะไร ไฟล์บางไฟล์ของคุณมีใบอนุญาตโอเพนซอร์สและบางไฟล์ไม่มี
Jaime

คำเตือน: รหัสนี้ไม่เป็นมิตรกับโครงการ ARC
Almo

คุณเพียงแค่เพิ่มตัวเลือกการสร้างเพื่อระบุว่าเป็นไฟล์ที่ไม่ใช่ ARC หรือยินดีที่จะแปลงเป็น ARC และส่งคำขอดึง
progrmr

14

สวิฟท์ 4

คุณสามารถเลื่อนขึ้นและลงUITextFieldหรือUIViewด้วยUIKeyBoardด้วยAnimation ป้อนคำอธิบายรูปภาพที่นี่

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
เกือบจะสมบูรณ์แบบ. บน iPhone X แม้ว่าคุณจะมีช่องว่างแปลก ๆ ระหว่างแป้นพิมพ์และฟิลด์
Houman

12

นี่คือโซลูชันแฮ็คที่ฉันสร้างขึ้นมาเพื่อการจัดวางเฉพาะ โซลูชันนี้คล้ายกับโซลูชัน Matt Gallagher ซึ่งจะเป็นการเลื่อนส่วนในมุมมอง ฉันยังใหม่กับการพัฒนา iPhone และไม่คุ้นเคยกับวิธีการทำงานของเลย์เอาต์ ดังนั้นแฮ็คนี้

การใช้งานของฉันจำเป็นต้องสนับสนุนการเลื่อนเมื่อคลิกในเขตข้อมูลและยังเลื่อนเมื่อผู้ใช้เลือกถัดไปบนแป้นพิมพ์

ฉันมี UIView ที่มีความสูง 775 ตัวควบคุมถูกกระจายออกโดยทั่วไปในกลุ่ม 3 ที่มีพื้นที่ขนาดใหญ่ ฉันลงเอยด้วยเค้าโครง IB ต่อไปนี้

UIView -> UIScrollView -> [UI Components]

แฮ็คมาที่นี่

ฉันตั้งค่าความสูง UIScrollView เป็น 500 หน่วยที่ใหญ่กว่าจากนั้นจึงจัดวางจริง (1250) จากนั้นฉันก็สร้างอาร์เรย์ที่มีตำแหน่งสัมบูรณ์ที่ฉันต้องการเลื่อนไปมาและฟังก์ชั่นง่าย ๆ เพื่อให้ได้มาซึ่งหมายเลข IB Tag

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

ตอนนี้สิ่งที่คุณต้องทำคือใช้โค้ดสองบรรทัดต่อไปนี้ใน textFieldDidBeginEditing และ textFieldShouldReturn (อันหลังถ้าคุณกำลังสร้างการนำทางฟิลด์ถัดไป)

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

ตัวอย่าง.

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

วิธีนี้ไม่ 'เลื่อนกลับ' เหมือนวิธีอื่น นี่ไม่ใช่ข้อกำหนด อีกครั้งนี่เป็น UIView ที่ค่อนข้าง 'สูง' และฉันไม่มีเวลาเรียนรู้เกี่ยวกับเอ็นจิ้นเลย์เอาต์ภายใน


12

ตามเอกสารในฐานะของ iOS 3.0 UITableViewControllerคลาสจะปรับขนาดและจัดตำแหน่งมุมมองตารางใหม่โดยอัตโนมัติเมื่อมีการแก้ไขในบรรทัดของฟิลด์ข้อความ ฉันคิดว่ามันไม่เพียงพอที่จะใส่เขตข้อมูลข้อความภายใน a UITableViewCellตามที่บางคนระบุไว้

จากเอกสาร :

ตัวควบคุมมุมมองตารางรองรับการแก้ไขแบบอินไลน์ของแถวมุมมองตาราง ตัวอย่างเช่นหากแถวมีเขตข้อมูลข้อความฝังตัวในโหมดแก้ไขแถวนั้นจะเลื่อนแถวที่แก้ไขด้านบนแป้นพิมพ์เสมือนจริงที่แสดง


ฉันพบความคิดเห็นเดียวกัน ใช่มันเป็นความจริง สิ่งที่แปลกคือมันทำงานใน UITabelViewController หนึ่งอันและอันที่สองไม่ใช่ แต่ฉันไม่พบความแตกต่างในการนำไปใช้ของฉัน
Morpheus78

11

ที่นี่ฉันพบทางออกที่ง่ายที่สุดในการจัดการปุ่มกด

คุณต้องคัดลอกโค้ดด้านล่างและเปลี่ยนฟิลด์ข้อความหรือมุมมองที่คุณต้องการเลื่อนขึ้น

ขั้นตอนที่ 1

เพียงคัดลอกวางด้านล่างสองวิธีในตัวควบคุมของคุณ

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

ขั้นตอนที่ 2

ลงทะเบียนและยกเลิกการลงทะเบียนการแจ้งเตือนปุ่มกดในviewWillAppearและ viewWillDisappearวิธีการตามลำดับ

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

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

ขั้นตอนที่ 3

ที่นี่เป็นส่วนวิญญาณมาแทนที่ฟิลด์ข้อความของคุณและเปลี่ยนความสูงเท่าไหร่ที่คุณต้องการจะกลับหัวกลับหาง

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

การอ้างอิง : ดีโปรดขอบคุณผู้ชายคนนี้ที่แบ่งปัน snip รหัสที่สวยงามนี้โซลูชั่นที่สะอาด

หวังว่านี่จะเป็นประโยชน์กับใครบางคนอย่างแน่นอน


ฉันไม่คิดว่านี่จะดีที่สุด Ithink @Dheeraj VS ถูกต้อง: สามารถทำได้อย่างง่ายดาย & อัตโนมัติหากฟิลด์ข้อความนั้นอยู่ในเซลล์ของตาราง (แม้ในขณะที่ table.scrollable = NO) โปรดทราบว่า: ตำแหน่งและขนาดของตารางจะต้องสมเหตุสมผล เช่น: - หากตำแหน่ง y ของตารางนับ 100 จากด้านล่างของมุมมองจากนั้นแป้นพิมพ์ความสูง 300 จะซ้อนทับทั้งตาราง - หากความสูงของตาราง = 10 และฟิลด์ข้อความในนั้นจะต้องเลื่อนขึ้น 100 เมื่อแป้นพิมพ์ปรากฏขึ้นเพื่อให้สามารถมองเห็นได้ฟิลด์ข้อความนั้นจะไม่อยู่ในขอบเขตของตาราง
samthui7

@ samthui7 คำตอบ Dheeraj ใช้ได้เฉพาะในกรณีที่คุณใช้ TableViewController ไม่ใช่เพียงแค่ tableview มันทำให้เป็นข้อ จำกัด ที่บางครั้งไม่เหมาะ
Ben G

10

รับการค้นหากวดวิชาที่ดีสำหรับผู้เริ่มต้นในเรื่องที่คิดว่าการกวดวิชาที่ดีที่สุดที่นี่

ในMIScrollView.hตัวอย่างที่ด้านล่างของบทช่วยสอนให้แน่ใจว่าได้ใส่ช่องว่างไว้ที่

@property (nonatomic, retain) id backgroundTapDelegate;

อย่างที่คุณเห็น.


สวัสดี savagenoob ขอบคุณสำหรับลิงก์ที่ให้ไว้และยินดีต้อนรับสู่ stackoverflow โปรดลองและให้ข้อมูลมากที่สุดเท่าที่จะทำได้เมื่อตอบคำถาม (ในอนาคต) - การเชื่อมโยงง่าย ๆ จะเปราะบาง ที่กล่าวว่าหากคำตอบคือลิงค์ไปยังบทช่วยสอนที่ดีที่อาจถูกมองข้าม
Maarten Bodewes

10

เมื่อUITextFieldอยู่ในการUITableViewCellเลื่อนควรตั้งค่าโดยอัตโนมัติ

หากไม่ใช่อาจเป็นเพราะรหัส / การตั้งค่ามุมมอง Table ไม่ถูกต้อง

ตัวอย่างเช่นเมื่อฉันโหลดตารางยาวของฉันอีกครั้งหนึ่งUITextFieldที่ด้านล่างดังนี้

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

จากนั้นฟิลด์ข้อความของฉันที่ด้านล่างถูกบังด้วยแป้นพิมพ์ซึ่งปรากฏขึ้นเมื่อฉันคลิกภายในฟิลด์ข้อความ

เพื่อแก้ไขปัญหานี้ฉันต้องทำเช่นนี้ -

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

ฉันสับสนรหัสนี้มีไว้เพื่ออะไร? เมื่อคีย์บอร์ดปรากฏขึ้นviewWillAppearจะไม่ถูกเรียก และreloadDataไม่ทำให้แถวที่ถูกบดบังปรากฏให้เห็น
Adam Johns

10

ใช้บุคคลที่สามนี้คุณไม่จำเป็นต้องเขียนแม้แต่บรรทัดเดียว

https://github.com/hackiftekhar/IQKeyboardManager

ดาวน์โหลดโครงการและลากและวางIQKeyboardManagerในโครงการของคุณ หากคุณพบปัญหาใด ๆ โปรดอ่านREADMEเอกสาร

ผู้ชายจริงๆปวดหัวลบมันในการจัดการแป้นพิมพ์


8

หมายเหตุ : คำตอบนี้ถือว่า textField ของคุณอยู่ใน scrollView

ฉันชอบที่จะจัดการกับเรื่องนี้โดยใช้ scrollContentInset และ scrollContentOffset แทนที่จะยุ่งกับกรอบของมุมมองของฉัน

ก่อนอื่นลองฟังการแจ้งเตือนของคีย์บอร์ด

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

ขั้นตอนต่อไปคือการรักษาคุณสมบัติที่แสดงถึงการตอบกลับแรกในปัจจุบัน (UITextfield / UITextVIew ที่ปัจจุบันมีแป้นพิมพ์)

เราใช้วิธีการมอบหมายเพื่อตั้งค่าคุณสมบัตินี้ หากคุณใช้องค์ประกอบอื่นคุณจะต้องมีสิ่งที่คล้ายกัน

โปรดทราบว่าสำหรับฟิลด์ข้อความเราตั้งใน didBeginEditing และสำหรับ textView ใน shouldBeginEditing นี่เป็นเพราะ textViewDidBeginEditing ถูกเรียกหลังจาก UIKeyboardWillShowNotification ด้วยเหตุผลบางประการ

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

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

ในที่สุดนี่คือความมหัศจรรย์

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

นี่เป็นวิธีแก้ปัญหาโดยใช้ 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
    }

}

คำตอบที่ถูกต้อง แต่ฉันมีปัญหาศูนย์เมื่อใช้ทั้ง TextField และ TextView ความช่วยเหลือใด ๆ
Thiha Aung

@Thiha Aung, ตัวแปร IBOutlet ของคุณในซอร์สโค้ดของคุณเชื่อมต่อกับ IB หรือไม่?
Homam

ใช่พวกเขามีการเชื่อมต่อเช่นกันเพียงแค่มีข้อผิดพลาดเมื่อใช้ UITextView ที่บรรทัดนั้น: if (! CGRectContainsPoint (aRect, self.activeTextField.frame.origin)) {
Thiha Aung

หมายความว่า self.activeTextField นั้นเป็นศูนย์
Thiha Aung
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.