[Swift] I implemented a Twitter timeline in UITableView (Part 1)

Create a timeline with UITableView.

I think it's easy to use UITableView itself once you get used to it, I think it's a little difficult to create a custom Cell.

This time, I will implement the timeline from 1 in the project I just created.

Also, because I am not good at explaining, the article has become quite long. Therefore, it is divided into 3 articles in total.

It would be helpful if you could let it flow with a light feeling.

The GitHub repository will be ** here **.

Create a Table View

The first thing to do **-Define a simple TableView -Create a custom cell -Link TableView and custom cells ** I would like to go.

First, instantiate the UITableView class. Select the size is the full width of the view and the style is plain.

ViewController.swift



let tableView = UITableView(frame: self.view.bounds, style: .plain)

Next, add two protocols. The protocols to add are UITableViewDelegate and UITableViewDataSource.

ViewController.swift



class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {・ ・ ・}

If you comply with the two protocols, an error will occur, so fix it. Then the following method will be defined.

ViewController.swift



    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        <#code#>
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        <#code#>
    }

The first method specifies how many cells to place in the ** section **. The return type is Int type and returns the number of cells.

This time, I tentatively declared an array called item. The return value is the number of elements in the array item.

ViewController.swift


    let item = ["Cell 1", "Cell 2", "Cell 3", "Cell 4", "Cell 5"]

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return item.count
    }

The second method defines the information when constructing the cell.

Now I want to temporarily add the element of the array item to the textLabel of the cell.

ViewController.swift



    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
        cell.textLabel?.text = item[indexPath.row]
        return cell
    }

I wrote the process in the requested method, The data cannot be reflected as it is.

In viewDidLoad with tableView.delegate = self Write tableView.dataSource = self.

By describing this You can delegate the processing of the first compliant protocol to yourself.

ViewController.swift



    override func viewDidLoad() {
        super.viewDidLoad()
        let tableView = UITableView(frame: self.view.bounds, style: .plain)
        tableView.delegate = self
        tableView.dataSource = self
    }

Finally, write view.addSubview because it needs to be reflected in the view.

ViewController.swift



    override func viewDidLoad() {
        super.viewDidLoad()

・ ・ ・

        view.addSubview(tableView)
    }

At this point, try starting the simulator once.

Then you can see that the items in the array are stored in the cell in order. スクリーンショット 2021-01-01 10.27.23.png

This time it's a Twitter timeline, so you need to create a custom cell.

How to make a custom cell You can either ** write it directly in the view controller ** or use ** .xib **.

This time, we will create a custom cell by using the latter .xib method.

I create the file as usual, Set Subclass to UITableViewCell and check Also create XIB file. スクリーンショット 2021-01-01 10.51.20.png

Then two files will be created. スクリーンショット 2021-01-01 10.53.32.png

Place the object in a file with the extension .xib on the left side of the screen, Describe the process in a file with the extension .swift on the right side of the screen.

It's a little rough, but we'll implement the UI. Implemented the icon, name, how many minutes ago the tweet time, the content of the tweet, and the four buttons below.

The processing when pressing the four buttons below is not implemented this time, I would like to implement other parts.

However, I will make it a specification to output with print () later so that you can see that it was pressed.

Defined in the newly created class, awakeFromNib () is Called immediately after being loaded from a Storyboard or nib file.

setSelected (_ selected: Bool, animated: Bool) is Performs the state animation processing of the selected state and the normal state. スクリーンショット 2021-01-01 11.15.45.png

A method that creates a cell for ViewController.swift Specifies to generate cells based on this custom cell.

First, set the Identifier so that you can reference the custom cell. Select the cell in CustomTableViewCell.xib and the Identifier will be displayed in the editor on the right.

Set any name for this Identifier. スクリーンショット 2021-01-01 12.35.06.png

After setting the identifier, write the following in viewDidLoad of ViewController.swift. (It does not have to be described in the absolute viewDidLoad.) Describe the identifier set earlier in forCellReuseIdentifier.

ViewController.swift



    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "customCell")
        
    }

Then modify inside cellForRowAt.

I used to instantiate the UItableViewCell class, Change to refer to customCell.

After as!, Please use the file name of your custom cell.

ViewController.swift



    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! CustomTableViewCell
        cell.content.text = self.item[indexPath.row]
        return cell
    }

