Alex
Alex

Reputation: 371

iOS 8 custom keyboard self.view.frame returns 0

I am writing a custom keyboard for iOS 8. My main UIView returns {{0, 0}, {0, 0}} in my viewDidLoad method. I have tried receiving the CGRect with view.frame and bounds but both return 0. However if I use these same methods to retrieve my view's coordinates in the viewDidAppear method they return fine: {{0, 0}, {320, 216}}. The problem here is that viewDidAppear runs after didLoad so I cannot retrieve the size from didAppear and use it in didLoad. Here is my question:

Why does self.view 's frame return 0 in viewDidLoad? Shouldn't its size be set already in here? This (viewDidLoad) is where I'm supposed to be doing most of my initiation and UI set up, correct? I have moderate experience in Xcode.

I am programming this in swift, although this shouldn't matter(?) I thought it was worth noting. Answers in Obj-C are fine.

Edit: I created an entirely new Xcode project and target with the following class and receive the same error:

import UIKit

class KeyboardViewController: UIInputViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    println(self.view.frame) // returns (0.0,0.0,0.0,0.0)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()

}

override func viewDidAppear(animated: Bool) {
    println(self.view.frame) // returns (0.0,0.0,320.0,216.0)
}


}

I've tested with the simulator and physical devices too ranging from 6 to 4S. Nothing seems to make a difference.

Upvotes: 2

Views: 1552

Answers (3)

Steve Rosenberg
Steve Rosenberg

Reputation: 19524

I could get it to work by adding a textField on top and making it FirstResponder:

import UIKit

class KeyboardViewController: UIInputViewController {
    
    @IBOutlet var textField: UITextField!
    override func viewDidLoad() {
        super.viewDidLoad()
        textField.becomeFirstResponder()
        println(self.view.frame) // returns (0.0,0.0,375.0,667.0)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        
    }
    
    override func viewDidAppear(animated: Bool) {
        println(self.view.frame) // returns (0.0,0.0,375.0,667.0)

    }
}

Upvotes: 0

kb1ooo
kb1ooo

Reputation: 8872

It is a general property of the view display hierarchy (not only of keyboard extensions) that the frame size will not be correct in viewDidLoad. See here and here which also gives some suggestions on how to handle layout.

I would recommend using auto layout (similar to how "nextKeyboardButton" is done in the keyboard extension example code) in which case for the most part you won't have to worry about handling frame changes. E.g. in viewDidLoad

    MyKeyboardView* keyboardView = [[keyboardView alloc] init];
    keyboardView.translatesAutoresizingMaskIntoConstraints = NO;

    [self.inputView addSubview: keyboardView];

    NSLayoutConstraint *leftSideConstraint = [NSLayoutConstraint constraintWithItem:keyboardView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.inputView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0];
    NSLayoutConstraint *rightSideConstraint = [NSLayoutConstraint constraintWithItem:keyboardView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.inputView attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0];
    NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:keyboardView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.inputView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
    NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:keyboardView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.inputView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0];

    [self.inputView addConstraints:@[leftSideConstraint, rightSideConstraint, bottomConstraint, topConstraint]];

In this way, the frame for your keyboardView will track the inputView frame (which is set to the default keyboard frame size for the orientation and device) and will therefore resize automatically with orientation changes. You can then handle further custom layout in layoutSubviews of your MyKeyboardView class. One last thing, if your MyKeyboardView does any custom drawing in drawRect, you will need to set the contentMode in MyKeyboardView's init self.contentMode = UIViewContentModeRedraw; otherwise drawRect won't be called upon frame changes.

Upvotes: 1

Leo Moon85
Leo Moon85

Reputation: 150

You should check with self.inputView.frame instead of self.view.frame for custom keyboard

You can use isPortraitLayout method to check/initialise view if its landscape mode or portrait mode...

-(BOOL) isPortraitLayout{
    int appExtensionWidth = (int)round(self.view.frame.size.width);

    int possibleScreenWidthValue1 = (int)round([[UIScreen mainScreen] bounds].size.width);
    int possibleScreenWidthValue2 = (int)round([[UIScreen mainScreen] bounds].size.height);

    int screenWidthValue;

    if (possibleScreenWidthValue1 < possibleScreenWidthValue2) {
        screenWidthValue = possibleScreenWidthValue1;
    } else {
        screenWidthValue = possibleScreenWidthValue2;
    }

    return (appExtensionWidth == screenWidthValue);
}

Upvotes: 1

Related Questions