【问题标题】:Swift PresentViewController Dismisses KeyboardSwift PresentViewController 关闭键盘
【发布时间】:2015-10-14 23:03:15
【问题描述】:

我正在创建一个包含多个 UIViewControllers 的注册向导。

我目前已经设置好了,所以用户输入他的电子邮件,单击键盘上的“开始”按钮,然后下一个 UIViewController 从右侧滑入,用户将在其中输入他的姓名。但问题是,当我调用presentViewController 将下一个UIViewController 带入时,它会关闭键盘。

我希望键盘在切换ViewControllers 时始终保持打开状态。如果您查看 Facebook 的 iOS 应用程序,他们会在他们的注册页面上做我正在尝试做的事情。

任何帮助或建议将不胜感激。我读过一些关于使用覆盖窗口的文章,但不知道如何去做,因为我的注册向导中有多个 UIViewController

这是我在注册向导中的初始控制器:

class SignUpEmailViewController: UIViewController {
    var titleLabel = UILabel.newAutoLayoutView()
    var emailField = SignUpTextField(placeholder: "Enter your email address")
    var emailLabel = UILabel.newAutoLayoutView()
    var continueButton = SignUpContinueButton.newAutoLayoutView()
    var footerView = SignUpFooterView.newAutoLayoutView()

    let presentAnimationController = PushInFromLeftAnimationController()
    let dismissAnimationController = PushInFromRightAnimationController()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        setupGestures()
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        emailField.becomeFirstResponder()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func setupViews() {
        view.backgroundColor = UIColor.colorFromCode(0xe9eaed)

        titleLabel.text = "Get Started"
        titleLabel.font = UIFont(name: "AvenirNextLTPro-Demi", size: 18)
        titleLabel.textColor = Application.greenColor

        emailField.enablesReturnKeyAutomatically = true
        emailField.returnKeyType = .Go
        emailField.delegate = self

        emailLabel.text = "You'll use this email when you log in and if you ever need to reset your password."
        emailLabel.font = UIFont(name: "AvenirNextLTPro-Regular", size: 13)
        emailLabel.textColor = .colorFromCode(0x4e5665)
        emailLabel.numberOfLines = 0
        emailLabel.textAlignment = .Center

        continueButton.addTarget(self, action: "continueButtonPressed", forControlEvents: .TouchUpInside)
        continueButton.hidden = true

        view.addSubview(titleLabel)
        view.addSubview(emailField)
        view.addSubview(emailLabel)
        view.addSubview(continueButton)
        view.addSubview(footerView)
        setupConstraints()
    }

    func setupGestures() {
        let gestureRecognizer = UISwipeGestureRecognizer(target: self, action: "swipeHandler")
        gestureRecognizer.direction = .Down
        view.addGestureRecognizer(gestureRecognizer)

        let tapGesture = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
        view.addGestureRecognizer(tapGesture)
    }

    func setupConstraints() {
        titleLabel.autoAlignAxisToSuperviewAxis(.Vertical)
        titleLabel.autoPinEdgeToSuperviewEdge(.Top, withInset: screenSize.height * 0.2)

        emailField.autoAlignAxisToSuperviewAxis(.Vertical)
        emailField.autoPinEdge(.Top, toEdge: .Bottom, ofView: titleLabel, withOffset: 15)
        emailField.autoSetDimensionsToSize(CGSize(width: screenSize.width * 0.85, height: 40))

        emailLabel.autoAlignAxisToSuperviewAxis(.Vertical)
        emailLabel.autoPinEdge(.Top, toEdge: .Bottom, ofView: emailField, withOffset: 10)
        emailLabel.autoSetDimension(.Width, toSize: screenSize.width * 0.85)

        continueButton.autoAlignAxisToSuperviewAxis(.Vertical)
        continueButton.autoPinEdge(.Top, toEdge: .Bottom, ofView: emailField, withOffset: 10)
        continueButton.autoSetDimensionsToSize(CGSize(width: screenSize.width * 0.85, height: 30))

        footerView.autoSetDimension(.Height, toSize: 44)
        footerView.autoPinEdgeToSuperviewEdge(.Bottom)
        footerView.autoPinEdgeToSuperviewEdge(.Leading)
        footerView.autoPinEdgeToSuperviewEdge(.Trailing)
    }

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