So far, the simulator looks like this: You can see that the contents of the custom cell are reflected. スクリーンショット 2021-01-01 12.48.24.png

However, if it is left as it is, even if there are many tweets as shown in the image below, all will not be displayed. If it is Twitter, I think that the vertical width will increase according to the content of the tweet. スクリーンショット 2021-01-01 12.54.56.png

Next, the height is automatically adjusted. Constraint each object.

Since the height of the label is automatically adjusted, restrictions are placed on the top, bottom, left, and right. スクリーンショット 2021-01-01 13.00.42.png

Then change the number of lines in the Label from the default of 1 to 0. スクリーンショット 2021-01-01 13.01.20.png

By doing so, the height is automatically adjusted according to the number of characters. スクリーンショット 2021-01-01 13.05.23.png

It's getting pretty Twitter-like!

For the rest, I would like to add the following functions. ** ・ Show how many minutes ago you tweeted ・ When each button is pressed, the fact that it was pressed is output to the log. -Added the function to open the details screen of a tweet when you tap it **

First, I would like to implement "** Show how many minutes ago was tweeted **". As an implementation method, I would like to subtract the tweeted time from the current time and put out a few minutes ago.

This time, I will play with the array item a little and explicitly define the time when it was tweeted. Also, I made it possible to store the name as well.

ViewController.swift



    var item = [
        ["content": "2020\n is\n It's over.\n this year too\n good\n one year\It was n.", "date": Date().timeIntervalSince1970 - 313123123, "name": "Saito"],
        ["content": "Have a nice year.", "date": Date().timeIntervalSince1970 - 123212312, "name": "Honda"],
        ["content": "Happy New Year.", "date": Date().timeIntervalSince1970 - 20000000, "name": "Suzuki"],
        ["content": "I will give you 10,000 yen for New Year's gifts.", "date": Date().timeIntervalSince1970 - 2323232, "name": "Kawasaki"],
        ["content": "I will study harder in 2021.", "date": Date().timeIntervalSince1970 - 13213, "name": "Mitsubishi"],
        ["content": "This year is a real nuisance, so I'll tighten my mind.", "date": Date().timeIntervalSince1970 - 3333, "name": "Toyota"],
        ["content": "I look forward to working with you again this year.", "date": Date().timeIntervalSince1970 - 10, "name": "Takeda"]
    ]

Next, I modified the method for forming cells.

I am getting the current time with let now = Date (). TimeIntervalSince1970.

let past = item [indexPath.row] ["date "] as! TimeInterval is Getting the value of the key value:" date " of the array at the same number as the row in the cell of the array item.

The part of cell. ***. Text = is You are assigning a value to a custom cell property.

In addition, the following is defined in viewDidLoad. The sequence is reversed in the array with item = item.reversed (). Since the latest contents are added to the end, the order is reversed so that the beginning is the latest.

ViewController.swift



    override func viewDidLoad() {
        super.viewDidLoad()
        item = item.reversed()
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! CustomTableViewCell
        let now = Date().timeIntervalSince1970
        
        let past = item[indexPath.row]["date"] as! TimeInterval
        cell.time.text = timeCheck(now: now, past: past)
        cell.content.text = (item[indexPath.row]["content"] as! String)
        cell.name.text = (item[indexPath.row]["name"] as! String)
        
        return cell
    }

The timeCheck (now :, past :) method that appeared in the code above, It will be the original method I created.

This method specifies the time at the time of posting and the current time as arguments. Calculate how long ago from the difference obtained by subtracting the past time from the current time.

Twitter is expressed abstractly as before. So I made it possible to return with an expression suitable for it.

ViewController.swift



    func timeCheck(now: TimeInterval, past: TimeInterval) -> String {
        let timeDiff = Int(now - past)
        
        let year = timeDiff / 31104000
        let month = timeDiff / 2592000
        let day = timeDiff / 86400
        let hour = timeDiff / 3600
        let minute = timeDiff / 60
        let second = timeDiff
        
        if year != 0 {
            return "\(year)year ago"
        } else if month != 0 {
            return "\(month)Months ago"
        } else if day != 0 {
            return "\(day)A day ago"
        } else if hour != 0 {
            return "\(hour)by the time"
        } else if minute != 0 {
            return "\(minute)Minutes ago"
        } else {
            return "\(second)Seconds ago"
        }
        
    }

The simulator looks like this: The height is automatically adjusted, and the time is displayed as ○ hours ago. スクリーンショット 2021-01-01 16.03.33.png

