键盘inputAccessoryView中带有“上一个”和“下一个”的工具栏

23

我一直在尝试实现这个工具栏,当顶部的textField是第一个响应者时只启用“下一个”按钮,当底部的textField是第一个响应者时只启用“上一个”按钮。

它似乎有效,但每次我需要两次点击“上一个”/“下一个”按钮才能启用/禁用对立的按钮。

我是不是在响应链中漏掉了什么导致这种情况发生?

这是我的代码:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.topText becomeFirstResponder];
}


- (UIToolbar *)keyboardToolBar {

    UIToolbar *toolbar = [[UIToolbar alloc] init];
    [toolbar setBarStyle:UIBarStyleBlackTranslucent];
    [toolbar sizeToFit];

    UISegmentedControl *segControl = [[UISegmentedControl alloc] initWithItems:@[@"Previous", @"Next"]];
    [segControl setSegmentedControlStyle:UISegmentedControlStyleBar];
    segControl.momentary = YES;
    segControl.highlighted = YES;

    [segControl addTarget:self action:@selector(changeRow:) forControlEvents:(UIControlEventValueChanged)];
    [segControl setEnabled:NO forSegmentAtIndex:0];

    UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithCustomView:segControl];

    NSArray *itemsArray = @[nextButton];

    [toolbar setItems:itemsArray];

    return toolbar;
}

- (void)changeRow:(id)sender {

    int idx = [sender selectedSegmentIndex];

    if (idx == 1) {
        [sender setEnabled:NO forSegmentAtIndex:1];
        [sender setEnabled:YES forSegmentAtIndex:0];
        self.topText.text = @"Top one";
        [self.bottomText becomeFirstResponder];
    }
    else {
        [sender setEnabled:NO forSegmentAtIndex:0];
        [sender setEnabled:YES forSegmentAtIndex:1];
        self.bottomText.text =@"Bottom one";
        [self.topText becomeFirstResponder];
    }
}


-(void)textFieldDidBeginEditing:(UITextField *)textField {
    if (!textField.inputAccessoryView) {
        textField.inputAccessoryView = [self keyboardToolBar];
    }
}

2
你为什么要使用分段控件而不是两个按钮? - Simon Goldeen
1
一个 UISegmentedControl 绝对比两个按钮更加好看。 - Gabriele Petronella
7个回答

34

这是我使用的UIViewController扩展程序,每当我需要一组UITextField通过输入附件提供导航时使用。使用这种方法不需要使用UITextField代理,并且将行为添加到多个表单变得非常简单。还支持“完成”按钮以关闭。

extension UIViewController {
  func addInputAccessoryForTextFields(textFields: [UITextField], dismissable: Bool = true, previousNextable: Bool = false) {
    for (index, textField) in textFields.enumerated() {
      let toolbar: UIToolbar = UIToolbar()
      toolbar.sizeToFit()

      var items = [UIBarButtonItem]()
      if previousNextable {
        let previousButton = UIBarButtonItem(image: UIImage(named: "Backward Arrow"), style: .plain, target: nil, action: nil)
        previousButton.width = 30
        if textField == textFields.first {
          previousButton.isEnabled = false
        } else {
          previousButton.target = textFields[index - 1]
          previousButton.action = #selector(UITextField.becomeFirstResponder)
        }

        let nextButton = UIBarButtonItem(image: UIImage(named: "Forward Arrow"), style: .plain, target: nil, action: nil)
        nextButton.width = 30
        if textField == textFields.last {
          nextButton.isEnabled = false
        } else {
          nextButton.target = textFields[index + 1]
          nextButton.action = #selector(UITextField.becomeFirstResponder)
        }
        items.append(contentsOf: [previousButton, nextButton])
      }

      let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
      let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: view, action: #selector(UIView.endEditing))
      items.append(contentsOf: [spacer, doneButton])


      toolbar.setItems(items, animated: false)
      textField.inputAccessoryView = toolbar
    }
  }
}

例子:

let field1 = UITextField()
let field2 = UITextField()
addInputAccessoryForTextFields([field1, field2], dismissable: true, previousNextable: true)

