Sweeper
Sweeper

Reputation: 271175

Why the two CGRect don't intersect when they do visually?

I am building an app. It needs to accept user input from some UITextFields. And sometimes the keyboard will hide the text field so I need to move up the view when the keyboard CGRect intersects with the text field's frame.

I followed this tutorial an I added some of my own logic because I have multiple text fields.

Here is my relevant code: (The whole thing is in a VC that conforms to UITextFieldDelegate)

var focusedTextField: UITextField?
var viewMovedUp = false
var keyboardSize: CGRect!

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("onRotate:"), name:UIDeviceOrientationDidChangeNotification, object: nil);
}

override func viewDidDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    return true
}

func textFieldDidBeginEditing(textField: UITextField) {
    focusedTextField = textField
}

func onRotate (notification: NSNotification) {
    view.endEditing(true)
}

func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardSize =  (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
            self.keyboardSize = keyboardSize
            assert(focusedTextField != nil)
            if CGRectIntersectsRect(focusedTextField!.bounds, keyboardSize){
                moveView(up: true)
            }
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    if viewMovedUp {
        moveView(up: false)
    }
}

func moveView (up up: Bool) {
    let keyboardHeight = keyboardSize.height
    let movement = (up ? -keyboardHeight : keyboardHeight)

    UIView.animateWithDuration(0.3, animations: {
        self.view.frame = CGRectOffset(self.view.frame, 0, movement)
    })
    viewMovedUp = up
}

If you don't want to read the whole code, I'll explain the gist of it. So basically when the user taps on one of the text fields, textFieldDidBeginEditing gets called. That basically sets the focusedTextField to the text field that the user is editing. Then keyBoardWillShow gets called. It gets the keyboard size and assign it to a class-level variable called keyboardSize It then checks if the focused text field (remember that?) is covered by the keyboard (via CGRectIntersectRect). If it is then we move the view up by calling moveView. That method works perfectly fine so no need to explain.

Now on to the problem!

Let's look at a screen shot of the VC:

enter image description here

When I tap on the "Enter A" text field, the view moves up as expected. But when I tap on the "Enter P" text field, the keyboard shows up and covers the text field completely.

After some debugging, I found that

CGRectIntersectsRect(focusedTextField!.bounds, keyboardSize)

returns false so moveView is not called. The "Enter P" text field and keyboard size is as follows:

Bounds of text field:
    x: 62 y: 94.5
    height: 32.5 width: 278
Keyboard size:
    x: 0 y: 158
    height: 162 width: 568

Just from these figures, I don't think they overlap. But visually, they really do!

enter image description here

I also tried to change focusedTextField!.bounds to focusedTextField.frame but it still doesn't work.

Why is this happening? How do I fix it?

Upvotes: 1

Views: 649

Answers (1)

matt
matt

Reputation: 535086

The problem is that in this code:

CGRectIntersectsRect(focusedTextField!.bounds, keyboardSize)

...you are comparing apples and oranges:

  • focusedTextField!.bounds is in the coordinate space of focusedTextField

  • keyboardSize is in the coordinate space of the window

(And the reason why focusedTextField.frame didn't work is that it is in yet another coordinate space, that of the text field's superview.)

Those are two very different coordinate spaces, so you cannot compare these rects. You have to convert one of them to the coordinate space of the other.

For example, I think this will do it:

newKeyboardSize = focusedTextField.convertRect(keyboardSize, fromView:nil)

Now newKeyboardSize and focusedTextField.bounds should be in the same coordinate space.

Upvotes: 3

Related Questions