Programmaticall​y Add Split View Controllers

An often ignored but rather powerful View Controller is the UISplitViewController. You can make one from a template and the storyboard, but I often skip both and do it programmatically, which is especially good when I’m prototyping in a playground.  Let’s give it a try using the exercise files I’ve been using for the past few tips. There we added a color table. Split view controllers have a master, which I’ll make the color table, and the detail, which will tell us the color name, and show the color. 

Head over to the AppDelegate in this project. Split View controllers should be your root view, and you have two view controllers be the roots for the detail and master view.  I usually embed the root view controllers in navigation controllers, so I can add view controllers off that. I did that for the master already. I only need the detail, which I can do like this:  

let colorDetailViewController = ColorDetailViewController()
let detailNavigationViewController = UINavigationController(rootViewController: colorDetailViewController)

 I’ll instantiate a split view controller. 

let splitViewController = UISplitViewController()

You assign the master and detail to the viewControllers property, which is an array of two elements. The first element of the array is the master and the second the detail.  

splitVC.viewControllers = [navigationVC,colorDetailVC]

 I  like doing one more thing to control the width of the master view. I’ll set the property preferredPrimaryColumnWIdthFraction to a proportion. I’ll set this one to 1/3.  

splitVC.preferredPrimaryColumnWidthFraction = 1/3

Finally, assign the splitViewController to the window‘s rootViewController

window?.rootViewController = splitViewController

Build and run this on an iPhone XR simulator. You’ll just get a single view of the detail. On a compressed width, such as you get in portrait, you get only one of the two views.

On a regular width you’ll get both. Rotate the simulator with a command-left arrow. In Portrait, you see the master and detail.

  If you tap a color, it still doesn’t do anything in the detail. I’d like to show the color on the detail when selected from the mater. These are two view controllers without any way to communicate with each other. I’ll use delegation here. 

Stop the app. If you go to HueColorTableViewController, you’ll see I made a protocol and delegate.  I’ll  add the delegate’s method to the code here in did select 

delegate.didSelectColor(color: colors[row])

In the ColorDetailViewController, I’ll adopt the delegate

UIViewController,HueColorTableViewControllerDelegate  {

I  added a callback to change the background color and display the name. 

Go back to AppDelegate and add a line to direct the delegate correctly. 

colorTVC.delegate = colorDetailVC

Run that, and you get the same split view controller, but select a color and it appears.

That’s the basics of adding a split view controller to a project programmatically. 

The Whole Code

Below you’ll find the code for this project. You can also download the code from GitHub here.

AppDelegate.swift

//
//  AppDelegate.swift
//  ColorPicker
//
//
//  An exercise file for iOS Development Tips Weekly
//  by Steven Lipton (C)2018, All rights reserved
//  For videos go to http://bit.ly/TipsLinkedInLearning
//  For code go to http://bit.ly/AppPieGithub
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        window = UIWindow(frame: UIScreen.main.bounds)
        
        // Instantiate root view controllers
        let hueColorTableViewController = HueColorTableViewController()
        let colorDetailViewController = ColorDetailViewController()
        hueColorTableViewController.delegate = colorDetailViewController
        
        
        // Embed in navigation controllers
        let masterNavigationViewController = UINavigationController(rootViewController: hueColorTableViewController)
        let detailNavigationController = UINavigationController(rootViewController: colorDetailViewController)
        
        
        // Embed in Split View controller
        let splitViewController = UISplitViewController()
        splitViewController.viewControllers = [masterNavigationViewController,detailNavigationController]
        splitViewController.preferredPrimaryColumnWidthFraction = 1/3
        
        // Root view controller of window
        window?.rootViewController = splitViewController
        window?.makeKeyAndVisible()
        return true
    }

}


HueColorTable.swift

//
//  HueColorTableViewController.swift
//  ColorPicker
//
//
//  An exercise file for iOS Development Tips Weekly
//  by Steven Lipton (C)2018, All rights reserved
//  For videos go to http://bit.ly/TipsLinkedInLearning
//  For code go to http://bit.ly/AppPieGithub
//