Next, we will implement "** When each button is pressed, the fact that it was pressed is output to the log **".

Specify a value for the tag of each button, and make a conditional branch with the tag of which button was pressed.

CustomTableViewController.swift



    @IBAction func actionButton(_ sender: Any) {
        guard let button = sender as? UIButton else {
            return
        }
        
        switch button.tag {
        case 1:
            print("The reply button has been pressed.")
        case 2:
            print("The retweet button was pressed.")
        case 3:
            print("The like button has been pressed.")
        case 4:
            print("The share button has been pressed.")
        default:
            print("Error:I don't know which button was pressed.")
        }
    }

The execution result is as follows.

スクリーンショット 2021-01-01 16.18.04.png When you press each button, the appropriate log will be output. Now you can implement the process when you press the button!

Next, implement "** Add a function to open the detail screen of the tweet when you tap it **".

The method that detects which cell was tapped when the cell was tapped Do this with didSelectRowAt.

ViewController.swift



    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        <#code#>
    }

This time I would like to make a screen transition and display the details on a separate screen. First, describe the process to perform screen processing on a separate screen.

Add an object from + at the top right of the screen. You can add a new view by searching for ViewController and dragging and dropping. スクリーンショット 2021-01-01 19.48.23.png

Next, create a new file.

Select File> New> File> Cocoa Touch Class. This time, I named it DetailViewController.swift. スクリーンショット 2021-01-01 19.51.25.png

Associate DetailViewController.swift with ViewController. Enter the file name to be linked to the Class part on the upper right of the screen. スクリーンショット 2021-01-01 19.50.41.png

Associate NavigationController with ViewController. Select View Controller> Editor> Embed in> Click Navigation Controller スクリーンショット 2021-01-01 20.39.00.png

Then the Navigation Controller will be added to the left of the view controller.

Next, link the view controller and Detail view controller. Drag and drop the mark on the left of the view controller while holding down the ** control key **.

Then a menu will appear, so select show. It's OK if the two views are connected by an arrow! スクリーンショット 2021-01-01 20.41.27.png

Next, when the screen transitions from the view controller, the screen transitions with reference to the identifier, so We will define the identifier of DetailViewController. スクリーンショット 2021-01-01 20.56.35.png

Now, let's describe the screen transition in the didSelectRowAt introduced earlier.

The processing of let detailVC = ・ ・ ・ is It is a process to get DetailViewController from the instance of Storyboard.

I don't know which view controller is the detail view controller, so The DetailViewController is identified by the (identifier:" detailView ") part.

navigationController? .PushViewController (detailVC, animated: true) is It is a process to perform screen transition. Specify the screen transition destination with the first argument.

tableView.deselectRow (at: indexPath, animated: true) is When I tap a cell, the color of that cell changes. The color is restored after the screen transition.

You can see the difference by trying with and without writing!

ViewController



    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let detailVC = storyboard?.instantiateViewController(identifier: "detailView") as! DetailViewController
        navigationController?.pushViewController(detailVC, animated: true)
        tableView.deselectRow(at: indexPath, animated: true)
    }

If you execute the processing up to this point, you can confirm that the screen transition has been completed.

I would like to finish the first one so far.

Since the minimum Twitter implementation is done at this point Please try to make corrections personally from here!

Also, if you have any better code, please point it out.

Thank you for watching until the end.

Click here for more -> [Swift] I implemented a Twitter timeline in UITableView (Part 2)

Recommended Posts

[Swift] I implemented a Twitter timeline in UITableView (Part 2)
[Swift] I implemented a Twitter timeline in UITableView (Part 1)
[Swift] I implemented a Twitter timeline in UITableView (Part 3)
I want to use FireBase to display a timeline like Twitter
I want to remove the top margin in Grouped UITableView (swift)
I tried a calendar problem in Ruby
Creating a matrix class in Java Part 1
I tried embedding a formula in Javadoc
I made a primality test program in Java
I wanted to make (a == 1 && a == 2 && a == 3) true in Java
I wrote a primality test program in Java
I made a rock-paper-scissors game in Java (CLI)
I made a Ruby extension library in C
[Swift] [Beginner] I searched a lot about #selector
What I learned in Java (Part 2) What are variables?
I wrote a prime factorization program in Java
I made a function to register images with API in Spring Framework. Part 1 (API edition)