点击文本字段后动画视图重置位置

Posted

技术标签:

【中文标题】点击文本字段后动画视图重置位置【英文标题】:Animated views resetting position after tapping textfield 【发布时间】:2017-08-02 14:48:15 【问题描述】:

我没有故事板,一切都是程序化的。我有三个 TextField,其中一个是隐藏的(isHidden = true),在登录按钮后面,登录按钮下方是一个注册按钮。如果您点击注册按钮,登录按钮会滑到注册按钮下方,然后使用 layoutSubViews() 显示隐藏的 textField 并将 isHidden 设置为 false。

我的问题是,当点击任何文本字段时,loginButton 会移回其原始位置。我尝试将文本字段委托移动到视图控制器,在 willLayoutSubviews 和 didLayoutSubviews 中调用 setup(),但同样的事情仍然发生。

视图控制器:

class WelcomeScreenViewController: UIViewController 

    private var currentUser: SplitterUser? 
        didSet 
            let nextViewController = MyBillsViewController()
            nextViewController.currentUser = self.currentUser
            present(nextViewController, animated: true)
        
    

    // swiftlint:disable line_length
    private let titleLogoLabel = TitleLabelLogo(frame: CGRect.zero, accessID: AccesID.titleLogoLabel)
    private let emailTextField = SplitterTextField(frame: CGRect.zero, accessID: AccesID.emailTextField)
    private let passwordTextField = SplitterTextField(frame: CGRect.zero, accessID: AccesID.passwordTextField)
    private let confirmPasswordTextField = SplitterTextField(frame: CGRect.zero, accessID: AccesID.confirmPasswordTextField)
    private let loginButton = SplitterButton(frame: CGRect.zero, accessID: AccesID.loginButton)
    private let registerButton = SplitterButton(frame: CGRect.zero, accessID: AccesID.registerButton)
    // swiftlint:enable line_length

    override func viewDidLoad() 
        super.viewDidLoad()
        setup()
    

    private func setup() 
        view.backgroundColor = Color.mainBackground

        view.addSubview(titleLogoLabel)
        view.addSubview(emailTextField)
        view.addSubview(passwordTextField)
        view.addSubview(confirmPasswordTextField)
        view.addSubview(loginButton)
        view.addSubview(registerButton)

        applyCommonLayoutFeaturesToAllViews()
        placeTitleLogoLabel()
        placeEmailTextField()
        placePasswordTextField()
        placePasswordConfirmationTextField()
        placeLoginButton()
        placeRegisterButton()
        setupKeyboard()
    

    private func applyCommonLayoutFeaturesToAllViews() 
        view.subviews.forEach  subview in
            subview.pinToSuperview(edges: [.left, .right])
            subview.translatesAutoresizingMaskIntoConstraints = false
        
    

    private func placeTitleLogoLabel() 
        let titleLogoLabelY = view.frame.height/4.5
        titleLogoLabel.pinTop(to: view, constant: titleLogoLabelY)
        titleLogoLabel.addHeightConstraint(with: Layout.titleLogoTextHeight)
    

    private func placeEmailTextField() 
        emailTextField.centerYToSuperview()
        emailTextField.addHeightConstraint(with: Layout.textFieldHeight)
    

    private func placePasswordTextField() 
        passwordTextField.pinTop(to: emailTextField,
                                 constant: Layout.textFieldHeight + Layout.spacer,
                                 priority: .required,
                                 relatedBy: .equal)
        passwordTextField.addHeightConstraint(with: Layout.textFieldHeight)
    

    private func placePasswordConfirmationTextField() 
        confirmPasswordTextField.pinTop(to: passwordTextField,
                                        constant: Layout.textFieldHeight + Layout.spacer,
                                        priority: .required,
                                        relatedBy: .equal)
        confirmPasswordTextField.addHeightConstraint(with: Layout.textFieldHeight)
        confirmPasswordTextField.isHidden = true
    

    private func placeLoginButton() 
        loginButton.pinTop(to: passwordTextField,
                           constant: Layout.textFieldHeight + Layout.spacer,
                           priority: .required,
                           relatedBy: .equal)
        loginButton.addHeightConstraint(with: Layout.buttonHeight)
        loginButton.addTarget(self, action: #selector(loginButtonTapped), for: .touchUpInside)
    

    private func placeRegisterButton() 
        registerButton.pinTop(to: loginButton,
                              constant: Layout.buttonHeight + Layout.spacer,
                              priority: .required,
                              relatedBy: .equal)
        registerButton.addHeightConstraint(with: Layout.buttonHeight)
        registerButton.addTarget(self, action: #selector(registerButtonTapped), for: .touchUpInside)
    

    @objc private func registerButtonTapped() 
        if confirmPasswordTextField.isHidden 
            animateLoginButton()
         else 
            registerNewUser()
        
    

    @objc private func loginButtonTapped() 
        if !confirmPasswordTextField.isHidden 
            animateLoginButton()
            self.view.layoutSubviews()
         else 
            //segue to next vc
        
    

    private func animateLoginButton() 
        if confirmPasswordTextField.isHidden 
            moveLoginButtonDown()
         else 
            moveLoginButtonUp()
        
    

    private func moveLoginButtonDown() 
        //Move loginButton down revealing confirmationPasswordTextView behind it
        UIView.animate(withDuration: 0.3, animations: 
            self.loginButton.frame.origin.y += Layout.loginButtonYMovement
            self.confirmPasswordTextField.isHidden = false
        )
    

    private func moveLoginButtonUp() 
        //Move the loginButton up, when it has finished moving hide the confirmationPasswordTextView
        UIView.animate(withDuration: 0.3, animations: 
            self.loginButton.frame.origin.y -= Layout.loginButtonYMovement
        , completion:  _ in
            self.confirmPasswordTextField.isHidden = true
        )
    


extension UIViewController 
    func setupKeyboard() 
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(keyboardWillShow(sender:)),
                                               name: NSNotification.Name.UIKeyboardWillShow,
                                               object: nil)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(keyboardWillHide(sender:)),
                                               name: NSNotification.Name.UIKeyboardWillHide,
                                               object: nil)
    

    @objc private func keyboardWillShow(sender: NSNotification) 
        self.view.frame.origin.y = Layout.welcomeScreenKeyboardMovement
    

    @objc private func keyboardWillHide(sender: NSNotification) 
        self.view.frame.origin.y = 0
    

