7 min workout app iOS Swift Tutorial iOS8

UITableView Delegate and Datasource in Swift

In this part we will learn how to populate our UITableview with all the workouts and learn more about UITableView Delegate and Datasource method in Swift.  This is the part 2 of the multipart series if you have checked the first part I suggest you to complete that before continuing 7 min workout app in Swift for iOS8

To begin with lets look at the main cocoapods which we will be using in this project

  • Flat Colors for iOS : Chameleon It also has .dmg for adding the colors to Xcode. I have personally used this cocoapod in many projects.
  • MZTimerLabel This cocoapod provides a subclass of UILabel whose contents change with the timer/stopwatch. We will be using this for showing the countdown timer.
  • youtube-ios-player-helper We will be using this cocoapod to display youtube videos embedded in the app.

Once you have the pods install open the projectname.xcworkspace. Also make sure you setup the bridging headers appropriately.

At this point you should have Chameleon pod installed. Go and change the colors as appropriate. If you don’t want to use this its totally fine go ahead with whatever colors you like.

At this point lets look at the class which will provide all the necessary Workouts ,related background color and the youtube video link id.

Lets define a class for workout. The workout class will have videoId (Youtube video id), title, workoutText and the background color for that workout.

//
//  Workout.swift
//  SevenMinWorkOut
//
//  Created by Shrikar Archak on 4/18/15.
//  Copyright (c) 2015 Shrikar Archak. All rights reserved.
//

import Foundation

class Workout {
    var videoId: String!
    var title: String!
    var workoutText: String!
    var color: UIColor!
    
    init(title: String, videoId: String, workoutText: String, color: UIColor){
        self.videoId = videoId
        self.title = title
        self.workoutText = workoutText
        self.color = color
    }
        
}

Create a new file called WorkoutDataSource.swift. This is a simple class with static data but you can use parse or any other rest api to fetch the workouts from the web.

//
//  WorkoutDataSource.swift
//  SevenMinWorkOut
//
//  Created by Shrikar Archak on 4/18/15.
//  Copyright (c) 2015 Shrikar Archak. All rights reserved.
//

import Foundation

class WorkoutDataSource{
    var workouts:[Workout]
	    
    init() {
        workouts = []
        let wk1 = Workout(title: "Jumping Jacks", videoId: "UpH7rm0cYbM", workoutText: "A calisthenic jump done from a standing position with legs together and arms at the sides to a position with the legs apart and the arms over the head.",color:  UIColor.flatRedColor())
        workouts.append(wk1)

        let wk2 = Workout(title: "Wall Sits", videoId: "y-wV4Venusw", workoutText: "A wall sit, also known as a Roman Chair, is an exercise done to strengthen the quadriceps muscles. It is characterized by the two right angles formed by the body, one at the hips and one at the knees.", color: UIColor.flatTealColor())
        workouts.append(wk2)


        let wk4 = Workout(title: "Abdominal Crunches", videoId: "2yOFvV-NSeY", workoutText: "A crunch begins with lying face up on the floor with knees bent. The movement begins by curling the shoulders towards the pelvis. The hands can be behind or beside the neck or crossed over the chest. Injury can be caused by pushing against the head or neck with hands.", color: UIColor.flatPurpleColor())
        workouts.append(wk4)

        let wk3 = Workout(title: "Push Ups", videoId: "Eh00_rniF8E", workoutText: "An exercise in which a person lies facing the floor and, keeping their back straight, raises their body by pressing down on their hands.", color: UIColor.flatBlueColor())
        workouts.append(wk3)

        
        let wk5 = Workout(title: "Step-ups onto a chair", videoId: "kM2FfDIwsao", workoutText: "To do a step-up, position your chair in front of your body. Stand with your feet about hip-width apart, arms at your sides. Step up onto the seat with one foot, pressing down while bringing your other foot up next to it. ", color: UIColor.flatGreenColor())
        workouts.append(wk5)

        let wk6 = Workout(title: "Squats", videoId: "mGvzVjuY8SY", workoutText: "Crouch or sit with one's knees bent and one's heels close to or touching one's buttocks or the back of one's thighs.", color: UIColor.flatNavyBlueColor())
        workouts.append(wk6)

        let wk7 = Workout(title: "Triceps dips on a chair", videoId: "0326dy_-CzM", workoutText: "Triceps dips on a chair", color: UIColor.flatWatermelonColor())
        workouts.append(wk7)

    }
    
    func getWorkOuts() -> [Workout]{
        return workouts
    }
    
}

Lets see how we can populate some data so that we can see it in the UITableView. Create a new file called MainViewController.swift which should subclass UIViewController. Also make sure to conform to the UITableViewDataSource and UITableViewDelegate Protocol.

