Swift - 子视图 frame.maxY 读取不正确

Swift - Subviews frame.maxY reading incorrectly

我有一个以编程方式设置的基本注册屏幕,其中的 UI 元素位于一个视图中,该视图本身位于滚动视图中。

屏幕中的最后一个 UI 元素是注册按钮。我使用 Will Show 和 Will Hide 通知设置了键盘通知观察器。

我在第 7 代 iPod touch 模拟器上 运行 这段代码。

我的问题是当尝试读取注册按钮的 maxY 值并将其与键盘 minY 进行比较时,它打印出错误的数字。

键盘明显挡住了注册按钮,这意味着按钮的 maxY 值将大于键盘的 minY 值。

但是打印的值显示注册按钮框架的读取有问题。

这是我的代码:

import UIKit

class RegisterVC: UIViewController {
    
    
    private let scrollView: UIScrollView = {
        let scroll = UIScrollView()
        scroll.clipsToBounds = true
        scroll.isScrollEnabled = true
        scroll.translatesAutoresizingMaskIntoConstraints = false
        scroll.showsVerticalScrollIndicator = false
        
        return scroll
    }()
    
    private let scrollInnerView: UIView = {
        let innerView = UIView()
        innerView.translatesAutoresizingMaskIntoConstraints = false
        
        return innerView
    }()
    
