I have a basic sign up screen set up programmatically with the UI elements inside a view that is itself inside a scroll view.
The last UI element in the screen is a register button. I set up a keyboard notification observer with the Will Show and Will Hide notifications.
I am running this code on iPod touch 7th gen simulator.
My problem is when trying to read the maxY value of the sign up button and compare it to the keyboard minY it prints wrong numbers.
The keyboard is clearly blocking the register button which mean the button's maxY value will be greater the the keyboard minY value.
However the values printed shows that there is something wrong with the reading of the register button frame.
Here is my code:
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)
}
}
CodePudding user response:
It's because the keyboard frame and the button frame are in two different coordinate systems. You cannot compare them directly. You need to convert the button frame to window coordinates before comparing them. Or else convert the keyboard frame to the button frame coordinates (the button's superview).
Actually what I typically do is convert the keyboard frame to the internal coordinates of the target view and compare that to the target view's bounds. For example:
// 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
That tells me whether the keyboard would cover the sliding view, and if so, by how much.