2013-10-31 69 views
15

我正在使用UIKeyboardWillShowNotificationUIKeyboardWillHideNotification沿着鍵盤動畫顯示動畫使用UIKeyboardAnimationDurationUserInfoKey,UIKeyboardAnimationCurveUserInfoKeyUIKeyboardFrameEndUserInfoKey出現動畫。沿着鍵盤動畫UIView出現動畫

一切工作正常,只要元素的開始位置在屏幕的底部。我的元素(截圖中的輸入框)從UITabBarController的上方開始,所以如果我的動畫開始,鍵盤和UITextField之間會出現間隙,UITextField會沿着動畫收縮,直到它結束。

我正在尋找的東西是這樣的:「使用相同的動畫曲線動畫,但如果鍵盤達到我的最大位置,則開始移動。

如果我要添加一個啓動動畫的延遲,它將不適用於緩動,這可能會在未來的iOS版本中打破。

如果你與我分享你的想法,這將是非常好的。 :-)

Keyboard closed Keyboard opened Keyboard animating (see the gap

+0

如果任何人使用約束...可能是這個解決方案幫助他們... http://stackoverflow.com/questions/31356293/uitableview-and-uiview-with-keyboardwillshow/31356527#31356527 –

回答

14

通常有可能使用保持鍵盤上面的一個觀點,因爲它以動畫到位兩種方法。如您所知,首先是偵聽UIKeyboardWillShowNotification並在userData中使用伴隨的持續時間/曲線/幀值來幫助您在鍵盤上方定位和製作視圖的動畫。

第二種方法是爲調用鍵盤的視圖(UITextField,在這裏)提供inputAccessoryView(我意識到這不會提供你要求的效果,一旦鍵盤運行到工具欄/文本框就會「推動」工具欄/文本框,但後面會詳細介紹) iOS將把inputAccessoryView保存爲也可以將父母的鍵盤和父母聯繫起來。根據我的經驗,這提供了最好看的動畫。我不認爲我曾經使用UIKeyboardWillShowNotification方法完成完美動畫,尤其是現在在iOS7中,在鍵盤動畫的末尾有一些反彈。 UIKit Dynamics也可能會將此反彈應用於您的視圖,但將其與鍵盤完全同步會很困難。

以下是我在過去爲類似於您的場景所做的:底部定位UIToolbar在customView欄按鈕項中有UITextField用於輸入。在你的情況下,這是定位在UITabBar以上。該ITextField有一個自定義inputAccessoryView集,這是另一個UIToolbar另一個UITextField

當用戶點擊文本字段併成爲第一響應者時,鍵盤將第二個工具欄/文本字段與其一起移動到位(並且此轉換看起來非常好!)。當我們注意到發生這種情況時,我們將firstResponder從第一個文本字段轉換爲第二個,這樣一旦鍵盤就位,它就會有閃爍的插入符號。

當你決定結束編輯時,該怎麼做。首先,您必須在第二個文本字段中放棄第一個響應者,但如果您不小心,系統會將第一個響應者狀態傳遞迴原始文本字段!所以你必須防止這種情況發生,因爲否則你將處於一個無限循環中傳遞第一響應者,並且鍵盤永遠不會解散。其次,您需要將任何輸入到第二個文本字段的文本反射回第一個文本字段。

下面是這個方法的代碼:

@implementation TSViewController 
{ 
    IBOutlet UIToolbar*  _toolbar; // parented in your view somewhere 

    IBOutlet UITextField* _textField; // the customView of a UIBarButtonItem in the toolbar 

    IBOutlet UIToolbar*  _inputAccessoryToolbar; // not parented. just owned by the view controller. 

    IBOutlet UITextField* _inputAccessoryTextField; // the customView of a UIBarButtonItem in the inputAccessoryToolbar 
} 

- (void) viewDidLoad 
{ 
    [super viewDidLoad]; 

    _textField.delegate = self; 
    _inputAccessoryTextField.delegate = self; 

    _textField.inputAccessoryView = _inputAccessoryToolbar; 
} 

- (void) textFieldDidBeginEditing: (UITextField *) textField 
{ 
    if (textField == _textField) 
    { 
     // can't change responder directly during textFieldDidBeginEditing. postpone: 
     dispatch_async(dispatch_get_main_queue(), ^{ 

      _inputAccessoryTextField.text = textField.text; 

      [_inputAccessoryTextField becomeFirstResponder]; 
     }); 
    } 
} 

- (BOOL) textFieldShouldBeginEditing: (UITextField *) textField 
{ 
    if (textField == _textField) 
    { 
     // only become first responder if the inputAccessoryTextField isn't the first responder. 
     return ![_inputAccessoryTextField isFirstResponder]; 
    } 
    return YES; 
} 

- (void) textFieldDidEndEditing: (UITextField *) textField 
{ 
    if (textField == _inputAccessoryTextField) 
    { 
     _textField.text = textField.text; 
    } 
} 

// invoke this when you want to dismiss the keyboard! 
- (IBAction) done: (id) sender 
{ 
    [_inputAccessoryTextField resignFirstResponder]; 
} 

@end 

還有最後一個可能性,我能想到的。上面的方法有兩個獨立的工具欄/文本框的缺點。理想情況下,你需要的只是其中的一組,並且你希望看起來鍵盤會「推」它們(或將它們拉下來)。在現實中,動畫速度足夠快,我不認爲大多數人會注意到上述方法有兩套,但也許你不喜歡那樣..

這最後的方法是監聽鍵盤顯示/隱藏,並使用CADisplayLink同步動畫工具欄/文本字段,因爲它實時檢測鍵盤位置的變化。在我的測試中,它看起來不錯。我看到的主要缺點是工具欄的位置稍微滯後。我正在使用自動佈局,轉換到傳統的框架定位可能會更快。另一個缺點是對鍵盤視圖層次結構的依賴性沒有顯着改變。這可能是最大的風險。

這還有一個竅門。工具欄使用約束條件定位在我的故事板中。距離視圖底部的距離有兩個限制。一個綁定到IBOutlet「_toolbarBottomDistanceConstraint」,這是代碼用於移動工具欄的內容。這個約束是一個「垂直空間」約束與「平等」關係。我將優先級設置爲500.有一個「大於或等於」關係的第二個平行「垂直空間」約束。這個常量是視圖底部的最小距離(例如,在標籤欄上方),優先級爲1000.通過這兩個約束,我可以將工具欄從底部距離設置爲任意值I像,但它永遠不會低於我的最低價值。這是使鍵盤看起來像推/拉工具欄,但讓它在某個點「放棄」動畫的關鍵。

最後,也許您可​​以將此方法與您已有的方法混合使用:使用CADisplayLink回調來檢測鍵盤何時「進入」您的工具欄,然後手動定位剩餘工具欄的動畫,使用真正的UIView動畫來爲你的工具欄設置動畫。您可以將持續時間設置爲鍵盤顯示動畫持續時間減去已經發生的時間。

@implementation TSViewController 
{ 
    IBOutlet UITextField*   _textField; 

    IBOutlet UIToolbar*    _toolbar; 

    IBOutlet NSLayoutConstraint* _toolbarBottomDistanceConstraint; 

    CADisplayLink*     _displayLink; 
} 

- (void) dealloc 
{ 
    [[NSNotificationCenter defaultCenter] removeObserver: self]; 
} 

- (void) viewDidLoad 
{ 
    [super viewDidLoad]; 

    [self.view addGestureRecognizer: [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(dismiss:) ]]; 

    _textField.inputAccessoryView = [[UIView alloc] init]; 

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

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

    [[NSNotificationCenter defaultCenter] addObserver: self 
              selector: @selector(keyboardDidShowHide:) 
               name: UIKeyboardDidShowNotification 
               object: nil]; 

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

- (void) keyboardWillShowHide: (NSNotification*) n 
{ 
    _displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(tick:)]; 
    [_displayLink addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes]; 
} 

- (void) keyboardDidShowHide: (NSNotification*) n 
{ 
    [_displayLink removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes]; 
} 

- (void) tick: (CADisplayLink*) dl 
{ 
    CGRect r = [_textField.inputAccessoryView.superview.layer.presentationLayer frame]; 
    r = [self.view convertRect: r fromView: _textField.inputAccessoryView.superview.superview]; 

    CGFloat fromBottom = self.view.bounds.size.height - r.origin.y; 
    _toolbarBottomDistanceConstraint.constant = fromBottom; 
} 

- (IBAction) dismiss: (id) sender 
{ 
    [self.view endEditing: YES]; 
} 

@end 

這裏的視圖層次和約束:

enter image description here

+0

我已經嘗試採用你首先在iOS7中描述的方法。儘管我可以讓光標顯示在輸入附件文本視圖中,但輸入仍然會轉到原始文本視圖(您稱之爲_textfield)。如果我強迫他辭職爲響應者,鍵盤會消失。有任何想法嗎? – pickwick

+0

@pickwick - 聽起來像第二個文本框的代表沒有連接。檢查兩個文本字段共享相同的代理。 – TomSwift

+0

感謝您的幫助,但不幸的是,事實並非如此。無論如何,我認爲我應該還是應該在第二個文本框中看到文本,而不管它是否是委託人。我會繼續玩... – pickwick

1

我沒有一個詳細的解決方案爲湯姆提供的,但我有一個想法,你可以玩的。我一直在做自動佈局和限制的很多有趣的事情,你可以做一些令人驚歎的事情。請注意,您無法將滾動視圖中的項目與其中的項目進行約束。

所以你有你的主要觀點,我認爲它是一個表視圖或scrollView內的其他視圖,所以你必須處理。我建議的方式是拍攝視圖的快照,將當前視圖保存在ivar(您的表格)中,並將其替換爲錨定在底部的「非常高的容器視圖」,將包含快照的UIImageView放入這個視圖,它與常量= 0的容器視圖之間的約束。給用戶沒有任何改變。

在「inputAccessoryView」中,當視圖添加到superView中(並且存在window屬性時),可以刪除圖像和容器視圖之間的約束,並添加一個約束底部的新約束的文本字段添加到inputAccessoryView的頂部,其中距離必須大於某個值。您必須四處遊覽以獲取值,因爲它將成爲您的scrollView中爲任何contentValue調整的textField的偏移量。然後,您很可能必須將該約束添加到該窗口(保留一個ivar,以便以後可以將其刪除)。

在過去我玩過鍵盤,你可以看到它被添加到窗口中,它的框架偏移,因此它剛好低於屏幕底部(在iOS5中) - 它不在滾動視圖。

當鍵盤完成滾動時,您可以看到圖像視圖的滾動位置,確定偏移量,然後從圖像視圖切換回實際滾動視圖。

請注意,我做了這個快照,動畫,最後取代過去的視圖相當成功。你會花一些時間在這,也許它會工作,也許不會 - 但如果你把一個簡單的演示項目放在一起,你可以快速驗證,如果你可以在容器視圖中獲取imageView本身,以使用鍵盤輸入配件上的約束視圖。一旦這個工作,你可以做到「真實」。

編輯:正如湯姆斯威夫特指出的那樣,鍵盤位於另一個更高的「Z」級別的窗口,因此沒有辦法直接連接真正的鍵盤和用戶視圖之間的約束。

但是 - 在鍵盤通知中,我們可以得到它的大小,動畫持續時間,甚至動畫曲線。因此,當您獲得第一個鍵盤通知時,請創建一個新的透明視圖,並將其置於特殊「imageView」(快照)視圖的底部。使用鍵盤長度和曲線的UIView動畫,並且透明視圖將與鍵盤動畫完全一致 - 但在您的窗口中。將約束放在透明視圖上,並且應該能夠實現所需的確切行爲(在iOS6中)。真的,在這一點上支持iOS 5 - 對於尚未升級的10個人來說呢?!?!?。

如果你必須支持iOS5,並且你想要這個「凹凸」行爲,那麼計算何時動畫將達到它「擊中」你的textField的大小,並且當鍵盤開始移動時使用帶有延遲的UIView動畫,所以它不會馬上開始移動,但當它移動時,它會跟蹤鍵盤。

+0

啊,這很有趣:從佔位符inputAccessoryView和輸入字段thingy中添加一個約束。現在我想試試:) – TomSwift

+0

@TomSwift我只是希望真正的工作不會讓我無法自己去嘗試這件事 - 我明天在應用程序上有一個巨大的演示,沒時間玩。 YMMV :-) –

+0

非常好的主意!我會試一試,即使我必須支持iOS5,這個「問題」可以保留iOS5。:) –