InputAccessoryView停靠在底部

时间:2013-11-04 08:53:45

标签: ios uinavigationcontroller uitoolbar uikeyboard uiresponder

我正在努力实现与Apple的消息应用程序中底部文本输入栏类似的定位行为。

我尝试了很多方法,搜索得很高,也有很多类似的问题,但都没有令人满意。

指定:

  1. 视图底部有UIToolbar
  2. 当键盘出现并消失时,工具栏将跟随键盘
  3. 当键盘可见时,工具栏应保持在键盘顶部
  4. 当隐藏键盘时,工具栏会停留(“停靠”)在视图底部
  5. 建议的解决方案:

    手动设置工具栏动画以响应键盘外观通知

    此解决方案不符合第二个要求的特殊情况(当键盘出现并消失时,工具栏将跟随键盘):

    • 在iOS 7中,引入了UIScrollViewKeyboardDismissMode。它启用了用于解除键盘的交互式手势。当用户翻过键盘的顶部边缘时,键盘框架逐渐跟随。此解决方案无法容纳此行为,只是将工具栏搁置在其动画位置。

    此外,此解决方案也无法满足第三个要求的特殊情况(当键盘可见时,工具栏应保持在键盘顶部):

    • 旋转。该解决方案需要额外的,烦人的无关代码(我们将在下一个提议的解决方案中看到)旋转工具栏以响应设备旋转。

    此解决方案的另一个问题是:

    • 键盘高度。使用此解决方案,工具栏不会被假定为键盘高度的一部分,因此必须编写其他代码以支持正确插入内容。

    下一个提议的解决方案:

    使用UIResponder的{​​{1}}

    这个解决方案似乎是Apple打算支持这种行为的方式,因为它解决了手动设置工具栏动画的所有缺点。但是这个解决方案完全忽略了第四个要求(当键盘被隐藏时,工具栏保持(“停靠”)在视图的底部)。


    似乎解决方案是使用inputAccessoryView的{​​{1}},但不知何故使UIResponder不会移动到视图下方。我正在寻找干净的代码来实现这一目标。在其他地方有精心制作的,几乎是高贵的尝试,但如上所述,它们不符合要求。

    在我的特定情况下,我希望使用inputAccessoryView的工具栏,这会带来其他问题,因为这不是inputAccessoryView的预期行为。无论如何,我愿意介绍一些hacky修复来实现这一目标。

5 个答案:

答案 0 :(得分:56)

Jason Foreman(@threeve)刚刚展示了“解决方案”。在视图控制器上(是的,查看控制器)添加inputAccessoryView:并返回要在底部停靠的视图并使用键盘移动。它只是有效。视图实际上不需要在视图层次结构中,它将由视图控制器自动插入。

编辑:同时实现canBecomeFirstResponder并返回YES(如Max Seelemann所述)。 reloadInputViews也很方便。

答案 1 :(得分:31)

Jonathan Badeen提到的solution为我工作。这是来自Arik的代码,展示了如何实现它(这应该放在适当的视图控制器中):

    - (BOOL)canBecomeFirstResponder{

        return YES;

    }

    - (UIView *)inputAccessoryView{

        return self.inputAccessoryToolbar;  
     // where inputAccessoryToolbar is your keyboard accessory view

    }

答案 2 :(得分:9)

对于那些寻找Swift版本的人:

将您的工具栏(在我的情况下' myToolBar')连接到您的视图控制器。然后覆盖canBecomeFirstResponder方法并覆盖getter inputAccessoryView变量。另外,请不要忘记添加self.myToolBar.removeFromSuperview(),否则xcode会抱怨。

class ViewController: UIViewController {

    @IBOutlet var myToolBar: UIToolbar!

    override func canBecomeFirstResponder() -> Bool {
        return true
    }


    override var inputAccessoryView:UIView{
        get{
            return self.myToolBar
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.myToolBar.removeFromSuperview()
    }
}

答案 3 :(得分:6)

Slack的优秀人员提供了一个优秀,易于实施的开源解决方案: SlackTextViewController

以下是如何通过四个步骤实现带有停靠工具栏的视图:

