a

Câu hỏi Animate UIView dọc theo bàn phím xuất hiện hình động


tôi đang dùng UIKeyboardWillShowNotification và UIKeyboardWillHideNotification để tạo hiệu ứng một chế độ xem dọc theo bàn phím, hãy xuất hiện hoạt ảnh bằng UIKeyboardAnimationDurationUserInfoKey, UIKeyboardAnimationCurveUserInfoKey và UIKeyboardFrameEndUserInfoKey.

Tất cả mọi thứ hoạt động tốt, miễn là các yếu tố bắt đầu vị trí là ở dưới cùng của màn hình. Phần tử của tôi (hộp đầu vào trong ảnh chụp màn hình) bắt đầu trên UITabBarController, vì vậy nếu hoạt ảnh của tôi bắt đầu có khoảng cách giữa bàn phím và UITextField, nó co lại dọc theo hình động, cho đến khi nó kết thúc.

Những gì tôi đang tìm kiếm là một cái gì đó như: "Animate với đường cong hình ảnh động tương tự, nhưng bắt đầu di chuyển, nếu bàn phím đạt đến vị trí maxY của tôi".

Nếu tôi sẽ thêm một sự chậm trễ để bắt đầu các hình ảnh động nó sẽ không chính xác với việc nới lỏng và điều này có thể phá vỡ trong phiên bản iOS trong tương lai.

Nó sẽ là tuyệt vời nếu bạn chia sẻ ý tưởng của bạn với tôi. :-)

