22

我有一个固定在键盘顶部的文本字段。我不能使用 inputAccessoryView 因为它总是显示。我能够观察到键盘隐藏/显示通知以使用键盘上下移动,但这似乎不适用于 UIScrollViewKeyboardDismissModeInteractive。有没有办法获得关于键盘位置的持续反馈以同步动画?

4

8 回答 8

18

编辑:看起来这在 iOS 8 中不起作用,伙计们——对不起!我也在寻找新的解决方案

我通过创建一个不可见的 inputAccessoryView 解决了这个问题。

textView.inputAccessoryView = [[MJXObservingInputAccessoryView alloc] init];

附件视图观察其父视图的框架并发布您可以匹配的通知。

static NSString * const MJXObservingInputAccessoryViewSuperviewFrameDidChangeNotification = @"MJXObservingInputAccessoryViewSuperviewFrameDidChangeNotification";

@interface MJXObservingInputAccessoryView : UIView @end

@implementation MJXObservingInputAccessoryView

- (void)willMoveToSuperview:(UIView *)newSuperview
{
    if (self.superview)
    {
        [self.superview removeObserver:self
                            forKeyPath:@"frame"];
    }

    [newSuperview addObserver:self
                   forKeyPath:@"frame"
                      options:0
                      context:NULL];

    [super willMoveToSuperview:newSuperview];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (object == self.superview && [keyPath isEqualToString:@"frame"])
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:MJXObservingInputAccessoryViewSuperviewFrameDidChangeNotification
                                                            object:self];
    }
}

@end
于 2013-10-24T21:23:39.963 回答
16

我找到了一个解决方案(尽管有点破解),我实现了 scrollViewDidScroll 来收听 UITableView 中内置的 panGestureRecognizer。事实证明,即使手指在整个手势中滑动,键盘的顶部也保持完美,因此您可以不断更新您的文本字段以保持在平移手指的上方。

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {

    CGPoint fingerLocation = [scrollView.panGestureRecognizer locationInView:scrollView];
    CGPoint absoluteFingerLocation = [scrollView convertPoint:fingerLocation toView:self.view];

    if (_keyboardIsOpen && scrollView.panGestureRecognizer.state == UIGestureRecognizerStateChanged && absoluteFingerLocation.y >= (self.view.frame.size.height - _keyboardFrame.size.height)) {

        [UIView animateWithDuration:.05 animations:^{
            //This is an autolayout constraint that needs to be set to the distance between the top of the keyboard and the bottom of the screen (with a buffer of 3)
            _bottomViewVerticalSpacingConstraint.constant = [[UIScreen mainScreen] bounds].size.height - absoluteFingerLocation.y - 3;
            [self.view layoutIfNeeded];
        }];
    }
}

然后我也注册通知

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

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

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillChangeFrame:)
                                             name:UIKeyboardWillChangeFrame object:nil];

像这样处理它们

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

    [UIView animateWithDuration:.05 animations:^{
        _bottomViewVerticalSpacingConstraint.constant = kbSize.height;
        [self.view layoutIfNeeded];
    }];
}


-(void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    _keyboardIsOpen = NO;
    [UIView animateWithDuration:.3 animations:^{
        _bottomViewVerticalSpacingConstraint.constant = 0;
        [self.view layoutIfNeeded];
    }];
}

-(void)keyboardWillChangeFrame:(NSNotification*)aNotification {
    NSDictionary* info = [aNotification userInfo];
    _keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
}
于 2015-09-04T00:15:07.250 回答
7

Malcolm 的回答将适用于 iOS 8 和 7,只需稍作调整。我没有足够的声誉来评论他的帖子,因此将其添加为社区 wiki,供需要适用于 iOS 7 和 8 的解决方案的人们使用。

标题

#import <UIKit/UIKit.h>

static NSString *const SSObservingInputAccessoryViewFrameDidChangeNotification = @"SSObservingInputAccessoryViewFrameDidChangeNotification";

@interface SSObservingInputAccessoryView : UIView

@end

执行

#import "SSObservingInputAccessoryView.h"

@implementation SSObservingInputAccessoryView

- (void)willMoveToSuperview:(UIView *)newSuperview {
    if (self.superview) {
        [self.superview removeObserver:self
                            forKeyPath:@"center"];

        [self.superview removeObserver:self
                            forKeyPath:@"frame"];
    }

    [newSuperview addObserver:self
                   forKeyPath:@"center"
                      options:0
                      context:nil];

    [newSuperview addObserver:self
                   forKeyPath:@"frame"
                      options:0
                      context:nil];

    [super willMoveToSuperview:newSuperview];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (object == self.superview
        && ([keyPath isEqualToString:@"center"] || [keyPath isEqualToString:@"frame"])) {
        [[NSNotificationCenter defaultCenter] postNotificationName:SSObservingInputAccessoryViewFrameDidChangeNotification
                                                            object:self];
    }
}

