HW#38 TableView & Custom Cell- MovieList & Passing data

Hello everyone, today I’ll show you how to do tableView & customCell in Swift’s storyboard.

Here is reference base on Peter Pan article:

Summary:

I find the most interesting part of coding to be solving problems, creating things, and making them happen. I know that the tableView is quite important for iOS development, so I’m trying to use both the storyboard and programmatic methods to implement a tableView.

Initially, I wanted to use AVPlayerViewController to show movie trailers after a user taps a tableViewCell.

However, due to copyright issues, I’ve decided to use WebKit to display movie trailers on YouTube, which seems like a safer choice for showing movies without any risk.

Subject:

  1. Create the data model.
  2. Custom Cell by Storyboard.
  3. Add segment Control in NavigationBar
  4. Setup tableView.
  5. Passing Data from tableViewCell to webView.
  6. Setup Web view and setup forward Button for last page & back button for next page.

1. Create the data models:

Create a struct named MovieList and define the movieName / movieTitle / url as strings.

struct MovieList {
var movieName : String
var movieTitle: String
var url: String
}

I created two different data arrays for different subjects, one for startWar another for starTrek, which are my favorite movie series.

StarWar:

let movieList = [
MovieList(movieName: "Star Wars episode 1", movieTitle: "The Phantom Menace", url: "https://www.youtube.com/watch?v=bD7bpG-zDJQ"),
MovieList(movieName: "Star Wars episode 2", movieTitle: "Attack of the Clones", url: "https://www.youtube.com/watch?v=gYbW1F_c9eM"),
MovieList(movieName: "Star Wars episode 3", movieTitle: "Revenge of the Sith", url: "https://www.youtube.com/watch?v=5UnjrG_N8hU"),
MovieList(movieName: "Star Wars episode 4", movieTitle: "A New Hope", url: "https://www.youtube.com/watch?v=vZ734NWnAHA"),
MovieList(movieName: "Star Wars episode 5", movieTitle: "The Empire Strikes Back", url: "https://www.youtube.com/watch?v=JNwNXF9Y6kY"),
MovieList(movieName: "Star Wars episode 6", movieTitle: "Return of the Jedi", url: "https://www.youtube.com/watch?v=7L8p7_SLzvU"),
MovieList(movieName: "Star Wars episode 7", movieTitle: "The Force Awakens", url: "https://www.youtube.com/watch?v=sGbxmsDFVnE"),
MovieList(movieName: "Star Wars episode 8", movieTitle: "The Last Jedi", url: "https://www.youtube.com/watch?v=Q0CbN8sfihY"),
MovieList(movieName: "Star Wars episode 9", movieTitle: "The Rise of Skywalker", url: "https://www.youtube.com/watch?v=8Qn_spdM5Zg")
]

StarTrek:

let starTrekMovieList = [
MovieList(movieName: "Star Trek", movieTitle: "The Motion Picture",
url: "https://www.youtube.com/watch?v=zrXvBaFFu80"),
MovieList(movieName: "Star Trek II", movieTitle: "The Wrath of Khan",
url: "https://www.youtube.com/watch?v=WCpYqWAIwFA"),
MovieList(movieName: "Star Trek III", movieTitle: "The Search for Spock",
url: "https://www.youtube.com/watch?v=mkJ3--2K7yo"),
MovieList(movieName: "Star Trek IV", movieTitle: "The Voyage Home",
url:"https://www.youtube.com/watch?v=zZH3OD9d9Sc&list=PLZbXA4lyCtqp8DAiaHxYkC7u1drvVHMCu"),
MovieList(movieName: "Star Trek V", movieTitle: "The Final Frontier",
url: "https://www.youtube.com/watch?v=7S5TDrAWBd8"),
MovieList(movieName: "Star Trek VI", movieTitle: "The Undiscovered Country",
url: "https://www.youtube.com/watch?v=VPz-6HuM8Sc"),
MovieList(movieName: "Star Trek", movieTitle: "Generations",
url: "https://www.youtube.com/watch?v=w9Ubnn2vP6Y"),
MovieList(movieName: "Star Trek", movieTitle: "First Contact",
url: "https://www.youtube.com/watch?v=D7KCb-O20Fg"),
MovieList(movieName: "Star Trek", movieTitle: "Insurrection",
url: "https://www.youtube.com/watch?v=I7cb4ZK9WV4"),
MovieList(movieName: "Star Trek", movieTitle: "Nemesis",
url: "https://www.youtube.com/watch?v=9wD5S1mWl0I"),
MovieList(movieName: "Star Trek", movieTitle: "Star Trek",
url: "https://www.youtube.com/watch?v=a3VbUpscgOc"),
MovieList(movieName: "Star Trek", movieTitle: "Into Darkness",
url: "https://www.youtube.com/watch?v=QAEkuVgt6Aw"),
MovieList(movieName: "Star Trek", movieTitle: "Beyond",
url: "https://www.youtube.com/watch?v=rf5_lKS42mc")
]

