IQListKit

VIEW/UICollectionView controlled model.

User list

Sort by insertion

Bergen

IQListKit allows the use of UITableView/UICollectionView without implementing dataSource. Give the partition and its models a cell type and it takes care of the rest, including animating the adjustments.

For the iOS13 : Thanks to Apple for NSDiffableDataSourceSnapshot

For iOS12 and below: Thanks to Ryo Aoyama for DiffableDataSources.

Requirements

Library Language iOSMinimum Objective Minimum X code version
IQListKit (1.0.0) Quick iOS 13.0 Xcode 11
IQListKit (1.1.0) Quick iOS 9.0 Xcode 11

Swift version supports

5.0 and above

Installation with cocoa pods

IQListKit is available through CocoaPods. To install
, add the following line to your pod file:

pod ‘IQListKit’ (in English)

under IQListKit, 1.0.0.

Installation with source code

Drag the IQListKit directory from the demo project into your project.

Installation with Swift Package Manager

Swift Package Manager (SPM) is Apple’s dependency management tool. It is now supported by Xcode 11. So it can be used in all kinds of AppleOS projects. It can be used with other tools such as CocoaPods and Carthage.

To install IQListKit into your packages, add a reference to IQListKit and the target version in the dependencies section of your Package.fast file:

Import packageDescription

let package = package(name: UW_PROJECT_NAME, products : [],dependencies: [.package(url: https://github.com/hackiftekhar/IQListKit.git, from: 1.0.0)].

To install the IQListKit package through Xcode

If you want to learn how to use the presentation, download the PDF presentation here: PDF presentation

We will examine IQListKit using a simple example.

Suppose we need to display a list of users in UITableView, and for that we have such a user model:

to structure the user {

Let yourself go: In / / A unique identifier of each user
leaves the name: String //username
}

Before we start implementing, we need to learn about the Hashable protocol.

What is hashing? I have never used it before.

The Hashable protocol is used to determine the uniqueness of an object/variable. Technically, a hash type is a type that has a hash value as an integer that can be compared between different types.

Many types in the standard library correspond to hashes : String, int, float, double, boolean and even set values are chopped by default.
To validate the hash protocol, we need to modify our model a bit, as shown below:

// We have Int and String variables in structure
// We don’t need to manually validate the hash protocol
// It will work immediately by simply adding the hash protocol to the user in structure
: Hashable

Let yourself go: Name
: Channel
}

However, if we want to validate manually, we need to implement the feature hash (in hasher: inout hasher) and preferably also validate it in the Equatable protocol by implementing the static function == (lhs: User, rhs: User) -> Bool as shown below :

to structure the user: Hashable

func hash(in hash: inout Hasher) { //Manual confirmation for hash protocol
hash.combine(id)
}

static function == (lhs : User, rhs : User) -> Bool { //Manual confirmation of the Equatable
protocol lhs.id == rhs.id && lhs.name == rhs.name
}

Let yourself go: Name
: Channel
}

Now let’s get back to the implementation part. To use IQListKit, we need to perform a few steps:

Step1) Attach our UserCell using the IQModelableCellprotocol.

What is the IQModelableCell protocol and how can I confirm it?

The IQModelableCell protocol states that anyone who accepts me must expose a variable with a model name, and it can be any type that matches Hashable.

Suppose we have a UserCell:

UserCell Class : HOPEFUL TRAFFIC REPORT.

@IBOutlet var labelName : UILabel!
}

There are several ways to confirm this by simply opening a template with a variable name.

Method 1: Directly with our custom model

UserCell Class : ITUableViewCell, IQModelableCell.

@IBOutlet var labelName : UILabel!

Inverter model : User? //On custom model validates hashable protocol
}

Method 2: Prints our user template in the common name as Model

UserCell Class : ITUableViewCell, IQModelableCell.

@IBOutlet var labelName : UILabel!

typealias Template = User // Enter a user template under a common name

Inverter model : A model? //Model is user
}

Method 3: By creating a choppable structure in each cell (preferred)

This method is preferred because it is possible to use different parameters in the model

UserCell Class : ITUableViewCell, IQModelableCell.

@IBOutlet var labelName : UILabel!

Structure model : Hashable {
allows user : User
canShowMenu : Bool //Own parameters that can be controlled by the ViewController
let parameter2 : In //other configurable parameter
… etc. (if needed)
}