这里是一个合理的箭头图标


很棒的解决方案! - Hayden Holligan
1
哥们儿,你真是个天才!比我在网上找到的其他例子简单多了...非常感谢!☺️ - bluefox
目前为止最好的解决方案 - Romit Kumar
很棒的解决方案!您能否提供如何在其他类(如代理)中访问上一个、下一个和完成按钮操作的方法? - Steve Gear
可能会使用这个。少数问题:可关闭(dismissable)没有任何作用,箭头图标是404。 - Nate

17

Swift:

lazy var inputToolbar: UIToolbar = {
    var toolbar = UIToolbar()
    toolbar.barStyle = .default
    toolbar.translucent = true
    toolbar.sizeToFit()

    var doneButton = UIBarButtonItem(title: "Done", style: .bordered, target: self, action: "inputToolbarDonePressed")
    var flexibleSpaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    var fixedSpaceButton = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)

    var nextButton  = UIBarButtonItem(image: UIImage(named: "keyboardPreviousButton"), style: .bordered, target: self, action: "keyboardNextButton")
    nextButton.width = 50.0
    var previousButton  = UIBarButtonItem(image: UIImage(named: "keyboardNextButton"), style: .Bordered, target: self, action: "keyboardPreviousButton")

    toolbar.setItems([fixedSpaceButton, nextButton, fixedSpaceButton, previousButton, flexibleSpaceButton, doneButton], animated: false)
    toolbar.userInteractionEnabled = true

    return toolbar
    }()

在UITextFieldDelegate中

 func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
    textField.inputAccessoryView = inputToolbar

    return true
}


11

好的,经过查看了精彩的BSKeyboardControls后,我尝试在textFieldDidBeginEditing中实现分段控件的启用和禁用,而不是我的@selector所在的地方。我还引入了一个变量来表示分段控件。现在它已经可以工作了。 这里是修改后的代码片段:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.topText becomeFirstResponder];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];

}

- (UIToolbar *)keyboardToolBar {

    UIToolbar *toolbar = [[UIToolbar alloc] init];
    [toolbar setBarStyle:UIBarStyleBlackTranslucent];
    [toolbar sizeToFit];

    self.segControl = [[UISegmentedControl alloc] initWithItems:@[@"Previous", @"Next"]];
    [self.segControl setSegmentedControlStyle:UISegmentedControlStyleBar];
    self.segControl.momentary = YES;

    [self.segControl addTarget:self action:@selector(changeRow:) forControlEvents:(UIControlEventValueChanged)];
    [self.segControl setEnabled:NO forSegmentAtIndex:0];

    UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithCustomView:self.segControl];

    NSArray *itemsArray = @[nextButton];

    [toolbar setItems:itemsArray];

    return toolbar;
}

- (void)changeRow:(id)sender {

    int idx = [sender selectedSegmentIndex];

    if (idx) {
        self.topText.text = @"Top one";
        [self.bottomText becomeFirstResponder];
    }
    else {
        self.bottomText.text =@"Bottom one";
        [self.topText becomeFirstResponder];
    }
}


-(void)textFieldDidBeginEditing:(UITextField *)textField {

    if (!textField.inputAccessoryView) {

        textField.inputAccessoryView = [self keyboardToolBar];
    }
    if (textField.tag) {

        [self.segControl setEnabled:NO forSegmentAtIndex:1];
        [self.segControl setEnabled:YES forSegmentAtIndex:0];
    }
}

7
我的建议是“不要重复造轮子”。
在键盘上添加“Prev”和“Next”按钮以在UITextView之间切换非常常见,您可以找到许多已准备好使用的良好实现。
例如,查看BSKeyboardControl

谢谢你的回复,Gabriele。我本以为只需要使用这几个方法就能简单地完成它,而不是导入一个(虽然很小的)实现。但我仍然看不出我做错了什么,这让我很烦恼。如果没有人提供更正意见(或者我在查看BSKeyboardControl时找到解决方法),我会将你标记为被接受的解决方案。 - BillySangster