文本字段:

class SplitterTextField: UITextField, UITextFieldDelegate 

    var accessID: String!

    required init(frame: CGRect, accessID: String) 
        super.init(frame: frame)
        self.accessID = accessID
        setup()
    

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
    

    private func setup() 
        delegate = self
        backgroundColor = Color.textFieldBackground
        accessibilityIdentifier = accessID
        textAlignment = .center
        returnKeyType = .done
        placeholder = NSLocalizedString("\(accessID!)PlaceHolder", comment: "")
    

    func textFieldShouldReturn(_ textField: UITextField) -> Bool 
        resignFirstResponder()
        return true
    

我不知道还有什么可以尝试的。任何想法都会很棒。谢谢

【问题讨论】:

当按下登录按钮时,位置正在重置,然后动画你是什么意思? 没有按登录按钮和注册按钮,但当我在登录按钮移动后点击文本字段时,它会重置回起点 除了 textFieldShouldReturn 之外,您的代码中是否有任何文本字段的 edlegate 函数? 不,这是唯一的功能 你在layoutSubviews中有位置设置吗? 【参考方案1】:

添加一个您像往常一样键入WelcomeScreenViewController 的XIB 文件(因此没有故事板)。以图形方式创建自动布局约束(这更容易)。

在您的视图控制器代码中有一些这样的约束作为出口,然后您可以对其进行动画处理(根据需要与一些子视图的 alpha 一起)。

我知道这是一个完全不同的解决方案,但我认为你把事情弄得太复杂了。 UIViewController/XIB 对仍然是构建 UI 的一种非常好且简单的方法。

【讨论】:

任务的重点(我已经为自己设定)是不要使用它们,因为我工作的公司不使用任何图形界面。不过谢谢 我明白了。他们为什么不呢? 几个原因,我记得的一个原因是,当多个人在一个项目上工作时,有故事板等会使 github 变得复杂,并且由于您必须通过 IB 搜索而使解决问题变得更加困难。我认为 这确实是不使用 Storyboard 的一个很好的理由。由于这个原因和其他原因,我讨厌 Storyboard。但是他们为什么不为每个屏幕使用代码/XIB 对并自定义UIView?当您与多人在一起时,这一切都很好。 我不确定,因为我没有问。当我这样做时,我会告诉你,我可能是错的,他们确实这样做了,但我认为他们没有。【参考方案2】:

您已经对视图设置了约束以进行布局。自动布局使用约束来计算视图的框架。那你直接修改loginButton的框架:

self.loginButton.frame.origin.y += Layout.loginButtonYMovement

下次自动布局运行时,它会根据约束重置loginButton的框架。

要移动loginButton,您需要修改设置其框架的约束。因此,例如,您应该保存(在实例变量中)您在placeLoginButton 中创建的顶部约束。然后在moveLoginButtonDown中可以修改保存的约束常量,在动画块中调用self.view.layoutSubviews()

【讨论】:

我不太明白如何实现你所说的,你介意给我一点帮助吗?

以上是关于点击文本字段后动画视图重置位置的主要内容,如果未能解决你的问题,请参考以下文章

调用动画后 UIImage 视图重置

NavController 中的滚动视图在显示键盘后不会重置(Swift)

如何在滚动视图中重置文本字段

在 Swift 中添加子视图时如何阻止动画重置。?

编辑文本后如何重置tableview的高度

CABasicAnimation 在动画完成后重置为初始值