    private let profilePic: UIImageView = {
        let imageView = UIImageView()
        imageView.image = UIImage(systemName: "person.circle")
        imageView.contentMode = .scaleAspectFit
        imageView.tintColor = .gray
        imageView.translatesAutoresizingMaskIntoConstraints = false
        
        return imageView
    }()
    
    
    private let usernameField: UITextField = {
        let field = UITextField()
        field.autocapitalizationType = .none
        field.autocorrectionType = .no
        field.returnKeyType = .next
        field.layer.cornerRadius = 12
        field.layer.borderWidth = 1
        field.layer.borderColor = UIColor.lightGray.cgColor
        field.placeholder = "Username..."
        field.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: 0))
        field.leftViewMode = .always
        field.backgroundColor = .white
        field.keyboardType = .default
        field.isHighlighted = false
        field.textAlignment = .left
        field.translatesAutoresizingMaskIntoConstraints = false
        
        return field
    }()
    
    private let emailField: UITextField = {
        let field = UITextField()
        field.autocapitalizationType = .none
        field.autocorrectionType = .no
        field.returnKeyType = .next
        field.layer.cornerRadius = 12
        field.layer.borderWidth = 1
        field.layer.borderColor = UIColor.lightGray.cgColor
        field.placeholder = "Email Address..."
        field.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: 0))
        field.leftViewMode = .always
        field.backgroundColor = .white
        field.keyboardType = .default
        field.textAlignment = .left
        field.translatesAutoresizingMaskIntoConstraints = false
        
        return field
    }()
    
    private let passwordField: UITextField = {
        let field = UITextField()
        field.autocapitalizationType = .none
        field.autocorrectionType = .no
        field.returnKeyType = .done
        field.layer.cornerRadius = 12
        field.layer.borderWidth = 1
        field.layer.borderColor = UIColor.lightGray.cgColor
        field.placeholder = "Password..."
        field.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: 0))
        field.leftViewMode = .always
        field.backgroundColor = .white
        field.isSecureTextEntry = true
        field.textAlignment = .left
        field.keyboardType = .default
        field.translatesAutoresizingMaskIntoConstraints = false
        
        return field
    }()
    
    private let registerButton: UIButton = {
        let button = UIButton()
        button.setTitle("Create Account", for: .normal)
        button.backgroundColor = .systemGreen
        button.setTitleColor(.white, for: .normal)
        button.layer.cornerRadius = 12
        button.layer.masksToBounds = true
        button.titleLabel?.font = .systemFont(ofSize: 20, weight: .bold)
        button.translatesAutoresizingMaskIntoConstraints = false
        
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = "Create Account"
        view.backgroundColor = .white
        
        view.addSubview(scrollView)
        scrollView.addSubview(scrollInnerView)
        scrollInnerView.addSubview(profilePic)
        scrollInnerView.addSubview(usernameField)
        scrollInnerView.addSubview(emailField)
        scrollInnerView.addSubview(passwordField)
        scrollInnerView.addSubview(registerButton)
        usernameField.delegate = self
        emailField.delegate = self
        passwordField.delegate = self
        profilePic.isUserInteractionEnabled = true
        registerButton.addTarget(self,
                                 action: #selector(registerButtonTapped),
                                 for: .touchUpInside)

        setUpKeyboard()
        setUpConstraints()
        
    }
    
    private func setUpConstraints() {
        
        // Scroll View Constraints
        
        scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
        
        // Scroll Inner View Constraints
        
        scrollInnerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        scrollInnerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
        scrollInnerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
        scrollInnerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
        scrollInnerView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
        scrollInnerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor, constant: 1).isActive = true
        
        // Profile Picture Constraints
        
        profilePic.widthAnchor.constraint(equalTo: scrollInnerView.widthAnchor, multiplier: 1/3).isActive = true
        profilePic.heightAnchor.constraint(equalTo: scrollInnerView.widthAnchor, multiplier: 1/3).isActive = true
        profilePic.centerXAnchor.constraint(equalTo: scrollInnerView.centerXAnchor).isActive = true
        profilePic.topAnchor.constraint(equalTo: scrollInnerView.topAnchor, constant: 10).isActive = true
        
        // User Name Field Constraints
        
        usernameField.widthAnchor.constraint(equalTo: scrollInnerView.widthAnchor, constant: -60).isActive = true
        usernameField.heightAnchor.constraint(equalToConstant: 45).isActive = true
        usernameField.topAnchor.constraint(equalTo: profilePic.bottomAnchor, constant: 10).isActive = true
        usernameField.centerXAnchor.constraint(equalTo: profilePic.centerXAnchor).isActive = true
        
        // Email Field Constraints
        
        emailField.widthAnchor.constraint(equalTo: usernameField.widthAnchor).isActive = true
        emailField.heightAnchor.constraint(equalTo: usernameField.heightAnchor).isActive = true
        emailField.topAnchor.constraint(equalTo: usernameField.bottomAnchor, constant: 10).isActive = true
        emailField.centerXAnchor.constraint(equalTo: usernameField.centerXAnchor).isActive = true
        
        // Password Field Constraints
        
        passwordField.widthAnchor.constraint(equalTo: emailField.widthAnchor).isActive = true
        passwordField.heightAnchor.constraint(equalTo: emailField.heightAnchor).isActive = true
        passwordField.topAnchor.constraint(equalTo: emailField.bottomAnchor, constant: 10).isActive = true
        passwordField.centerXAnchor.constraint(equalTo: emailField.centerXAnchor).isActive = true
        
        // Register Button Constraints
        
        registerButton.widthAnchor.constraint(equalTo: passwordField.widthAnchor).isActive = true
        registerButton.heightAnchor.constraint(equalTo: passwordField.heightAnchor).isActive = true
        registerButton.topAnchor.constraint(equalTo: passwordField.bottomAnchor, constant: 20).isActive = true
        registerButton.centerXAnchor.constraint(equalTo: passwordField.centerXAnchor).isActive = true
    }
    
    private func setUpKeyboard() {
        
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowNotification), name: UIResponder.keyboardWillShowNotification, object: nil)
        
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideNotification), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    @objc private func keyboardWillShowNotification(_ notification: NSNotification) {
        
        guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
        else {
            
            return
        }
        
        print(keyboardSize.minY)
        print(registerButton.frame.maxY)
        
    }
}

因为键盘边框和按键边框在两个不同的坐标系下。你不能直接比较它们。在比较它们之前,您需要 convert 按钮框架到 window 坐标。否则 将键盘框架转换 到按钮框架坐标(按钮的父视图)。

实际上我通常做的是将键盘框架转换为目标视图的内部坐标,并将其与目标视图的边界.例如:

// n is the notification
let d = n.userInfo!
var r = d[UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
r = self.slidingView.convert(r, from:nil) // <- this is the key move!
let h = self.slidingView.bounds.intersection(r).height

这告诉我键盘是否会覆盖滑动视图,如果是,覆盖多少。