UITableViewDataSource protocol deal with providing data for the tableview. The required functions that need to be implemented are

- tableView:cellForRowAtIndexPath:
- tableView:numberOfRowsInSection:

cellForRowAtIndexPath return an instance of UITableViewCell that will be displayed in the tableView along with necessary data to display. (Can be a basic tableview cell or a custom tableview cell)

numberOfRowsInSection will tell how many rows are present in the tableview for that particular section.

UITableView Delegate protocol methods deals with the way the UITableView appears. Like the height of the row, the header/footerview for a section or what needs to be done when we select a UITableViewCell.

With the necessary information about the protocol, lets move on to the next section. Select the previously designed storyboard, then select the controller -> go to the identity inspector and select the custom class as MainViewController.

Open the assistant editor with the viewcontroller and the MainViewController.swift file open next to each other CTRL+DRAG from uitableview to the file and create a IBOutlet called tableView. Like the way we did for overlaying a UIView on top of the index label in the previous part. Drag a UIView on top of the UITableView and pin it to the bottom of the UITableView ( Make the height as 55). Also drag a button on top of the UIView can call it as Start Workout

Setup the delegate

    
  self.tableview.delegate = self
  self.tableview.datasource = self

Most of the other part is pretty boilerplate code where we need to implement the numberOfRowsInSection and cellForRowAtIndexPath.

//
//  MainViewController.swift
//  SevenMinWorkOut
//
//  Created by Shrikar Archak on 4/19/15.
//  Copyright (c) 2015 Shrikar Archak. All rights reserved.
//

import UIKit

class MainViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var tableView: UITableView!
    let manager = WorkoutDataSource()
    var workouts = []
    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.delegate = self
        self.tableView.dataSource = self
        self.workouts = manager.getWorkOuts()
    }
    

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.workouts.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        let workout = self.workouts[indexPath.row] as? Workout
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? WorkoutCell!
        cell!.textCell?.text = workout?.title
        cell!.backgroundColor = workout?.color
        cell!.countLabel.text = "\(indexPath.row+1)"
        cell!.selectionStyle = UITableViewCellSelectionStyle.None
        return cell!
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if(segue.identifier == "detailview"){
            let cell = sender as? WorkoutCell
            let indexPath = tableView.indexPathForCell(cell!)
            let nvc = segue.destinationViewController as? UINavigationController
            if let tmp = workouts[indexPath!.row] as? Workout{
                let dvc = nvc?.topViewController as! DetailViewController
                dvc.workout = tmp
            }
            
        }
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        self.tableView.contentInset = UIEdgeInsetsMake(0,0,55,0)
    }
    
}

One key thing to note here is the viewDidLayoutSubviews method. Since we had a uiview fixed to the bottom of the table view. We won’t be able to see the last row of the UITableView since this view covers it. To avoid that problem we need to set the contentInsets at the bottom with the value equal to the height of the UIView which in our case is 55.

Lets go ahead and implement the DetailViewController. Drag a UILabel and UIView on to the DetailViewController. Set the Autolayout constraints for the UILabel.
Also select the UIView and change the class to YTPlayerView. Now CTRL+DRAG from the label and the uiview to the DetailViewController as detailText and playerView.

7min19

In viewDidLoad we will preload the video and also customize the navigation controller

//
//  DetailViewController.swift
//  SevenMinWorkOut
//
//  Created by Shrikar Archak on 4/19/15.
//  Copyright (c) 2015 Shrikar Archak. All rights reserved.
//

import UIKit

class DetailViewController: UIViewController {

    @IBOutlet weak var detailText: UILabel!
    var workout: Workout!
    @IBOutlet weak var playerView: YTPlayerView!
    let playerVars = ["playsinline":"1"]
    override func viewDidLoad() {
        super.viewDidLoad()
  
        self.playerView.loadWithVideoId(workout.videoId, playerVars: playerVars)
        self.view.backgroundColor = workout.color
        self.detailText.text = workout.workoutText
        self.title = workout.title
        self.navigationController?.navigationBar.barTintColor = workout.color
        self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    @IBAction func close(sender: AnyObject) {
        self.dismissViewControllerAnimated(true, completion: nil)
    }

}

Checkout the next part of the tutorial here 7 min workout app flow

About the author

Shrikar

Backend/Infrastructure Engineer by Day. iOS Developer for the rest of the time.

  • Anthony

    Hi Mr. Archak,

    Thank you for taking the time to put this tutorial together.

    I have a couple of questions, for Workout.swift and WorkoutDataSource.swift, what are these classes a subclass of?

    Thanks.

/* ]]> */