Inverter model : A model? // Our new structure template validates the
hash protocol }

Step 2) Connect the model to cell.

To do this, we can simply implement the doSet variable in our model.

UserCell Class : ITUableViewCell, IQModelableCell.

@IBOutlet var labelName : UILabel!

Inverter model : User? { // To simplify, we use the 1. Method
didSet {
guard let model = model else {
return
}

labelName.text = model.name
}
}
}

Step 3) Create and configure the IQList variable

Suppose we have such a UsersTableViewController:-.

UserTableSeeController class : EXECUTIVEViewController.

private var-users = [user]() // assuming an array of users is loaded from somewhere, for example. B. A response to an API call

//…

function loadDataFromAPI() {

//Get list of users of API
APIClient.getUsersList({[weak i] leads to

Result of the circuit {

cas .success(let users) :
self ?.users = users //refresh users array
self ?.refreshUI() //refresh data

case .failure(let error):
// handling error
}
}
}
}

We will now create an instance of IQList and provide it with a list of templates and cell types.
The listView parameter takes either UITableView or UICollectionView.
The delegateDataSource parameter is optional, but it is preferred if we want to have
perform additional matches in our cell before
is displayed, or to get reminders when the cell is clicked.

UserTableSeeController class : EXECUTIVEViewController.

//…

private lazy var list = IQList (listView : tableView, delegatesDataSource : self)

override viewDidLoad() {
super.viewDidLoad()

// Additional configuration if no element should be displayed
// list.noItemImage = UIImage(name: empty)
// list.noItemTitle = No element
// list.noItemMessage = No element is displayed here.
// list.noItemAction(title: reload, target: self, action: #selector(_ :))))
}
}

UserViewTableController Extension : IQListViewDelegateDataSource {
}

Step 4) Provide thetemplates with the cell types in IQList in the performUpdates method.

Let’s do this in a separate function called refreshUI

UserTableSeeController class : EXECUTIVEViewController.

//…

func refreshUI(animated:Bool = true) {

// This is the current method for reloading data.
//We can compare it to a tableView.reloadData()
//It does everything
list.performUpdates({)

// When using multiple sections, each section must be unique.
// This should also validate the hash, so we can also Int
// Like this `input section = IQSection(id: 1)`
leave section = IQSection(id: first)

// We also can specify header/footer and its size, they are optional
//let section = IQSection(ID: first,
// header: I header, header size: CGSize(width: OUTableView.automaticDimension, height: 30),
// footer: I footer, footer size: CGSize(width: OUTableView.automaticDimension, height: 50))

list.append(section)

//Count the list to display models in UserCell.

//The template is created with method 1 or method 2
list.append (UserCell.self, models: users, partition: partition)

/*
When a model is created with method 3
var models = [UserCell.Model]()

for users {
models.append(.init(user : user))
}
list.append(UserCell.self, models : models, section : section)
*/

// control whether or not changes are animated when reloading
}, animationDifference: animated, end: null)
}
}

Now, when our user array changes, we call the refreshUI() method to reload the tableView and that’s it.

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

IQListKit is a model-oriented framework, so we will be working with Cell and Models instead of IndexPath. IQListKit provides a few delegates to modify a cell or perform additional configuration based on the template before the cell is displayed. To do this, you can implement the IQList delegation method, as shown below