2. Custom Cell by Storyboard:

MovieTableViewCell:

I’m using storyboard to create a custom cell and setup the auto-layout for cell.

  • imageView
  • titleLabel
  • detailLabel

Create a function named nib that returns a UINib object with the nibName MovieTableViewCell.

What is nib?(we could throw back to old documentation from Apple)

Set up a string identifier named MovieTableViewCell so that we can use this identifier in the next steps.

import UIKit

class MovieTableViewCell: UITableViewCell {

@IBOutlet weak var movieImageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var detailLabel: UILabel!

static let identifier = "MovieTableViewCell"

static func nib() -> UINib {

return UINib(nibName: "MovieTableViewCell", bundle: nil)
}

override func awakeFromNib() {
super.awakeFromNib()
// Initialization code

movieImageView.layer.cornerRadius = movieImageView.bounds.height / 2
movieImageView.clipsToBounds = true

titleLabel.adjustsFontSizeToFitWidth = true
detailLabel.adjustsFontSizeToFitWidth = true

}

override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)

// Configure the view for the selected state
}

}

Explain Static:

Reference:

3. Add custom segment Control in NavigationBar

Create a function named segmentedControl to add a UISegmentedControl into the titleView of the navigationItem.

 func segmentedControl () {

let items: [String] = ["Star Wars", "Star Trek"]
segmentedController = UISegmentedControl(items: items)

segmentedController.selectedSegmentIndex = 0
print(segmentedController.selectedSegmentIndex)
navigationItem.titleView = segmentedController
segmentedController.addTarget(self, action: #selector(segmentedControlValueChanged) , for: .valueChanged)
}
Animation for segment controls.

4. Setup tableView:

Setup tableView by storyboard.

Before creating a tableView, it's important to understand the resources needed for its setup, namely dataSource and delegate.

The dataSource is responsible for providing the data that the table will display. This includes specifying the number of rows, the data each cell contains, and other data-related aspects.

On the other hand, the delegate handles events related to the appearance and functionality of the tableView.

These responsibilities include responding to user interactions, specifying the height of rows, and other aspects of the table's behavior and style. Therefore, at the beginning, our code needs to properly configure both the dataSource and the delegate for the tableView.

tableView.dataSource = self
tableView.delegate = self

I have created a function named setupTableViewDelegate to configure the delegate and dataSource of the tableView.

In addition, this function registers the custom tableView cells, StarTrekTableViewCell and MovieTableViewCell, which were created in the previous step under CustomCell.

  func setupTableViewDelegate () {

self.navigationItem.title = ""
tableView.dataSource = self
tableView.delegate = self
tableView.rowHeight = 100

self.tableView.isEditing = false

// Register the starWar cell to xib cell.
tableView.register(MovieTableViewCell.nib(), forCellReuseIdentifier: MovieTableViewCell.identifier)

// Register the starTrek cell to xib cell.
tableView.register(StarTrekTableViewCell.nib(), forCellReuseIdentifier: StarTrekTableViewCell.identifier)

segmentedControl ()

}

cellForRowAt:

  • Asks the data source for a cell to insert in a particular location of the table view.

Firstly, we create an instance named starWarCell by using the dequeueReusableCell(withIdentifier:) method of the UITableView.

This method dequeues a reusable cell with the specified identifier.

The cell is then downcast to the specific cell type MovieTableViewCell using as! for force casting, ensuring the cell is of the correct type.

   func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let starWarCell = tableView.dequeueReusableCell(withIdentifier: MovieTableViewCell.identifier, for: indexPath) as! MovieTableViewCell

let starTrekCell = tableView.dequeueReusableCell(withIdentifier: StarTrekTableViewCell.identifier, for: indexPath) as! StarTrekTableViewCell

self.navigationController?.navigationBar.prefersLargeTitles = true

switch segmentedController.selectedSegmentIndex {
case 0:
self.navigationItem.title = "Star War"
print("Star Wars is \(movieList.count)")
starWarCell.titleLabel.text = movieList[indexPath.row].movieName
starWarCell.detailLabel.text = movieList[indexPath.row].movieTitle
starWarCell.movieImageView.image = UIImage(named: movieList[indexPath.row].movieTitle)
case 1:
self.navigationItem.title = "Star Trek"
print("StarTrek is \(starTrekMovieList.count)")
starTrekCell.starTrekTitleLabel.text = starTrekMovieList[indexPath.row].movieName
starTrekCell.starTrekDetailTitleLabel.text = starTrekMovieList[indexPath.row].movieTitle
starTrekCell.posterImageView.image = UIImage(named: starTrekMovieList[indexPath.row].movieTitle)

return starTrekCell

default:
break
}
return starWarCell
}