import UIKit

protocol HueColorTableViewControllerDelegate{
    func didSelectColor(color:ColorEntry)
}


class HueColorTableViewController:UITableViewController{
    var delegate:HueColorTableViewControllerDelegate! = nil
    var colors:[ColorEntry] = ColorModel().hues(count: 12)
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return colors.count
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
        if cell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
        }
        let row = indexPath.row
        cell?.contentView.backgroundColor = colors[row].color
        cell?.textLabel?.text = colors[row].name
        cell?.textLabel?.numberOfLines = 0
        return cell!
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let row = indexPath.row
        title = colors[row].name
        delegate.didSelectColor(color: colors[row])
    }
    
    override func loadView() {
        super.loadView()
    }
    

}

ColorDetailViewController.swift

//
// ColorDetailViewController.swift
// ColorPicker
//
//  An exercise file for iOS Development Tips Weekly
//  by Steven Lipton (C)2018, All rights reserved
//  For videos go to http://bit.ly/TipsLinkedInLearning
//  For code go to http://bit.ly/AppPieGithub
//

import UIKit

class ColorDetailViewController: UIViewController, HueColorTableViewControllerDelegate  {
    let colorLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        addLayout()
        // Do any additional setup after loading the view.
    }
    
    //MARK:- Delegates
    func didSelectColor(color: ColorEntry) {
        view.backgroundColor = color.color
        colorLabel.text = color.name
    }
    
    //MARK:- Layout
    // All layout methods go here.
    func addLayout(){
        colorLabel.text = "Color Detail"
        let font = UIFont(name: "AmericanTypewriter", size: 30)!
        colorLabel.font = font
        colorLabel.backgroundColor = .lightGray
        colorLabel.textAlignment = .center
        colorLabel.numberOfLines = 0
        view.addSubview(colorLabel)
        colorLabel.translatesAutoresizingMaskIntoConstraints = false
        var constraints = [NSLayoutConstraint]()
        constraints += [NSLayoutConstraint(item: colorLabel, attribute: .leading, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .leading, multiplier: 1.0, constant: 0)]
        constraints += [NSLayoutConstraint(item: colorLabel, attribute: .trailing, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .trailing, multiplier: 1.0, constant: 0)]
        constraints += [NSLayoutConstraint(item: colorLabel, attribute: .top, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .top, multiplier: 1.0, constant: 0)]
        constraints += [NSLayoutConstraint(item: colorLabel, attribute: .height, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .height, multiplier: 1 / 9, constant: 0)]
        view.addConstraints(constraints)
    }
}

ColorModel.swift

//
//  ColorModel.swift
//  ColorPicker
//
//
//  An exercise file for iOS Development Tips Weekly
//  by Steven Lipton (C)2018, All rights reserved
//  For videos go to http://bit.ly/TipsLinkedInLearning
//  For code go to http://bit.ly/AppPieGithub
//

import UIKit

class ColorEntry{
    var name:String = ""
    var color:UIColor
    var hue:CGFloat = 0.0
    var brightness:CGFloat = 0.5
    var saturation:CGFloat = 1.0
    
    init(name:String,color:UIColor){
        self.color = color
        self.name = name
    }
}


class ColorModel{
    var colors = [ColorEntry]()
    init(){
        colors = []
    }
    func hues(count:Int)->[ColorEntry]{
        colors = []
        if count <= 0 {return colors}
        for hue in 0...count{
            let hueValue = CGFloat(hue)/CGFloat(count)
            let color = UIColor(hue: hueValue, saturation: 1.0, brightness: 1.0, alpha: 1.0)
            let name = String(format:"H:%04.3f S:1.0 B:1.0 ",hueValue)
            let colorEntry = ColorEntry(name: name, color: color)
            colors += [colorEntry]
        }
        return colors
    }
    
    func lightnessScale(hue:UIColor,count:Int){
        
    }
    
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: