通过 UiscrollView 快速滚动到 UITextField 仅作为第一次错误

Posted

技术标签:

【中文标题】通过 UiscrollView 快速滚动到 UITextField 仅作为第一次错误【英文标题】:Swift Scroll to UITextField via UiscrollView only acts first time bug 【发布时间】:2017-10-14 15:58:12 【问题描述】:

我在这里发布我的完整代码。问题是最初加载应用程序和视图控制器时。它完全有效。点击两个文本字段,滚动视图向上推,键盘位于当前文本字段下方。但是,如果点击文本字段.. 向上移动视图并在文本字段上重复,它就不再这样做了。此外,如果通过导航控制器返回然后再次加载此视图控制器,它不会做任何事情。它不再滚动了..(不再向上推动文本字段并且键盘在它下方)...

import UIKit
import Parse
import Alamofire
import SwiftyJSON

class VCreservacion: UIViewController,UITextFieldDelegate,UIScrollViewDelegate 

    var SUCURSALID = 0
    var EMP_NOMBRE = ""
    var DIRECCION = ""
    var PROVINCIA = ""
    var RESTID = 20556

    @IBOutlet var lbl_empresa: UILabel!
    @IBOutlet var lbl_direccion: UILabel!
    @IBOutlet var lbl_step: UILabel!
    @IBOutlet var cantidadView: UIView!
    @IBOutlet var datePicker: UIDatePicker!
    @IBOutlet var btn_reservar: UIButton!
    @IBOutlet var stackView: UIStackView!
    @IBOutlet var scrollView: UIScrollView!
    @IBOutlet var txtComentario: UITextField!
    @IBOutlet weak var txtCelular: UITextField!
    var activeField: UITextField?

    var steps = 2


    // MARK: RESERVE ACTION
    @IBAction func ReserveAction(_ sender: UIButton) 
        print("Reservando...")

        // For date formater
        var dateformated = ""
        var dateformated2 = ""
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        //formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) as TimeZone!


        dateformated = formatter.string(from: datePicker.clampedDate)
        dateformated2 = formatter.string(from: datePicker.date)
        print(dateformated)
        print(dateformated2)
        let cart = Cart.sharedInstance

        guard let user = PFUser.current()  else 
            cart.showAlertView("Login", message: "Debes estar logeado para poder reservar.")
            return
        

        Alamofire.request("URL String", parameters: ["qty": "\(steps)","sucursalid":"\(self.SUCURSALID)","restid":"\(RESTID)","comment":"\(txtComentario.text!)","phone":"\(txtCelular.text!)","datetime":"\(dateformated)","action":"request","userid":"\(user.objectId!)"]).authenticate(usingCredential: cart.credential).responseJSON() 

            response in

            if (response.error != nil ) 

                print(response.error.debugDescription)
                print(response.request)
                cart.showAlertView("Error", message: "there was an error.")

            

            if(response.result.value != nil) 

                let json = JSON(response.result.value!);

                print(json);


                let success:Bool = json["success"].boolValue
                let error: Bool = json["error"].boolValue

                if(success) 
                    print("con exito")
                    let alert = UIAlertController(title: "Reserva Enviada", message: "Tu reserva ha sido enviada y será revisada por el establecimiento", preferredStyle: .alert)
                    let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) 
                        UIAlertAction in

                        // Get the previous Controller.
                        let targetController: UIViewController = (self.navigationController?.viewControllers[self.navigationController!.viewControllers.count - 3])!

                        // And go to that Controller
                        self.navigationController?.popToViewController(targetController, animated: true)

                    

                    alert.addAction(okAction)
                    self.present(alert,animated:true)


                
            

        



    



    @IBAction func stepperValue(_ sender: UIStepper) 

        self.lbl_step.text = Int(sender.value).description
        steps = Int(sender.value)

    

    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)
        registerForKeyboardNotifications()
        self.scrollView.delegate = self
    

    override func viewWillDisappear(_ animated: Bool) 
        super.viewWillDisappear(animated)
        deregisterFromKeyboardNotifications()
        self.scrollView.delegate = nil
    


    // MARK: Viewdidload
    override func viewDidLoad() 
        super.viewDidLoad()
        // enable scroll on scrollview
         self.scrollView.isScrollEnabled = true

        // step label initial value 2
        self.lbl_step.text = "2"

        // Get celular or phone

        if ( Cart.sharedInstance.User_celular != "") 
            txtCelular.text = Cart.sharedInstance.User_celular
         else 
            txtCelular.text = Cart.sharedInstance.User_phone
        


        let nearesthour = Date().nearestHour()
        self.datePicker.minimumDate = nearesthour
        self.txtComentario.delegate = self
        self.txtCelular.delegate = self
        self.txtCelular.tag = 20
        self.scrollView.delegate = self


        // tap gesture recognizer

        let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        self.scrollView.addGestureRecognizer(tap)


        print("el enarest hour es \(nearesthour as Date?) y el date normal es \(Date())")

        self.btn_reservar.layer.cornerRadius = 10
        self.cantidadView.layer.cornerRadius = 10

        self.lbl_empresa.text = EMP_NOMBRE
        self.lbl_direccion.text = DIRECCION

        /*
        print("VC Reservacion")
        print("SUCURSAL \(SUCURSALID) ")
        print("EMP NOMBRE " + EMP_NOMBRE)
        print("DIRECCION " + DIRECCION)
        */


    

    // MARK: TEXTFIELD STUFF

    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool 
        self.activeField = textField
        return true
    
    /*
    func textFieldDidBeginEditing(_ textField: UITextField) 
        self.activeField = textField
    
    */


    func textFieldDidEndEditing(_ textField: UITextField) 

        activeField = nil

    




    func registerForKeyboardNotifications()
        //Adding notifies on keyboard appearing
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    

    func deregisterFromKeyboardNotifications()
        //Removing notifies on keyboard appearing
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    

    @objc  func keyboardWasShown(notification: NSNotification)
        //Need to calculate keyboard exact size due to Apple suggestions

        print(" Keyboaard shown")


        var info = notification.userInfo!
        let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
        print(" el keyboardsize is \(keyboardSize)")
        let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height + 80, 0.0)

        self.scrollView.contentInset = contentInsets
        self.scrollView.scrollIndicatorInsets = contentInsets

        var aRect : CGRect = self.view.frame
        print("VIEW COMPLETE FRAME IS \(aRect)")
        print("KEYBOARD FRAME HEIGHT \(keyboardSize!.height)")
        aRect.size.height -= keyboardSize!.height
        print("FRAME MENOS KEYBOARD ES \(aRect)")

        print("SCROLLVIEW CONTENT \(self.scrollView.contentSize)")
        if let activeField = self.activeField 

            print("ACTIVEFIELD FRAME ORIGIN \(activeField.frame.origin) ")
            print("Active field is textfield tag is \(activeField.tag)")

            // if (!aRect.contains(activeField.frame.origin))
            print("arect Does Not contains activeField")
            self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
            print("TEXTFIELD FRAME ES \(activeField.frame)")
            print(" SCROLLVIEW CONTENT \(self.scrollView.contentSize)")
            //
        


    

    @objc  func keyboardWillBeHidden(notification: NSNotification)
        //Once keyboard disappears, restore original positions
        var info = notification.userInfo!
        let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size

        let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
        self.scrollView.contentInset = contentInsets
        self.scrollView.scrollIndicatorInsets = contentInsets
        self.activeField = nil
        self.view.endEditing(true)
        self.scrollView.isScrollEnabled = false

    


    @objc func handleTap(_ sender: UITapGestureRecognizer) 
        self.view.endEditing(true)
        print("Tap")

    


    func textFieldShouldReturn(_ textField: UITextField) -> Bool 
        self.view.endEditing(true)
        return false
    


    override func viewDidLayoutSubviews() 
        super.viewDidLayoutSubviews()
        scrollView.contentSize = CGSize(width: view.frame.width, height: view.frame.height)
        print(" SCROLLVIEW CONTENT AFTER SUBVIEWS \(self.scrollView.contentSize)")
    


    /*  // NOT WORKING BECAUSE OF UISCROLLVIEW IN PLACE, MUST USE UITAPGESTURE RECOGNIZER
     override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
     self.view.endEditing(true)
     self.activeField?.resignFirstResponder()
     
     */