Documentation:

numberOfRowsInSection(一個section顯示多少列):

  • Tells the data source to return the number of rows in a given section of a table view.

Use the MovieList array to return the count of items in it, which indicates the number of data entries in the MovieList array.

This is implemented in the numberOfRowsInSection method of the UITableView.

The method uses a segmentedController to determine which array's count to return.

If the selected segment index is 0, it returns the count of movieList. If the index is 1, it returns the count of starTrekMovieList.

In other cases, it defaults to returning the count of movieList.

    // numberOfRowsInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

switch segmentedController.selectedSegmentIndex {
case 0:
print("Case 0: numberOfRowsInSection is \(segmentedController.selectedSegmentIndex)")
return movieList.count
case 1:
print("Case 1: numberOfRowsInSection is \(segmentedController.selectedSegmentIndex)")
return starTrekMovieList.count
default:
print("numberOfRowsInSection is break")
return movieList.count
}
}

Documentation:

5. Passing Data from tableViewCell to webView:

Using segmentedContoller.selectedSegmentIndex for switching different content.

And use instantiateViewController(withIdentifier:) for passing data.

// didSelectRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

switch segmentedController.selectedSegmentIndex {
case 0:
urlAddress = movieList[indexPath.row].url
webVCNavigationString = movieList[indexPath.row].movieTitle
case 1:
urlAddress = starTrekMovieList[indexPath.row].url
webVCNavigationString = starTrekMovieList[indexPath.row].movieTitle

default:
break
}

let webVC = storyboard?.instantiateViewController(withIdentifier: "WebViewController") as? WebViewController

webVC?.url = urlAddress
webVC?.navigationTitle = webVCNavigationString

navigationController?.pushViewController(webVC!, animated: true)
print(urlAddress)
print(indexPath)
}

Documentation:

6. Setup Webview and setup forward Button & back button:

Before we setup the WebView, we could read the documentation from Apple.

We can simply follow the step-by-step instructions provided by Apple to complete this process.

    override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.navigationDelegate = self
webView.uiDelegate = self
view = webView
}

Documentation:

1. Setup Auto-layout:

Add the layout constraint for webView.

2. Load the webView from URL:

Create a url and setup it’s property as string? .

    var url: String?

Create a function named showWebsite that loads a web request into a WebView.

This function first creates a URL object from a string (url).

It then creates a URLRequest object using this URL. Finally, the function loads this request into a webView.

    func showWebsite() {
let myURL = URL(string: url!)
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}

3. Go back or Go forward:

Create a function named configureSegmentItem to set up the tintColor and to configure segmentedControl as a custom view for UIBarButtonItem.

Additionally, add a target to segmentedControl so that when the user taps it, the IDE can recognize which action is being triggered.

    func configureSegmentItem () {

segmentedControl.tintColor = .darkGray
segmentedControl.isEnabled = true
segmentedControl.selectedSegmentIndex = 0
segmentedControl.addTarget(self, action: #selector(selectSegmentControl), for: .valueChanged)

let segmentItem: UIBarButtonItem = UIBarButtonItem(customView: segmentedControl)
self.navigationItem.rightBarButtonItem = segmentItem
}

Create the selectSegmentControl action for website could go back and go forward, I’m using switch the segmentedControl.selectedSegementIndex to define the interger when is goBack and goForward.

    @objc func selectSegmentControl (_ sender: UISegmentedControl) {
switch segmentedControl.selectedSegmentIndex {

// backBtn
case 0:
print("Case 0")
if webView.canGoBack {
webView.goBack()
print("webView go back")
}

// forwardBtn
case 1:
print("Case 1")
if webView.canGoForward {
webView.goForward()
print("webView go forward")
}
default:
break
}

}

Documentation:

The idea for the goBack and goForward buttons was inspired by Apple’s documentation on customizing your app’s navigation bar.

It’s a really great example that helps you to explore more customization options for the navigation bar.

Reference:

TableView:

  • UITableView Tutorial with Custom Cells, XIB — 2023 iOS UIKit Swift 5
  • Passing data by using storyboard without any segue.
  • UITableView 介紹
  • dequeueReusableCell 傳入 cell reuse id 的幾種寫法

How to add a segmented control to a navigation bar ? (iOS)

  • How to set cornerRadius on a imageview inside a table view cell
  • 放大版的 navigation bar

https://medium.com/彼得潘的-swift-ios-app-開發問題解答集/ios-11-的放大版-navigation-bar-85c05824a14c

  • adding segmented control with in the Navigation Bar
  • Customizing Your App’s Navigation Bar

WebKit:

  • Show the webView by using Webkit framework.
  • com.apple.WebKit.WebContent drops 113 error: Could not find specified service

Here’s my GitHub repository link:

--

--