iOS: Use UITableViewController for More Flexibility

6 minutes

Usually we use UIViewController for many cases, but sometime there is a time that we want app that support vertical-view on iPhone.

As I mention at the title we gonna use UITableViewController, but not using any table view features, but really just layout everything on the storyboard.

artboard-2

Reasons to use UITableViewController over UIViewController

  • Especially for iPhone, we want to support app for landscape view.
  • Dont have to manually setup constraints of a UIView inside UIScrollView.
  • To support UITextField. If the field at the bottom and on focus, it will automatically move the field upper than the keyboard appeared.

Single View Project

Let’s create a new single view project from the template, it has ViewController.swift as default which subclass to UIViewController.

Open the Main.storyboard and make a layout something like this.

S

But suddenly we want to add support for vertical. Should we embeded in UIScrollView and define contentSize? I can be really hard if we have complex auto-layout constraints among the subviews.

With all autolayout take in place you can imaging how hard to add into UIScrollView, and ContentView and have the auto layout contraint work in place.

Revision: UIScrollView is a view that is adjustable over the content view. It clips the content to its frame. There is 2 minimum condition to met to make UIScrollView working. The frame of UIScollView and the contentSize so it knows when to stop scrolling.

Insert UITableViewController

Lets be honest, we don’t intend at the begininig to use UITableViewController, but when we have to support vertical orientation for small iPhone screen, it can not be scrollable automatically. The view is created with UIViewController. The class that it referst also a UIViewController.

S

At storyboard, drag UITableViewController into the storybaord nearby the UIViewController that we want to replace.

Select the Table View, set it Content as Static Cells.

Delete ‘Table View Section’ that come with it.

Screen Shot 2016-11-18 at 2.12.11 AM.png

Original UIViewController

The Original View Controlle already has the autolayout set in place, designed with iPhone 7 layout, so when we run on iPhone 7 Plus it will look like this.

Simulator Screen Shot 18 Nov, 2016, 1.58.05 AM.png

The problem is just don’t scroll down.

Move the View to TableView’s Header

Now drag the view from the original view controller to the table view.

Screen Shot 2016-11-18 at 2.14.06 AM.png

So the UITableView will have a header view from the original UIViewController when we drag-drop like that.

Change the superclass of UIViewController

Now to to ViewController.swift, change the super class like this.


class ViewController: UITableViewController {
....
}

At the storyboard, change the initial view view controller to the new Table View, set the view controller class to ViewController.

Initial Issue

When testing on vertical mode, we will get the view get clipped. It’s not okay because we cannot see the whole view. So we have to code something inside the UITableViewController subclass, ViewController.swift.

Adding The Tweak

Let’s make an IBOutlet reference from the UITableView’s view as contentView, like the image below.

AA.png

Next, is to open the inspector to see at the storyboard and see height of the ‘contentView’ view. We will use the value to hard-code inside the code.

Currently, it’s hard to get the height when we open the app with landscape mode, that’s why we don’t have choise but to copy the height. It works anyway.

At ViewController.swift, add this variable declaration.


// Add this at class declaration
let contentViewHeight:CGFloat = <#View-height#>

Add a override function of viewWillLayoutSubivews().


override func viewWillLayoutSubviews() {
  super.viewWillLayoutSubviews()
    
  // This will let the our view view not being clipped
  // and also by setting tableView content size make it scrollable
  let size = CGSizeMake(self.view.frame.width, contentViewHeight)
  contentView.frame.size = size
  tableView.contentSize = size
  
}

P/S: Embedded in UINavigationController

We also tested it embeded with UINavigationController, which is also fine with Xcode 8. Previous version of Xcode need to add something inside the viewWillLayoutSubivews.


override func viewWillLayoutSubviews() {
  super.viewWillLayoutSubviews()
  
  // This will help with over padding issue if you
  // embedded inside navigation controller
  if (self.view.frame.origin.y == 0) {
    if let rect = self.navigationController?.navigationBar.frame {
      let y = rect.size.height + rect.origin.y
      self.tableView.contentInset = UIEdgeInsetsMake(y, 0, 0, 0)
    }
  } else if ( self.view.frame.origin.y == 44 || self.view.frame.origin.y == 64)  {
    self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
  }
  
  // This will let the our view view not being clipped
  // and also by setting tableView content size make it scrollable
  let size = CGSize(width:self.view.frame.width, height:contentViewHeight)
  contentView.frame.size = size
  tableView.contentSize = size
  
}