3

更新至Swift 3.0

lazy var inputToolbar: UIToolbar = {
    var toolbar = UIToolbar()
    toolbar.barStyle = .default
    toolbar.isTranslucent = true
    toolbar.sizeToFit()

    var doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(ContactViewController.inputToolbarDonePressed))
    var flexibleSpaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    var fixedSpaceButton = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)

    var nextButton = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(ContactViewController.keyboardNextButton))
    var previousButton = UIBarButtonItem(title: "Previous", style: .plain, target: self, action: #selector(ContactViewController.keyboardPreviousButton))

    toolbar.setItems([fixedSpaceButton, previousButton, fixedSpaceButton, nextButton, flexibleSpaceButton, doneButton], animated: false)
    toolbar.isUserInteractionEnabled = true

    return toolbar
}()

然后:

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    textField.inputAccessoryView = inputToolbar
    return true
}

请记得将 "ContactViewController" 更改为您的视图控制器的名称。


2
您也可以尝试使用pod UITextField-Navigation

pod 'UITextField-Navigation'

它会为任何UITextField添加两个lazy-loaded属性nextTextFieldpreviousTextField。您只需要在接口构建器或编程中指定nextTextField即可,工具栏将自动添加到键盘中。

编程方式:

import UITextField_Navigation

let textField1 = UITextField()
let textField2 = UITextField()
textField1.nextTextField = textField2

assert(textField2 == textField1.nextTextField)
assert(textField1 == textField2.previousTextField)

在界面生成器中: 在界面生成器中连接下一个文本框 输入图像描述

嗨,如何自定义工具栏。 - Nikhil Pandey
你好,我相信你已经更新了库,但是GitHub上的文档页面和示例项目还没有更新。 - Nikhil Pandey
你的库非常棒,请更新GitHub页面,我可以请求您重新制作文档并更新示例项目吗? - Nikhil Pandey
这很奇怪,因为我在我的库源代码中没有留下任何打印信息。 - Thanh Pham
这就是为什么要求您检查的原因,可能还有一个剩余。 - Nikhil Pandey
显示剩余10条评论

1
请尝试这个:-
- (void)viewDidLoad
{
[super viewDidLoad];
[self.topText becomeFirstResponder];
}
- (UIToolbar *)keyboardToolBar {

UIToolbar *toolbar = [[UIToolbar alloc] init];
[toolbar setBarStyle:UIBarStyleBlackTranslucent];
[toolbar sizeToFit];

UISegmentedControl *segControl = [[UISegmentedControl alloc]       initWithItems:@[@"Previous", @"Next"]];
[segControl setSegmentedControlStyle:UISegmentedControlStyleBar];
segControl.momentary = YES;
segControl.highlighted = YES;

[segControl addTarget:self action:@selector(changeRow:)  forControlEvents:(UIControlEventValueChanged)];
[segControl setEnabled:NO forSegmentAtIndex:0];

UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithCustomView:segControl];

NSArray *itemsArray = @[nextButton];

[toolbar setItems:itemsArray];

int idx = [sender selectedSegmentIndex];

if (idx == 1) {
    [sender setEnabled:NO forSegmentAtIndex:1];
}
else {
    [sender setEnabled:NO forSegmentAtIndex:0];
}

return toolbar;
}

- (void)changeRow:(id)sender {


int idx = [sender selectedSegmentIndex];

if (idx == 1) {
    self.topText.text = @"Top one";
    [self.bottomText becomeFirstResponder];
}
else {
    self.bottomText.text =@"Bottom one";
    [self.topText becomeFirstResponder];
}
}


-(void)textFieldDidBeginEditing:(UITextField *)textField {
if (!textField.inputAccessoryView) {
    textField.inputAccessoryView = [self keyboardToolBar];
}
}

当键盘更改时,您不需要启用其他项目,因为它们会每次重新初始化....


你在 keyboardToolBar: 中如何获取发送者?它的 setSegmentedControlStyle 也已经被弃用了,有什么解决方案吗? - Sonic Master

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接