Skip to content

Latest commit

 

History

History

complex

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
15 minutes tutorial

Collection View inside a Table View

Build a complex API-backed collection inside a table in minutes, right from Interface Builder!

Example: Messenger-like app

API URL: https://api.abstractlayer.com/demo/complex/get_contacts_and_messages

Table view

Integrate the framework

If you haven't already added the framework to your Xcode project, follow this tutorial.

Build the UI

  • Open Main.storyboard and delete the default view controller

  • Drag an instance of UITableViewController From the menu bar choose:

Editor → Embed in → Navigation Controller

  • Click on your navigation controller then check the box that says Is initial View Controller from the Attributes Inspector

Table view

The table view will have 2 sections (contacts & messages) each is represented by a prototype cell.

Follow the steps below to achieve the following storyboard design:

Table view

Design messages prototype cell:

  • Click on your prototype cell, and set the cell identifier to messageCell in the Attributes Inspector

  • Drag the prototype cell in your storyboard from the bottom to increase its height to 80

  • Design the table view's cell to match the design

    Table view
    • UIImageView for the user image (size 50x50)
    • UILabel for the name label (Font size 13, semibold, black)
    • UILabel for the last message label (Font size 12, regular, dark gray)
    • UILabel for the date label (Font size 12, regular, light gray)
  • Choose 2 lines for last message label

Table view

  • To design the header of the cell, drag another UITableviewCell prototype cell from the Object Library and set its cell identifier to messageHeader.

  • Drag a UILabel to this new header cell and rename it Messages

Design contacts prototype cell:

  • Drag an instance of UITableViewCell from the Object Library and set its identifier to contactCell

  • Drag a UICollectionView and make it take the full width of the cell.

Design the collection view cell:

Table view

  • UIImageView for the user image (size 50x50)

  • UILabel for the name label (Font size 12, regular, dark gray)

  • To design the header of the cell, drag another UITableviewCell prototype cell from the Object Library and place it on top of the message cell, then set its cell identifier to contactHeader.

  • Drag a UILabel to this new header cell and rename it Contacts

Before you move on, make sure to set the UI elements constraints properly!

Magic (Auto data-binding)

It's time to bind data between the JSON document and the UI elements.

Table View:

Table view

  • Navigate to your Attributes Inspector, and you'll find a list of new attributes

  • Paste the URL you just copied into the new Url field

  • Each section has a different cell identifier, so type in both separated by a comma. contactCell,messageCell

  • Each section has its own header identifier, so type in both separated by a comma. contactHeader,messageHeader

  • As for the JSON root, the first section API is handled by the collection view itself, so it must be left empty, which is represented by a dash (-). This means that the first section consists of only 1 row that is not to be handled by the table parsed API itself.

  • The second section, however, has messages as the root of the JSON to be parsed. (Check the API to see how the response looks like), this is why you need to specify it there. The end result is -, messages which means that the first section will be handled elsewhere, while the second section of the table will be handled by the parsed array.

Table view

Collection View:

Same steps apply for the collection view, so click on it, and set its class from UICollectionView to ALCollectionView in the Identity Inspector

Table view

  • Navigate to your Attributes Inspector, and fill out the following attributes

Table view

Your table view and collection view are now ready to process the API. It's time to match the JSON keys with the UI elements to fill the data automatically.

The next section applies for both prototype cells, so make sure you apply it on both the contact and message cells.

User Image

  • Click on your UIImageView and change its class to ALImageView in the Identity Inspector

Table view

  • Navigate to your Attributes Inspector and set the following:

JSON key: Type in image_url in the Json Key field so that Abstract Layer can automatically load the image using its URL value

Placeholder: You can choose an image from your bundle as a placeholder image while your real image gets downloaded.

Disk Cache: By default, all images handled by Abstract Layer will be downloaded and cached both in memory and on disk. You can turn off saving on disk by choosing NO for Disk Cache.

Cache Policy: The default Cache Policy is Least Recently Used (LRU). Other available policies are (LRU, LFU, FIFO, LIFO).

Circular Option Also, to get a circular user image, turn the circular option ON.

Table view

Name Label

  • Click on the name label and change its class to ALLabel in the Identity Inspector

Table view

  • Type in name in the Json Key field to automatically match the JSON value with the name label

Table view

Last Message Label

  • Click on the last message label and change its class to ALLabel in the Attributes Inspector

  • Type in last_message in the Json Key field

Date Label

  • Click on the date label and change its class to ALDateLabel in the Attributes Inspector

Table view

  • Type in timestamp in the Json Key field

  • Type in MM/dd/yyyy in the Output Format field to get the desired date format displayed.

The format should abide by Apple's DateFormatter rules

Table view

Handle any kind of error by checking the Error handling section

Table view

Run the project, and there you go! MAGIC!

Hold on

This looks great so far, but we're sure you've got many questions about how far can Abstract Layer go.

Error Handling

Handle all kind of errors when loading your table view


Passing Data

You have FULL access to parsed data, so passing it is very simple


Parameters

Be it Header or Body, Static or Dynamic, it's all in Interface Builder


Pagination

Enabling pagination takes less than a minute


Complex JSON

Any form of JSON is supported, no matter how complex it is


Loader

Enable loaders and pull-to-refresh with 2 clicks


The example below shows how you can fully customize the table view example by modifying the parsed data before displaying it.

Auto-parsing to models

Normally, you would create both a Message and a Contact class to pass data between different view controllers.

Create a class called Message:

Objective-C Swift

import Foundation

@objcMembers class Message: NSObject { var id: String? var name: String? var lastMessage: String? var timestamp: NSNumber? }