UserViewTableController Extension : IQListViewDelegateDataSource {

func listView(_ listView : IQListView, modifyCell cell : IQListCell, on indexPath : IndexPath) {
if let cell = cell if ? UserCell { //Casting our cell as UserCell
cell.delegate = self
//Or extra work with UserCell

// Get the user object associated with cell
let user = cell.model

// It is not recommended to use the indexPath variable to get a
template object // Don’t do this because we are a template-driven list and not an indexPath.
// let user = users [indexPath.row] }
}
}

– tableView(_ tableView : FACE, didSelectRowAt indexPath : IndexPath)

Don’t worry about it. We provide you with a custom template that is directly linked to the cell. That’s interesting!

UserViewTableController Extension : IQListViewDelegateDataSource {

func listView(_ listView : IQListView, didSelect item : IQItem, at indexPath : IndexPath) {
if let model = item.model as ? UserCell.Model { // We get a custom model associated with cell
if we have controller = UIStoryboard (name: Main, bundle: nil)
.instantiateViewController (ID: UserDetailViewController) how ? UserDetailViewController {
controller.user = template // when using method 1 or method 2
// controller.user = template.user // when using method 3
self.navigationController?.pushViewController(controller, animated: true)
}
}
}
}

– tableView(_ tableView : UITableView, estimatedHeightForRowAt indexPath : IndexPath) -> CGFloat

– tableView (_ tableView : EXPERIENCE, heightForRowAt indexPath : IndexPath) -> CGFloat

Since this method mainly returns values based on the cell and its model, we applied these configurations to the cell. This is part of the IQCellSizeProvider protocol and we can override the default behavior.

UserCell Class : ITUableViewCell, IQModelableCell.

//…

static estimated size (for model: AnyHashable?, listView: IQListView) -> CGSize {
return CGSize(width: listView.frame.width, height: 100)
}

static func format (for model: AnyHashable?, listView: IQListView) -> CGSize {

if you leave the model how? Model {Variation height : CGFloat = 100
//.
// return height based on
return CGSize (width: listView.frame.width, height: height)
}

//Or return constant height
return CGSize (width: listView.frame.width, height: 100)

//Or UITableView.automaticDimension for dynamic behavior
// return CGSize (width: listView.frame.width, height: UITableView.automaticDimension)
}
}

– tableView(_ tableView : OUTDOORSwipeActionsConfigurationForRowAt indexPath : IndexPath) -> UISwipeActionsConfiguration ?

– tableView(_ tableView : OUTLOOK, followSwipeActionsConfigurationForRowAt indexPath : IndexPath) -> UISwipeActionsConfiguration ?

– tableView(_ tableView : UITableView, editActionsForRowAt indexPath : IndexPath) -> [UITableViewRowAction] ?

Now, this method also essentially returns values based on the cell and its model, we have transferred these configurations to the cell. This is part of the IQCellActionsProvider protocol, and we can override the default behavior.

UserCell Class : ITUableViewCell, IQModelableCell.

//…

@available(iOS 11.0, *)
func leaderSwipeActions() -> [IQContextualAction] ? {
let action = IQContextualAction(style : .normal, title : Hello Leading) {(action, completionHandler) in
completionHandler(true)
//Do your stuffs here
}
action.backgroundColor = UIColor.orange

back [action] }.

func trailingSwipeActions() -> [IQContextualAction] ? {

let action1 = IQContextualAction(style : .normal, title : Hello Trailing) { [weak self](action, completedHandler) in
completionHandler(true)
guard let self = self, let user = self.model else {
return
}

// Do your own business here
}

action.backgroundColor = UIColor.purple

back. [action]}

This method also returns values based on the cell and its model, we have applied these configurations to the cell. This is also part of the IQCellActionsProvider protocol and we can override the default behavior.

UserCell Class : ITUableViewCell, IQModelableCell.

//…

@available(iOS 13.0, *)
func contextMenuConfiguration() -> UIContextMenuConfiguration? {

let contextMenuConfiguration = UIContextMenuConfiguration(id: null,
previewProvider : {( -> UIViewController? in
let controller = UIStoryboard (name: Main, bundle: nil)
.instantiateViewController(id: UserViewController) if ? UserViewController
controller ?.user = return controller self.model

}, actionProvider: {(actions) -> UIMenu ? in

var actions = [UIMenuElement]()
let action = UIAction(title: Hello Action) { _ in
// Put your stuff here
}
action.append(action)

return UIMenu (name: nested menu, children: actions)
})

Return to contextMenuConfiguration
}

@available(iOS 13.0, *)
func performPreviewAction(configuration: UIContextMenuConfiguration, animator
: UIContextMenuInteractionCommitAnimating) {
if let previewViewController = animator.previewViewController, let parent = viewParentController {
animator.addAnimations {
(parent.navigationController ? ? parent).show(previewViewController, sender: self)
}
}
}
}

UIView private extension {
varviewParentController : UIView controller? {var parentResponder.} {var parentResponder.} UIResponder? = self
while let next = parentResponder ?.next {
if let viewController = next as ? UIViewController {
returns viewController
} else { parentResponder = next }
}
returns null
}
}

UserViewTableController Extension : IQListViewDelegateDataSource {

//…

//cell will be displayed soon
listView function (_ listView : IQListView, willDisplay cell : IQListCell, on indexPath : IndexPath)

//Cell finished displaying function
listView(_ listView: IQListView, didEndCell, on indexPath: IndexPath)
}