【问题讨论】:

【参考方案1】:

我遇到了同样的问题。显然,获取键盘的大小发生了变化。我将 UIKeyboardFrameBeginUserInfoKey 更改为 UIKeyboardFrameEndUserInfoKey 并让它再次工作。

这是我在按下文本字段时移动视图的确切代码

@objc func keyboardWasShown(notification: NSNotification)
    self.scrollView.isScrollEnabled = true
    var info = notification.userInfo!
    let keyboardSize = (info[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size
    let contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)

    self.scrollView.contentInset = contentInsets
    self.scrollView.scrollIndicatorInsets = contentInsets

    var aRect : CGRect = self.view.frame
    guard let kbHeight = keyboardSize?.height elsereturn
    aRect.size.height -= kbHeight
    if let activeField = self.activeTextField 
        if (!aRect.contains(activeField.frame.origin))
            self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
        
    

【讨论】:

嗯..让我试试!!

以上是关于通过 UiscrollView 快速滚动到 UITextField 仅作为第一次错误的主要内容,如果未能解决你的问题,请参考以下文章

UIScrollView 的灵敏度/滚动速度与分页

快速禁用 UIScrollView 中的垂直滚动?

在触摸期间停止 UIScrollView 滚动

快速滚动 UIScrollView 时 UIImageView 停止调整大小

以编程方式快速创建水平 UIScrollView

UIScrollView 在滚动时暂停 NSTimer