#import 

@interface Message : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *lastMessage; @property (nonatomic, copy) NSString *imageUrl; @property (nonatomic, strong) NSDate *date; @end

Now you only have to type in the name of your class in your attributes inspetor.

Table view

That's all. Abstract Layer will now auto-parse the JSON document into your own custom classes. Check the section below to see a use case.

Display "Today" & "Yesterday" instead of full "MM/dd/yyyy" date

Remember: ALTableView is a subclass of UITableView

Remember: You have FULL access to request and response. Check Custom Request, Pasing Data & Auto Parsing Models

  • Create a new class, call it CustomTableViewCell

Table view

  • Set the Table view cell class to CustomTableViewCell

Table view

  • Control-drag your date label to the class as a new outlet and call it dateLabel

  • Don't forget to import AbstractLayer to the class's header

Table view

  • Create a new class, call it TableViewController and subclass it form UITableViewController

Table view

  • Set your table view class in storyboard to TableViewController

Table view

Objective-C Swift

import UIKit
import AbstractLayer

class TableViewController: UITableViewController {

// // MARK: Lazy load // private lazy var today:Double = { var calendar = NSCalendar.current calendar.timeZone = NSTimeZone(abbreviation: "UTC")! as TimeZone return calendar.startOfDay(for: Date()).timeIntervalSince1970 }()

private lazy var yesterday:Double = { var calendar = NSCalendar.current calendar.timeZone = NSTimeZone(abbreviation: "UTC")! as TimeZone return calendar.startOfDay(for: Date().addingTimeInterval(-86400)).timeIntervalSince1970 }()

// // MARK: Table view datasource // override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let table = tableView as! ALTableView

if (indexPath.section == 0) {
  return table.cellForRow(at: indexPath)!
}

let cell = table.cellForRow(at: indexPath) as! CustomTableViewCell

// Get item info
let array = table.array[1] as! [Message]
let item = array[indexPath.row] // Get item dictionary
 let timestamp = item.timestamp as! Double

// Date calculations
if timestamp > today {
  cell.dateLabel?.text = "Today"
} else if timestamp > yesterday {
  cell.dateLabel?.text = "Yesterday"
}

return cell

}

override func numberOfSections(in tableView: UITableView) -> Int { return tableView.numberOfSections }

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return tableView.numberOfRows(inSection: section) }

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let table = tableView as! ALTableView return table.heightForRow(at: indexPath) }

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let table = tableView as! ALTableView return table.viewForHeader(inSection: section) }

}


#import "TableViewController.h"
#import <AbstractLayer/AbstractLayer.h>
#import "CustomTableViewCell.h"

@interface TableViewController () @property (nonatomic, assign) NSTimeInterval today,yesterday; @end

@implementation TableViewController

// // UITableView Datasource //

  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [tableView numberOfRowsInSection:section]; }

  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    CustomTableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    // Get item info NSArray *array = [(ALTableView *)tableView array].firstObject; NSDictionary *item = array[indexPath.row]; CGFloat timestamp = [item[@"timestamp"] doubleValue];

    // Check dates if (timestamp > self.today) { cell.dateLabel.text = @"Today"; } else if (timestamp > self.yesterday) { cell.dateLabel.text = @"Yesterday"; } return cell; }

  • (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return [(ALTableView *)tableView heightForRowAtIndexPath:indexPath]; }

// // Lazy Instantiation //

  • (NSTimeInterval)today { if (!_today) { NSCalendar * calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; [calendar setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; _today = [calendar startOfDayForDate:[NSDate date]].timeIntervalSince1970; } return _today; }

  • (NSTimeInterval)yesterday { if (!_yesterday) { NSCalendar * calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; [calendar setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; _yesterday = [[calendar startOfDayForDate:[NSDate date]] dateByAddingTimeInterval:-86400].timeIntervalSince1970; } return _yesterday; }

@end

Table view

  • Run the app

So is it really 100% customizable? Can I actually use Abstract Layer in production?

Abstract Layer is as customizable as anything built from scratch.

As you've seen in the example above, the framework is fully customizable since it's built on top of native Apple UIKit components like UITableView & UICollectionView.

To customize any aspect of Abstract Layer, simply:

  • Subclass any of Abstract Layer classes to do your customizations
  • Conform to the delegate and datasource protocols just as you would do with a regular UITableView & UICollectionView

Abstract Layer is not a prototyping tool, it's strictly a production-level framework. All of our customers rely on Abstract Layer in their live apps.

Where to go next?

Abstract Layer supports lots of features on ALTableView, so make sure to check them all out!

<tr>
<td class="row-text"><b><a href="/#/menu/table-view/xib" target="_blank">XIB</a></b><p>Reuse cells by designing them in their own XIB</p><br/></td> 
<td class="row-text"><b><a href="/#/menu/table-view/authentication" target="_blank">Authentication</a></b><p>JWT is handled automatically once you provide your keys</p><br/></td> 
<td class="row-text"><b><a href="/#/menu/table-view/custom-cases" target="_blank">And More...</a></b><p>Check out the dedicated section for custom cases</p><br/></td> 
Error Handling

Handle all kind of errors when loading your table view


Passing Data

You have FULL access to parsed data, so passing it is very simple


Parameters

Be it Header or Body, Static or Dynamic, it's all in Interface Builder


Pagination

Enabling pagination takes less than a minute


Complex JSON

Any form of JSON is supported, no matter how complex it is


Loader

Enable loaders and pull-to-refresh with 2 clicks


Download the final project and try it out