  1. 将pod安装到项目中。 (http://cocoapods.org/?q=SlackTextViewController
  2. 如果您正在Swift中编写应用程序,请创建一个标头以在Swift代码与其Obj-C类之间架起桥梁。 Here's a nice quick walkthrough on that。 (一旦将课程翻译成Swift,任何人都希望就此进行合作,那么桥接标题是否有必要?)
  3. 创建一个继承自MessageViewController的{​​{1}},无需再编写任何代码:

    SLKTextViewController
  4. 在Storyboard中,创建一个继承自MessageViewController的视图控制器。
  5. 在设备或模拟器上测试应用程序,您将看到一个漂亮的停靠文本栏,当用户编写其他文本行时,该文本栏也会(作为奖励)扩展。
  6. 向Slack团队提供道具,以提取这样一个有用的吊舱。

答案 4 :(得分:2)

所以我知道这是一个老帖子,我不确定你是否解决了这个问题,但我找到了一种方法来实现这个目标。我相信inputAccessoryView中存在一个错误,但我创建了一个hacky解决方案来表现消息应用的方式。我想我提供了所有必要的代码来实现这个工作。我将尝试在不久的将来某个时间创建一个更合适的博客文章,并对我的发现进行更深入的描述。有任何问题,请告诉我。

@property(nonatomic,assign)BOOL isFirstKeyboard; //workaround for keyboard bug

@property(nonatomic,assign)BOOL isViewAppear;

@property(nonatomic,strong)ChatBarView *chatView; //custom chat bar view

@property(nonatomic,strong)UIView *footerPadView; //just to add some nice padding
////////////////////////////////////////////////////////////////////////////////////////////////////
//in the view did load
- (void)viewDidLoad
{
    //more app specific code...
    self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
    self.chatView.textView.inputAccessoryView = self.chatView;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
-(void)done
{
    self.isFirstKeyboard = YES;
    [self.chatView.textView becomeFirstResponder];
}
////////////////////////////////////////////////////////////////////////////////////////////////////
- (void) moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up
{
    if(!self.isViewAppear)
        return;
    //NSLog(@"keyboard is: %@", up ? @"UP" : @"Down");
    if(!up && !self.isFirstKeyboard)
        [self performSelector:@selector(done) withObject:nil afterDelay:0.01];
    else if(!up & self.isFirstKeyboard)
    {
        self.isFirstKeyboard = NO;
        [self.view addSubview:self.chatView];
        CGRect frame = self.chatView.frame;
        frame.origin.y = self.view.frame.size.height - self.chatView.frame.size.height;
        self.chatView.frame = frame;
    }
    else
    {
        NSDictionary* userInfo = [aNotification userInfo];
        NSTimeInterval animationDuration;
        UIViewAnimationCurve animationCurve;
        CGRect keyboardEndFrame;

        [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
        [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
        [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];

        // Animate up or down
        [UIView beginAnimations:nil context:nil];
        if(up)
            [UIView setAnimationDuration:0.2];
        else
            [UIView setAnimationDuration:0.3];
        [UIView setAnimationCurve:animationCurve];

        CGRect frame = self.footerPadView.frame;
        CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
        if (up)
            frame.size.height = keyboardFrame.size.height - self.chatView.frame.size.height;
        else
            frame.size.height = 0;
        self.footerPadView.frame = frame;
        self.tableView.tableFooterView = self.footerPadView;
        [UIView commitAnimations];
    }
}
////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)keyboardWillShow:(NSNotification *)aNotification {
    [self moveTextViewForKeyboard:aNotification up:YES];
}
////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)keyboardWillHide:(NSNotification *)aNotification
{
    [self moveTextViewForKeyboard:aNotification up:NO];
}
////////////////////////////////////////////////////////////////////////////////////////////////////