在 UITextfield 中输入文本后以编程方式更改焦点

Programmatically change focus after input text in UITextfield

我有一个简单的视图,它有两个文本视图和一个按钮。所有控件都垂直放置。我可以使用遥控器从一个控件导航到另一个控件。但是,我正在尝试让以下场景正常工作:

  1. 用户启动应用程序(焦点在第一个文本字段上)
  2. 用户输入文本字段并使用全屏键盘,然后她点击 "Next" 按钮
  3. 应用程序显示第二个文本字段(全屏键盘),输入一些文本并点击 "Done" 按钮
  4. 应用离开全屏键盘并聚焦按钮

虽然第 1-3 步开箱即用,但以编程方式更改焦点 (4) 不起作用。这是一些代码:

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if(textField == self.textfieldB) {
        self.activeView = self.textfieldB;
        [self setNeedsFocusUpdate];
        [self updateFocusIfNeeded];
        return YES;
    } 
    return NO;
}

并且在 - (UIView *)preferredFocusedView 中,我正在 returning 存储在 self.activeView 中的任何内容(是的,它将 return 按钮实例)。所有方法都按预期调用,但焦点不会改变。我在这里做错了什么?

提前致谢!

根据 this developer forums thread 行为是故意的:

When dismissing a presented view controller, like a fullscreen keyboard, UIKit will automatically return focus back to where it was when that view controller was first presented, if that view is still focusable. This is so that focus automatically returns back to where the user would expect it to be. There is currently no mechanism for overriding this behavior.

在类似的情况下,我在从键盘屏幕返回后延迟切换了焦点。这是一个丑陋的 hack,我希望 UIKit 对此不要那么自以为是。

如果包含的 UIViewController 将 restoresFocusAfterTransition 设置为 false,则可以更改它

只需设置 restoresFocusAfterTransition = false,然后覆盖 preferredFocusEnvironments

是的,您可以通过这样做来实现第 4 步:

    @IBOutlet weak var loginButton: UIButton!
    var focusedView: UIView?

    override var preferredFocusedView: UIView? {
        return focusedView
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if textField == passwordTextField {
            focusedView = loginButton
        }
        return true
    }

所以 none 我遇到的建议确实对我有用,包括较新的 restoresFocusAfterTransition 被设置为 false。

除此之外,唯一可行的是在 6rchid 的回答基础上延迟考虑焦点引擎更新。

class EmailViewController: UIViewController {

    @IBOutlet weak var emailTextField: UITextField!
    @IBOutlet weak var submitButton: UIButton!

    private var focusedEnvironments: [UIFocusEnvironment] = [UIFocusEnvironment]()

    override var preferredFocusEnvironments: [UIFocusEnvironment] {
        return focusedEnvironments
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        restoresFocusAfterTransition = false

        focusedEnvironments = [emailTextField, submitButton]
    }

}

extension EmailViewController: UITextFieldDelegate {

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

        emailTextField.isEnabled = false
        focusedEnvironments = [submitEmailButton]

        submitButton.setNeedsFocusUpdate()

        UIView.transition(with: submitEmailButton, duration: 1.0, options: .transitionCrossDissolve, animations: {
            self.submitButton.isEnabled = true
        }) { (_) in
            // re-enable this once focus updates have completed
            self.emailTextField.isEnabled = true
        }
        return true
    }
}

如果 UITextField 在焦点更新完成之前保持启用状态,焦点似乎总是要重置到 UITextField。所以诀窍是禁用它,然后在需要时重新启用它。