UserViewTableController Extension : IQListViewDelegateDataSource {

//…

//Returns the element size, only for tableView size.height
function listView(_ listView: IQListView, size item: IQItem, on indexPath: IndexPath) -> CGSize?

//Returns the HeaderView of section
func listView(_ listView: IQListView, headerFor section: IQSection, at sectionIndex: Int) -> UIView?

//Return the footerView of section
func listView(_ listView: IQListView, footerFor section: IQSection, at sectionIndex: Int) -> UIView?
}

UserCell Class : ITUableViewCell, IQModelableCell.

//…

var is high: Bool { //IQSelectableCell log
returns the process value
}.

var isSelectable : Bool { //IQSelectableCell protocol
returns false
}
}

UserCell Class : ITUableViewCell, IQModelableCell.

//…

//IQCellActionsProvider protocol
func contextMenuPreviewView(config: UIContextMenuConfiguration) -> UIView? {
returns viewToBePreview
}
}

IQListKit! Why aren’t you loading my created cell into the storyboard?

Well, uh… When we create a cell in a storyboard, to work with IQListKit, we need to specify the cell identifier and class name. If we use UICollectionView, we must also manually register our cell using the method list.registerCell(type: UserCell.self, registerType: .storyboard), because with UICollectionView we cannot see if a cell has been created in the storyboard.

I have a large dataset and the list.performUpdates method takes time to animate the changes. What can I do?

One would not think that the performUpdtes method is a content-based method for storing a thread. It can be called in the background and show a charge indicator. You can hide the charge indicator in the Outage Manager. Under the hood, the calculations of the changes are performed in the background. Thanks again to Apple for NSDiffableDataSourceSnapshot and to Ryo Aoyama for DiffableDataSources. OUTLOOK/UICOLOGY AREA is reloaded in the main thread. Please note the following code:-

UserTableSeeController class : EXECUTIVEViewController.

//…

func refreshUI(animated:Bool = true) {

//Display load indicator
loadIndicator.startAnimating()

// background updates
DispatchQueue.global().async {

self.list.performUpdates({}

let section = IQSection (Identifier: first)
self.list.append(section)

self.list.append (UserCell.self, templates: users, partition: partition)

}, animationDifference: animation, completion: {
//hide load indicator if completion is called in the main thread
self.loadIndicator.stopAnimating()
})
}
}
}

GitHub

 rxdatasources collectionviewrxdatasources multiple sectionsrxtableviewsectionedanimateddatasourcerxdatasources mvvmrxkeyboardrxcollectionviewsectionedreloaddatasourcecollectionview tableview swiftuicollectionview in uitableviewcell xibcollectionview in tableview githubcollection view in table view headeruitableview inside uicollectionview swift 4multiple collection view in tableview swiftuitableviewdelegateuitableviewdiffabledatasourceuicontextualactionuitableviewcontrolleruicollectionviewdiffabledatasourceswift multiple prototype celluitableview best practice iosalternatives to uitableviewuitableview internal workingmultiple custom cells in uitableview swift 4uitableview with multiple sections swift 4ios 13 tableviewdiffabledatasource combinecompositional layoutuicollectionview example objective-csimple collection view in swiftuicollectionview vs uitableviewcollectionviewcontroller in swiftadd view to uicollectionview swiftuicollectionview swift 4 programmaticallyuicollectionview inside uitableviewcell dynamic heightvertical uicollectionview inside uitableviewcelluicollectionview in uitableviewcell swift githubuicollectionview inside a uitableviewcell dynamic height (swift)uicollectionview height based on content swiftcollection view inside table view programmaticallyuicollectionview in uitableviewcell programmatically

You May Also Like

🥇 Create Gmail Mail without Phone Number  Step by Step Guide ▷ 2021

Gmail has become one of the most widely used email platforms in…

How Do You Redim a Multidimensional Array?

Arrays are simply sets of values or elements stored in continuous memory.…

What is JVM in Java | JVM Architecture, JIT Compiler

Java Virtual Machine (JVM) in Java is the heart of the entire…

🥇 Schedule Gmail Emails  Step by Step Guide ▷ 2021

Email scheduling is a very useful feature that has been available on…