Workshop 10: Make The Cursor Appear Above The Keyboard

In the previous workshop you learned how to hide the keyboard. Now, you have to add code in the ViewController class file to solve this keyboard problem.

When you tap the Text View control in portrait or landscape mode, the keyboard appear at the bottom of the screen. If the cursor is hidden by the keyboard, you can’t see what you are typing. To fix this problem, you have to add code in the ViewController class file to shift context of the Text View control up so the line containing the cursor is shown above the keyboard.

Before you begin, I assume you’ve done the following:

The first thing you have to do is declare a global variable for holding the onscreen keyboard’s size in the ViewController.swift file.

class ViewController: UIViewController, UITextViewDelegate {
  @IBOutlet weak var outputTextView: UITextView!
  var keyboardSize: CGSize!
  @IBAction func actionShowOutput(sender: AnyObject) {
  }
  .
  .
  .

Now, add this code in the ViewDidLoad method.

override func viewDidLoad() {
  super.viewDidLoad()
  NSNotificationCenter.defaultCenter().addObserver(self,
      selector:"keyboardDidShow:",
      name: UIKeyboardDidShowNotification,
      object: nil)
}

Every time the keyboard is shown onscreen, the iOS Simulator sends various notifications to the app regarding each state of the keyboard. So code shown above notify the app when the keyboard did show on the iOS Simulator screen via the UIKeyboardDidShowNotification observer.

The keyboardDidShow: method is fired every time you click the Text View. Here is the code to implement it in the ViewController.swift file. Add it below the touchesBegan() function.

func keyboardDidShow(notification: NSNotification) {
  let info: Dictionary = notification.userInfo!
  if let aValue = info[UIKeyboardFrameBeginUserInfoKey] as? NSValue {
    keyboardSize = aValue.CGRectValue().size
  }

  var contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height - 75, 0.0)
  outputTextView.contentInset = contentInsets
  outputTextView.scrollIndicatorInsets = contentInsets

  let isPortrait = UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation)
  let isLandscape = UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation)

  if isPortrait {
    println("Device is in PORTRAIT orientation and the keyboard size is \(keyboardSize.height)")
  } else if isLandscape {
    println("Device is in LANDSCAPE orientation and the keyboard size is \(keyboardSize.height)")
  }
}

Code Analysis

The first set of statements (line 2-4) put the keyboardDidShown parameter’s value in a Swift Dictionary object called info, get the onscreen keyboard’s size from the info dictionary and put it in a global variable called keyboardSize.

The second set of code (line 7-9) adjust the bottom content of the Text View control by the keyboard’s size less 75 pixels of space. The result of that adjustment is assigned to the Text View’s contentInset attribute.  The statement on line 9 is what make the Text View’s text shift above the onscreen keyboard.

The final set of code (11-18) isn’t needed. It is debugging code to show that the keyboardDidShow method’s code is working.  Feel free to delete it or comment it out. The first two line of code put the device’s orientation in variables. The if statement display a message in the Debugger window, depending on whether the device is in portrait or landscape orientation.

The Swift Code Demystified

The println() function  works like Objective-C’s NSLog() function to display content in Xcode’s Debugger area. As you can see in above code, it was used to display a string literal and the value stored in the keyboardSize variable.

The var keyword is for declaring a Swift variable, which is mutable variables. When you create a variable using this keyword, you don’t have initialize it right away.

The let keyword is for declaring a Swift constant and you must assign it a value; otherwise the compiler will complain. The value of a Swift constant is lock. If you try to change it later on in the class file, the compiler will complain.

The dot is used for accessing a class method. The dot is also used for accessing a class property and a function parameters. For example, keyboardDidShow() is a user defined function and it have a dictionary parameter called notification. We put the parameter in a Dictionary constant called info. The dictionary’s size element was assigned to the keyboardSize variable like this:

keyboardSize = aValue.CGRectValue().size

Here is a break down of above Swift statement:

  • aValue is an object.
  • CGRectValue() is a method of the object.
  • size is a property of the aValue object and we put it in the keyboardSize variable

Run The Application in The iOS Simulator

Run the application in the iOS iPhone 4s Simulator to test code implemented in the ViewController class, which solved the keyboard problem. Figure A and B shows what the simulator’s screen will look like in portrait and landscape orientation. The bottom area of both figures shows what you’ll see, when you click the Text View control in portrait or landscape orientation. Use this  (⌘ ←) keyboard shortcut keys to put the iOS Simulator in landscape orientation. Anytime the keyboard doesn’t appear on the iOS Simulator’s screen, use this (⌘ K) keyboard shortcut keys to show it.

xcode6-keyboard-problem-resultA xcode6-keyboard-problem-resultB
Figure A Figure B

When you click the last line in the Text View control, notice how the keyboard appear onscreen and the Text View control’s text shift up. As you type text, the cursor will eventually disappear behind the keyboard. The app will automatically, shift the control’s content up; thus keeping the cursor above the keyboard. After running the app in the iPhone Simulator, you should run it it in the iPad Simulator to see the keyboard code in action.