@end
于 2015-01-27T16:42:15.890 回答
2

iPhone X iOS 11.2.2 上的 Swift 4 仅交互式更新 CALayer 位置:

class MyValueObservingView: UIView {
    static let CALayerPositionChangeNotification = Notification.Name("CALayerPositionChangeNotification")
    static let CALayerPositionUserInfoKey = "position"

    override func willMove(toSuperview newSuperview: UIView?) {
        superview?.layer.removeObserver(self, forKeyPath: type(of: self).CALayerPositionUserInfoKey)
        newSuperview?.layer.addObserver(self, forKeyPath: type(of: self).CALayerPositionUserInfoKey, options: [.initial, .new], context: nil)
        super.willMove(toSuperview: newSuperview)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == type(of: self).CALayerPositionUserInfoKey, let position = change?[.newKey] as? CGPoint {
//            print("MyValueObservingView layer position changed to \(position)")
            NotificationCenter.default.post(name: type(of: self).CALayerPositionChangeNotification, object: self, userInfo: [type(of: self).CALayerPositionUserInfoKey: position])
        } else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        }
    }
}
于 2018-01-23T03:45:43.030 回答
1

有一种更简单的方法可以将某些东西固定到键盘上。您只需要实现这些方法,iOS 就会为您处理。

- (UIView *) inputAccessoryView {
     // Return your textfield, buttons, etc
}

- (BOOL) canBecomeFirstResponder {
    return YES;
}

这是一个很好的教程,将其分解更多

于 2015-03-16T22:49:23.497 回答
0

你试过DAKeyboardControl吗?

   UIView *addCommentContainer = self.addCommentContainer;
   [self.view addKeyboardPanningWithActionHandler:^(CGRect keyboardFrameInView) {
      [addCommentContainer setY:keyboardFrameInView.origin.y - addCommentContainer.frame.size.height];
   }];

您可以在此控件上查看处理键盘框架的源代码。

于 2013-10-09T09:54:47.900 回答
0

JordanC 的解决方案有问题,但可能适用于某些用例。我在下面将该代码转换为swift。

class MyViewController: UIViewController {
    @IBOutlet var tableView: UITableView!
    @IBOutlet var bottomViewVerticalSpacingConstraint: NSLayoutConstraint!

    private(set) var keyboardIsOpen: Bool = false
    private(set) var keyboardFrame: CGRect = .zero

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.delegate = self
        self.tableView.keyboardDismissMode = .interactive
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame(notification:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    }
    @objc func keyboardWillShow(notification: NSNotification) {
        keyboardIsOpen = true
        guard let userInfo = notification.userInfo else { return }
        guard let keyboardSize = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size else { return }
        UIView.animate(withDuration: 0.05) {
            self.bottomViewVerticalSpacingConstraint.constant = keyboardSize.height
            self.view.layoutIfNeeded()
        }
    }
    @objc func keyboardWillHide(notification: NSNotification) {
        keyboardIsOpen = false
        UIView.animate(withDuration: 0.05) {
            self.bottomViewVerticalSpacingConstraint.constant = 0
            self.view.layoutIfNeeded()
        }
    }
    @objc func keyboardWillChangeFrame(notification: NSNotification) {
        keyboardIsOpen = false
        guard let userInfo = notification.userInfo else { return }
        guard let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return }
        self.keyboardFrame = keyboardFrame
    }
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let fingerLocation = scrollView.panGestureRecognizer.location(in: scrollView)
        let absoluteFingerLocation = scrollView.convert(fingerLocation, to: self.view)
        if keyboardIsOpen && scrollView.panGestureRecognizer.state == .changed && absoluteFingerLocation.y >= (self.view.frame.size.height - keyboardFrame.size.height) {
            UIView.animate(withDuration: 0.05) {
                self.bottomViewVerticalSpacingConstraint.constant = UIScreen.main.bounds.size.height - absoluteFingerLocation.y - 3
                self.view.layoutIfNeeded()
            }
        }
    }
}
于 2019-03-29T18:45:39.167 回答
-2

这对我有用:

注册键盘确实隐藏了通知:UIKeyboardDidHideNotification

viewDidLoad将工具栏添加到视图底部时,使用addSubview.

我使用了一个 textView,所以textViewShouldBeginEditing我设置了 inputAccessoryView。

然后在键盘中做了 hide 方法,调整工具栏的框架,将 inputAccessoryView 设置为 nil,重要的是,再次将工具栏添加为视图的子视图。

于 2013-10-31T15:59:06.497 回答