Toolbar with “Previous” and “Next” for Keyboard inputAccessoryView

那年仲夏 提交于 2019-12-17 22:23:25

问题


I've been trying to implement this toolbar, where only the 'Next' button is enabled when the top textField is the firstResponder and only the 'Previous' button is enabled when the bottom textField is the firstResponder.

It kind of works, but what keeps happening is I need to tap the 'Previous'/'Next' buttons twice each time to enable/disable the opposing button.

Am I missing something in the responder chain that's making this happen?

Here's my code:

- (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];
    }
}

回答1:


Here is a UIViewController extension that I use whenever I need a group of UITextFields to provide navigation via input accessory. No need to use UITextField delegation with this approach, and adding the behavior to multiple forms becomes a single-liner. Also supports the 'Done' button to dismiss.

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
    }
  }
}

example:

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

Here's a reasonable arrow icon.




回答2:


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
    }()

In UITextFieldDelegate

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

    return true
}




回答3:


Okay, after looking at the brilliant BSKeyboardControls, I tried implementing the enabling and disabling of the segmented control in textFieldDidBeginEditing, instead of where my @selector was. I also introduced a variable for the segmented control. It works now. Here's the amended code snippet:

- (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];
    }
}



回答4:


My suggestion here is "don't reinvent the wheel".

Having Prev and Next button over a keyboard for switching between UITextViews is so common that you can find many good implementations ready to use.

Check out BSKeyboardControl, for instance.




回答5:


You can also try the pod UITextField-Navigation:

pod 'UITextField-Navigation'

It adds two lazy-loaded properties nextTextField and previousTextField to any UITextField. All you need is to specify the nextTextField either on the interface builder or programmatically, and the toolbar is added to the keyboard for you.

Programmatically:

import UITextField_Navigation

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

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

On the Interface Builder:




回答6:


Updated for 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
}()

And then:

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

Remember to change thange "ContactViewController" to the name of your View Controller.




回答7:


Try this :-

- (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];
}
}

you dont need to enable other item as they reinitialise every time when keyboard changes ....



来源:https://stackoverflow.com/questions/14148276/toolbar-with-previous-and-next-for-keyboard-inputaccessoryview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!