    func swipeHandler() {
        dismissViewControllerAnimated(true, completion: nil)
    }

    func continueButtonPressed() {
        presentNextViewController()
    }

    func dismissKeyboard() {
        view.endEditing(true)
    }

    func presentNextViewController() {
        let toViewController = SignUpNameViewController()
        toViewController.transitioningDelegate = self
        toViewController.firstNameField.becomeFirstResponder()
        presentViewController(toViewController, animated: true, completion: nil)
    }
}

extension SignUpEmailViewController: UIViewControllerTransitioningDelegate {
    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return presentAnimationController
    }

    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return dismissAnimationController
    }
}

extension SignUpEmailViewController: UITextFieldDelegate {
    func textFieldShouldReturn(textField: UITextField) -> Bool {
        presentNextViewController()
        return true
    }

    func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
        continueButton.hidden = true
        emailLabel.hidden = false
        return true
    }

    func textFieldShouldEndEditing(textField: UITextField) -> Bool {
        continueButton.hidden = false
        emailLabel.hidden = true
        return true
    }
}

这是我要展示的控制器:

class SignUpNameViewController: UIViewController, UIViewControllerTransitioningDelegate {
    var titleLabel = UILabel.newAutoLayoutView()
    var textFieldContainer = UIView.newAutoLayoutView()
    var firstNameField = SignUpTextField(placeholder: "First name")
    var lastNameField = SignUpTextField(placeholder: "Last name")
    var continueButton = SignUpContinueButton.newAutoLayoutView()
    var footerView = SignUpFooterView.newAutoLayoutView()

    let presentAnimationController = PushInFromLeftAnimationController()
    let dismissAnimationController = PushInFromRightAnimationController()

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        firstNameField.becomeFirstResponder()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        setupGestures()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func setupViews() {
        view.backgroundColor = UIColor.colorFromCode(0xe9eaed)

        titleLabel.text = "What's your name?"
        titleLabel.font = UIFont(name: "AvenirNextLTPro-Demi", size: 18)
        titleLabel.textColor = Application.greenColor

        firstNameField.returnKeyType = .Next
        firstNameField.enablesReturnKeyAutomatically = true

        lastNameField.returnKeyType = .Next
        lastNameField.enablesReturnKeyAutomatically = true

        continueButton.addTarget(self, action: "continueButtonPressed", forControlEvents: .TouchUpInside)

        view.addSubview(titleLabel)
        view.addSubview(textFieldContainer)
        textFieldContainer.addSubview(firstNameField)
        textFieldContainer.addSubview(lastNameField)
        view.addSubview(continueButton)
        view.addSubview(footerView)
        setupConstraints()
    }

    func setupGestures() {
        let gestureRecognizer = UISwipeGestureRecognizer(target: self, action: "swipeHandler")
        gestureRecognizer.direction = .Right
        view.addGestureRecognizer(gestureRecognizer)

        let tapGesture = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
        view.addGestureRecognizer(tapGesture)
    }

    func setupConstraints() {
        titleLabel.autoAlignAxisToSuperviewAxis(.Vertical)
        titleLabel.autoPinEdgeToSuperviewEdge(.Top, withInset: screenSize.height * 0.2)

        textFieldContainer.autoPinEdge(.Top, toEdge: .Bottom, ofView: titleLabel, withOffset: 15)
        textFieldContainer.autoAlignAxisToSuperviewAxis(.Vertical)
        textFieldContainer.autoSetDimensionsToSize(CGSize(width: screenSize.width * 0.8, height: 40))

        let spaceBetweenTextFields: CGFloat = 5
        let textFieldSize = ((screenSize.width * 0.8) - spaceBetweenTextFields) / 2
        let textFields: NSArray = [firstNameField, lastNameField]
        textFields.autoDistributeViewsAlongAxis(.Horizontal, alignedTo: .Horizontal, withFixedSize: textFieldSize, insetSpacing: false)

        firstNameField.autoPinEdgeToSuperviewEdge(.Top)
        firstNameField.autoPinEdgeToSuperviewEdge(.Bottom)

        lastNameField.autoPinEdgeToSuperviewEdge(.Top)
        lastNameField.autoPinEdgeToSuperviewEdge(.Bottom)

        continueButton.autoAlignAxisToSuperviewAxis(.Vertical)
        continueButton.autoPinEdge(.Top, toEdge: .Bottom, ofView: textFieldContainer, withOffset: 10)
        continueButton.autoSetDimensionsToSize(CGSize(width: screenSize.width * 0.8, height: 30))

        footerView.autoSetDimension(.Height, toSize: 44)
        footerView.autoPinEdgeToSuperviewEdge(.Bottom)
        footerView.autoPinEdgeToSuperviewEdge(.Leading)
        footerView.autoPinEdgeToSuperviewEdge(.Trailing)
    }

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