Keyboard closed Keyboard opened Keyboard animating (see the gap


15
2017-10-31 14:52


gốc


Nếu bất cứ ai sử dụng các ràng buộc ... có thể là giải pháp này sẽ giúp họ ....stackoverflow.com/questions/31356293/… - EI Captain v2.0


Các câu trả lời:


Thường có hai cách tiếp cận bạn có thể sử dụng để giữ một chế độ xem phía trên bàn phím khi nó hoạt hình vào vị trí. Như bạn biết, đầu tiên là lắng nghe UIKeyboardWillShowNotification và sử dụng giá trị thời gian / đường cong / khung đi kèm trong userData để giúp bạn định vị và tạo hiệu ứng cho chế độ xem của bạn phía trên bàn phím.

Cách tiếp cận thứ hai là cung cấp inputAccessoryView cho chế độ xem (UITextField, ở đây) đang gọi bàn phím. (Tôi nhận ra điều này sẽ không cung cấp hiệu ứng bạn đang yêu cầu, đó là "đẩy" thanh công cụ / textfield lên một khi bàn phím chạy vào nó. Nhưng nhiều hơn về điều này sau.)  iOS sẽ cha mẹ inputAccessoryView của bạn để xem mà cũng cha mẹ bàn phím và animate chúng lại với nhau. Theo kinh nghiệm của tôi, điều này cung cấp hình ảnh động đẹp nhất. Tôi không nghĩ mình từng có hoàn hảo hoạt ảnh bằng cách sử dụng UIKeyboardWillShowNotification cách tiếp cận, đặc biệt là bây giờ trong iOS7, nơi có một thư bị trả lại nhỏ ở cuối hoạt ảnh bàn phím. Có lẽ là một cách với UIKit Dynamics để áp dụng thư bị trả lại này cho chế độ xem của bạn, nhưng làm cho nó hoàn toàn đồng bộ với bàn phím sẽ khó.

Đây là những gì tôi đã làm trong quá khứ cho một kịch bản tương tự như của bạn: có vị trí dưới cùng UIToolbar có một UITextField trong một mục nút thanh tùy chỉnh cho đầu vào. Trong trường hợp của bạn, vị trí này được đặt ở trên UITabBar. Các ITextField có một tùy chỉnh inputAccessoryView thiết lập, đó là khác  UIToolbar với khác  UITextField.

Khi người dùng chạm vào trường văn bản và nó trở thành người trả lời đầu tiên, bàn phím sẽ hoạt hình vào vị trí với thanh công cụ / textfield thứ 2 cùng với nó (và quá trình chuyển đổi này trông rất đẹp!). Khi chúng ta nhận thấy điều này xảy ra, chúng ta chuyển đổi firstResponder từ trường văn bản đầu tiên sang trường thứ hai sao cho nó có dấu nháy khi bàn phím được đặt đúng vị trí.

Bí quyết là phải làm gì khi bạn xác định thời gian của nó để kết thúc chỉnh sửa. Trước tiên, bạn phải từ chứcFirstResponder trên trường văn bản thứ hai, nhưng nếu bạn không cẩn thận thì hệ thống sẽ chuyển trạng thái trả lời đầu tiên trở lại trường văn bản gốc! Vì vậy, bạn phải ngăn chặn điều đó, bởi vì nếu không bạn sẽ được trong một vòng lặp vô hạn của đi về phía trước trả lời đầu tiên, và bàn phím sẽ không bao giờ bỏ qua. Thứ hai, bạn cần phải phản chiếu bất kỳ đầu vào văn bản nào vào trường văn bản thứ hai trở lại trường văn bản đầu tiên.

Đây là mã cho phương pháp này:

@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

Có một khả năng cuối cùng mà tôi có thể nghĩ đến. Cách tiếp cận trên có nhược điểm của hai thanh công cụ / textfield riêng biệt. Những gì bạn lý tưởng muốn chỉ là một bộ trong số này, và bạn muốn nó xuất hiện mà bàn phím "đẩy" chúng lên (hoặc kéo chúng xuống). Trong thực tế, hoạt hình đủ nhanh khiến tôi không nghĩ rằng hầu hết mọi người sẽ nhận thấy có hai bộ cho cách tiếp cận trên, nhưng có thể bạn không thích điều đó ..

Cách tiếp cận cuối cùng này lắng nghe bàn phím hiển thị / ẩn và sử dụng CADisplayLink để đồng bộ hóa hoạt ảnh trên thanh công cụ / textfield khi nó phát hiện các thay đổi ở vị trí bàn phím trong thời gian thực. Trong các thử nghiệm của tôi có vẻ khá tốt. Nhược điểm chính mà tôi thấy là vị trí của thanh công cụ bị trễ một chút. Tôi đang sử dụng bố cục tự động và thay đổi sang định vị khung truyền thống có thể nhanh hơn. Một hạn chế khác là có sự phụ thuộc vào hệ thống phân cấp khung nhìn bàn phím không thay đổi đáng kể. Đây có lẽ là rủi ro lớn nhất.

Có một mẹo khác với điều này. Thanh công cụ được đặt trong bảng phân cảnh của tôi bằng cách sử dụng các ràng buộc. Có hai ràng buộc cho khoảng cách từ dưới cùng của khung nhìn. Một là gắn với IBOutlet "_toolbarBottomDistanceConstraint", và đây là những gì mã sử dụng để di chuyển thanh công cụ. Ràng buộc này là một ràng buộc "không gian dọc" với một mối quan hệ "bình đẳng". Tôi đặt mức độ ưu tiên là 500. Có một giới hạn "không gian dọc" song song thứ hai với mối quan hệ "Lớn hơn hoặc bằng". Hằng số này là khoảng cách tối thiểu đến đáy của khung nhìn (phía trên thanh tab của bạn), và mức độ ưu tiên là 1000. Với hai ràng buộc này, tôi có thể đặt các thanh công cụ khoảng cách từ dưới sang bất kỳ giá trị nào tôi giống như, nhưng nó sẽ không bao giờ giảm xuống dưới giá trị tối thiểu của tôi. Đây là chìa khóa để làm cho nó xuất hiện rằng bàn phím đang đẩy / kéo thanh công cụ, nhưng có nó "thả" các hình ảnh động tại một điểm nhất định.

Cuối cùng, có lẽ bạn có thể kết hợp phương pháp này với những gì bạn đã có: sử dụng gọi lại CADisplayLink để phát hiện khi bàn phím "chạy vào" thanh công cụ của bạn, sau đó thay vì định vị thanh công cụ theo cách thủ công cho phần còn lại của hoạt ảnh, sử dụng hoạt ảnh UIView thực để tạo hiệu ứng cho thanh công cụ của bạn vào vị trí. Bạn có thể đặt thời lượng là thời lượng hiển thị trên bàn phím-hoạt ảnh trừ đi thời gian đã trôi qua.

@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

Đây là cấu trúc phân cấp và ràng buộc của khung nhìn:

enter image description here


14
2017-11-06 20:56



Tôi đã thử áp dụng cách tiếp cận mà bạn mô tả lần đầu trong iOS7. Mặc dù tôi có thể để con trỏ xuất hiện trong chế độ xem văn bản phụ kiện đầu vào, đầu vào vẫn sẽ chuyển sang chế độ xem văn bản gốc (những gì bạn gọi là _textfield). Nếu tôi buộc phải từ chức như người trả lời, bàn phím sẽ biến mất. Bất kỳ ý tưởng? - pickwick
@pickwick - có vẻ như đại biểu cho văn bản thứ 2 không được nối kết. Kiểm tra xem cả hai trường văn bản có cùng một đại biểu hay không. - TomSwift
cảm ơn sự giúp đỡ của bạn, nhưng tiếc là đó không phải là nó. Trong mọi trường hợp, tôi sẽ nghĩ rằng tôi nên vẫn nhìn thấy văn bản trong textfield thứ 2, bất kể đó là đại biểu. Tôi sẽ tiếp tục chơi ... - pickwick
Trong iOS, nhiệm vụ nhỏ này là khoa học tên lửa, bạn bè của tôi. - Josh


Tôi không có một giải pháp chi tiết như Tom cung cấp, nhưng tôi có một ý tưởng bạn có thể chơi xung quanh. Tôi đã làm rất nhiều điều thú vị với bố cục tự động và các ràng buộc, và bạn có thể làm một số điều tuyệt vời. Lưu ý rằng bạn không thể hạn chế các mục trong một dạng xem cuộn với những thứ nằm trong một.

Vì vậy, bạn có quan điểm chính của bạn, tôi giả sử của nó một cái nhìn bảng hoặc một số xem khác bên trong một scrollView, vì vậy bạn phải đối phó với điều đó. Cách tôi đề nghị là chụp nhanh chế độ xem, lưu chế độ xem hiện tại trong bảng trắng (bảng của bạn) và thay thế bằng chế độ xem "vùng chứa rất cao" được neo ở phía dưới, đặt UIImageView chứa ảnh chụp nhanh vào quan điểm này, với một ràng buộc giữa nó và khung nhìn container của hằng số = 0. Để người dùng không có gì thay đổi.

Trong "inputAccessoryView", khi khung nhìn được thêm vào superView (và khi có thuộc tính cửa sổ), bạn có thể loại bỏ ràng buộc giữa hình ảnh và khung nhìn vùng chứa, và thêm một cái mới hạn chế đáy của trường văn bản lên đầu inputAccessoryView của bạn, nơi khoảng cách phải lớn hơn một số giá trị. Bạn phải chơi xung quanh để có được giá trị, vì nó sẽ được bù đắp của textField đó trong scrollView của bạn điều chỉnh cho bất kỳ contentValue. Sau đó, bạn rất có thể sẽ phải thêm ràng buộc đó vào cửa sổ (giữ một ngà voi với nó để bạn có thể loại bỏ nó sau này).

Trong quá khứ tôi chơi xung quanh với bàn phím, và bạn có thể thấy rằng nó được thêm vào cửa sổ, với khung hình của nó bù đắp để nó ngay dưới dưới cùng của màn hình (là trong iOS5) - nó không có trong một scrollView.

Khi bàn phím kết thúc cuộn, bạn có thể thấy nơi xem hình ảnh đã cuộn, xác định độ lệch, sau đó thực hiện chuyển đổi từ chế độ xem hình ảnh sang chế độ xem cuộn thực của bạn.

Lưu ý rằng tôi đã làm ảnh chụp nhanh này, animate, cuối cùng thay thế lượt xem trong quá khứ khá thành công. Bạn sẽ dành thời gian cho việc này, và có thể nó sẽ hoạt động và có thể không - nhưng nếu bạn kết hợp một dự án demo đơn giản, bạn có thể xác minh nhanh chóng nếu bạn có thể có được một imageView trong chế độ xem vùng chứa để di chuyển bằng các ràng buộc trên phụ kiện đầu vào bàn phím lượt xem. Một khi công trình đó bạn có thể làm điều đó "thật".

EDIT: Như Tom Swift đã chỉ ra, bàn phím nằm trong một cửa sổ khác ở mức "Z" cao hơn, và do đó không có cách nào để kết nối trực tiếp một ràng buộc giữa bàn phím thực và chế độ xem của người dùng.

Tuy nhiên - trong thông báo trên bàn phím, chúng tôi có thể nhận được kích thước của nó, thời lượng hoạt ảnh, thậm chí là đường cong hoạt hình. Vì vậy, khi bạn nhận được thông báo bàn phím đầu tiên, hãy tạo chế độ xem trong suốt mới và đặt nó ở vị trí trên cùng ở dưới cùng của chế độ xem "imageView" (ảnh chụp) đặc biệt của bạn. Sử dụng hoạt ảnh UIView về chiều dài và đường cong của bàn phím và chế độ xem trong suốt của bạn sẽ hoạt ảnh chính xác như bàn phím là hoạt ảnh - nhưng trong cửa sổ của bạn. Đặt các ràng buộc trên khung nhìn trong suốt, và bạn sẽ có thể đạt được hành vi chính xác mà bạn muốn (trong iOS6). Thực sự, hỗ trợ iOS 5 vào thời điểm này - cho 10 người chưa nâng cấp chưa?!?!?.

Nếu bạn phải hỗ trợ iOS5, và bạn muốn hành vi "bump" này, thì tính toán khi hoạt ảnh sẽ đạt đến kích thước mà nó "chạm" textField của bạn và khi bàn phím bắt đầu di chuyển, hãy sử dụng hoạt ảnh UIView với độ trễ, để nó không hoạt động 't bắt đầu di chuyển ngay lập tức, nhưng khi nó di chuyển, nó theo dõi bàn phím.


1
2017-11-06 22:29



Ah, điều đó thật thú vị: Thêm một ràng buộc từ inputAccessoryView giữ chỗ và trường đầu vào thingy. Bây giờ tôi muốn thử điều này :) - TomSwift
@TomSwift Tôi chỉ ước rằng công việc thực sự đã không ngăn tôi thử bản thân mình - Tôi có một bản demo lớn về một ứng dụng vào ngày mai và không có thời gian để chơi. YMMV :-) - David H
Ý tưởng rất hay! Tôi sẽ cung cấp cho nó một thử, mặc dù tôi có để hỗ trợ iOS5 này "vấn đề" là ok để giữ cho iOS5. :) - Sascha Hameister
Tôi không chắc chắn điều này sẽ làm việc. Vấn đề là bàn phím (và inputAccessoryView của nó) nằm trong một cửa sổ khác với trường nhập. Để thêm một ràng buộc giữa hai khung nhìn, cần phải có một khung nhìn tổ tiên chung, và trong trường hợp này không có sự ràng buộc nào. Suy nghĩ? - TomSwift
@TomSwift - thực sự, bàn phím nằm trong một cửa sổ khác! Tôi không nhớ rằng trường hợp trong iOS5. Nhưng có, bạn là chính xác - nếu hai cửa sổ không có hy vọng cho những gì tôi đề nghị để làm việc. Thở dài. - David H