    func dismissKeyboard() {
        view.endEditing(true)
    }

    func swipeHandler() {
        dismissViewControllerAnimated(true, completion: nil)
    }

    func continueButtonPressed() {
        let toViewController = SignUpPasswordViewController()
        toViewController.transitioningDelegate = self
        presentViewController(toViewController, animated: true, completion: {})
    }

    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return presentAnimationController
    }

    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return dismissAnimationController
    }
}

这是我的自定义过渡:

class PushInFromLeftAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
        return 0.35
    }

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
        let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
        let finalFrameForVC = transitionContext.finalFrameForViewController(toViewController)
        let containerView = transitionContext.containerView()
        let bounds = UIScreen.mainScreen().bounds
        toViewController.view.frame = CGRectOffset(finalFrameForVC, bounds.size.width, 0)
        containerView!.addSubview(toViewController.view)

        UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: {
            fromViewController.view.frame = CGRectOffset(finalFrameForVC, -bounds.size.width, 0)
            toViewController.view.frame = finalFrameForVC
            }, completion: {
                finished in
                transitionContext.completeTransition(true)
        })
    }
}

【问题讨论】:

  • 在这个问题上查看我的 cmets:stackoverflow.com/q/33127863/341994
  • 嘿@matt,我检查了你的答案并更新了我的问题以显示我的代码。知道当他们在注册向导中从 ViewController 切换到 ViewController 时,facebook 是如何保持键盘打开的吗?也许他们实际上并没有切换视图控制器,而是只是动画 TextFields 滑入?
  • 或者是向获得第一响应者角色的窗口添加 UITextField 的唯一解决方案?这似乎有点乱
  • 问题是,正如我们在其他 cmets 中讨论的那样,只有视图层次结构中的视图才能成为第一响应者。也许您可以通过自定义过渡动画来解决这个问题,因为这样您就可以完全控制整个过程中的视图。实际上,看起来您可能已经在这样做了。
  • 我也是这么想的@matt。我更新了我的问题以显示我的自定义转换。我从来没有从视图层次结构中删除 fromViewController 的视图,我只是将它滑出屏幕,这就是为什么我不确定为什么键盘仍然被关闭的原因。

标签: ios swift uiviewcontroller wizard presentviewcontroller


【解决方案1】:

我认为键盘被关闭是因为相应的 UIResponder 将在 ViewController 消失的同时辞职。

您是否尝试过将下一个 UITextField(在下一个 ViewController 中)设置为第一响应者?因此键盘将在前一个结束编辑之前链接到一个新的 UIResponder...

【讨论】:

  • 是的,我在调用 presentViewController() 之前尝试设置 toViewController.firstNameField.becomeFirstResponder(),但发生的情况是键盘向下移动有点像它正在辞职,然后又回到原来的位置
  • 嗯...是的,我期待这样的事情...我想知道通过明智地选择操作发生的顺序可以避免多少这种情况...因为我是另一个解决方案记住有点难看:在键盘后面有一个临时的 uitextfield,它在视图控制器进出时扮演第一响应者的角色。
  • 是的,我开始认为这可能是唯一的解决方案
  • 如何添加临时文本字段?我会将它添加为 UIWindow() 对象的子视图吗?
猜你喜欢
  • 2018-10-07
  • 2020-11-10
  • 2014-09-22
  • 1970-01-01
  • 2015-09-19
  • 1970-01-01
  • 2015-05-25
  • 2013-01-08
相关资源
最近更新 更多