{"id":1593,"url":"https://github.com/ekazaev/ChatLayout","last_synced_at":"2025-08-02T04:32:02.702Z","repository":{"id":38305902,"uuid":"277604520","full_name":"ekazaev/ChatLayout","owner":"ekazaev","description":"ChatLayout is an alternative solution to MessageKit. It uses custom UICollectionViewLayout to provide you full control over the presentation as well as all the tools available in UICollectionView. It supports dynamic cells and supplementary view sizes.","archived":false,"fork":false,"pushed_at":"2025-07-30T15:50:40.000Z","size":18846,"stargazers_count":963,"open_issues_count":1,"forks_count":76,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-07-30T18:34:28.026Z","etag":null,"topics":["chat","chat-application","chatui","collectionview","collectionviewlayout","custom-uicollectionviewlayout","differencekit","messagekit","messaging","messenger","swift","uicollectionview","uicollectionviewlayout"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ekazaev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"ekazaev"}},"created_at":"2020-07-06T17:19:54.000Z","updated_at":"2025-07-30T15:49:54.000Z","dependencies_parsed_at":"2024-01-07T22:23:35.021Z","dependency_job_id":"5674970f-2223-4c5b-be93-ee7b321a0e9d","html_url":"https://github.com/ekazaev/ChatLayout","commit_stats":{"total_commits":233,"total_committers":4,"mean_commits":58.25,"dds":"0.15450643776824036","last_synced_commit":"ed01d4c9ab213332929b01efbb6df9de949b676c"},"previous_names":[],"tags_count":100,"template":false,"template_full_name":null,"purl":"pkg:github/ekazaev/ChatLayout","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ekazaev%2FChatLayout","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ekazaev%2FChatLayout/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ekazaev%2FChatLayout/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ekazaev%2FChatLayout/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ekazaev","download_url":"https://codeload.github.com/ekazaev/ChatLayout/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ekazaev%2FChatLayout/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268334615,"owners_count":24233793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chat","chat-application","chatui","collectionview","collectionviewlayout","custom-uicollectionviewlayout","differencekit","messagekit","messaging","messenger","swift","uicollectionview","uicollectionviewlayout"],"created_at":"2024-01-05T20:15:50.717Z","updated_at":"2025-08-02T04:32:02.680Z","avatar_url":"https://github.com/ekazaev.png","language":"Swift","funding_links":["https://github.com/sponsors/ekazaev"],"categories":["Messaging","Swift"],"sub_categories":["Video"],"readme":"# ChatLayout\n\n[![Release](https://img.shields.io/github/release/ekazaev/ChatLayout.svg?style=flat\u0026color=darkcyan)](https://github.com/ekazaev/ChatLayout/releases)\n[![Version](https://img.shields.io/cocoapods/v/ChatLayout.svg?style=flat)](https://cocoapods.org/pods/ChatLayout)\n[![Documentation](https://ekazaev.github.io/ChatLayout/badge.svg)](https://ekazaev.github.io/ChatLayout/)\n[![Codecov](https://codecov.io/gh/ekazaev/ChatLayout/branch/master/graph/badge.svg)](https://codecov.io/gh/ekazaev/ChatLayout)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/b97c279a50984376ab2649f5a7d09e69)](https://www.codacy.com/gh/ekazaev/ChatLayout/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=ekazaev/ChatLayout\u0026amp;utm_campaign=Badge_Grade)\n[![Swift Package Manager](https://img.shields.io/badge/SwiftPM-compatible-brightgreen.svg?style=flat)](https://github.com/apple/swift-package-manager)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BA51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Swift 6.1](https://img.shields.io/badge/language-Swift6.1-orange.svg?style=flat)](https://developer.apple.com/swift)\n[![Platform iOS](https://img.shields.io/badge/platform-iOS%2013%20—%20iOS%2018-yellow.svg)](https://www.apple.com/ios)\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://habrastorage.org/webt/ji/ba/dj/jibadjc0hul-fzfwxm2w0ywdutg.png\"  alt=\"ChatLayout logo\"/\u003e\n\u003c/p\u003e\n\n## Table of contents\n\n- [About](#about)\n- [Features](#features)\n    - [What ChatLayout doesn't provide (And why it is good)](#what-chatlayout-doesnt-provide-and-why-it-is-good)\n- [Requirements](#requirements)\n- [Documentation](#documentation)\n- [Example](#example)\n- [Installation](#installation)\n- [Contributing](#contributing)\n- [Todo](#todo)\n    - [About `UICollectionViewDiffableDataSource`](#about-uicollectionviewdiffabledatasource)\n    - [About Supplementary Views](#about-supplementary-views)\n    - [About Texture](#about-texture)\n    - [About animation](#about-animation)\n- [License](#license)\n- [Articles](#articles)\n- [Sponsor this project](#sponsor-this-project)\n- [Author](#author)\n\n## About\n\n`ChatLayout` is an alternative solution to [MessageKit](https://github.com/MessageKit/MessageKit). It uses custom \n`UICollectionViewLayout` to provide you full control over the presentation as well as all the tools available in \n`UICollectionView`.\n\n## Features\n\n- Supports dynamic cells and supplementary view sizes.\n- Animated insertion/deletion/reloading/moving of the items.\n- Keeps content of the last visible item at the top or bottom of the `UICollectionView` during updates.\n- Provides tools for precise scrolling to the required item.\n- Shipped with generic container views to simplify the custom items implementation.\n- Pinned (sticky) headers/footers or cells.\n\n![](https://habrastorage.org/webt/jt/gq/sl/jtgqsluujffi4-jnxeikbwtyyu0.gif)\n![](https://habrastorage.org/webt/b7/cu/3s/b7cu3su6uk4hw1kqg3_ky3uklu4.gif)\n![](https://habrastorage.org/webt/sv/ul/cq/svulcqg5ompgyhp-pjxy1tyiie4.gif)\n![](https://habrastorage.org/webt/bq/kw/xg/bqkwxgggxnxlqyzau36utlwcyui.gif)\n![](https://habrastorage.org/webt/hn/ez/gq/hnezgqezp8vxg8vy8z7_ozetra0.gif)\n![](https://habrastorage.org/webt/gn/ny/qe/gnnyqepf46r4zdhyb4oug8vywvc.gif)\n![](https://habrastorage.org/webt/t9/b7/4r/t9b74rdyrkf8lszjuhj_vrbp7-s.gif)\n![](https://habrastorage.org/webt/nv/vr/js/nvvrjsqk0fzutq0y-uubjewyqjm.gif)\n\n### What ChatLayout doesn't provide (And why it is good)\n\n`ChatLayout` is the custom `UICollectionViewLayout`, so:\n\n- You don't have to extend or override any custom `UIViewController` or `UICollectionView`. You need to instantiate them \nyourself and use them the way you like. \n\n- `ChatLayout` does not rely on modified `UICollectionViewFlowLayout` nor does it rotate your `UICollectionView` upside-down. \nThis means you can use your views as if they would be regular cells within `UICollectionView`. You can benefit from using the \ndefault `UIKit` implementations of `adjustedContextInsets` (and others) because your view controller is a normal view \ncontroller without any hacks or tricks.\n\n- `ChatLayout` doesn't require you to calculate all the cell sizes before it renders them on the screen. You can fully use\nauto-layout constraints and rely on the fact that the correct size will be calculated in the runtime. However, `ChatLayout` \nas any other `UICollectionViewLayout` will benefit from you providing the estimated sizes of your cells as it will allow you \nto get better performance. \n\n- `ChatLayout` doesn't enforce you to use any specific data model. You can store your messages and update `UICollectionView`\nthe way you like. The only thing you need is to respect the natural boundaries that `UICollectionView` have and correctly\nimplement `UICollectionViewDataSource`. The Example app uses [DifferenceKit](https://github.com/ra1028/DifferenceKit) to \nprocess changes in the data model.\n\n- `ChatLayout` doesn't enforce you to use any specific `UIView`s to create your collection cells. You can create them the way \nyou like. It can be any `UICollectionViewCell` or `UICollectionReusableView`. There are some generic `UIView`s bundled with\nthe library that may help you to build them faster. However, you do not have to use them. \n\n- `ChatLayout` doesn't handle the keyboard appearance behavior. You have to implement\nthat yourself from scratch or use the library you are already using in your project. It gives you full control over the \nkeyboard presentation. The only thing you have to do is to update the `contentInsets` of your `UICollectionView`.\n\n- `ChatLayout` doesn't provide you any input control. You can use any one you like and customise it the way you like. \nThe Example app for instance uses [InputBarAccessoryView](https://github.com/nathantannar4/InputBarAccessoryView).\n\n## Documentation\n\nRefer to the documentation at the following [link](https://ekazaev.github.io/ChatLayout/) for detailed information and usage guidelines.\n\n## Example\n\nTo run the example project, clone the repo, and run `pod install` from the Example directory first.\n\n## Installation\n\n`ChatLayout` is available through [CocoaPods](https://cocoapods.org), [Carthage](https://github.com/Carthage/Carthage) \nand [SwiftPM](https://github.com/apple/swift-package-manager). See the `Example` app for the usage details.\n\nIf you are using cocoapods you can install the whole package using `pod 'ChatLayout'`. If you do not need the additional\ncomponents provided, you can install only the layout itself using `pod 'ChatLayout/Core'`\n\n## Contributing\n\n`ChatLayout` is in active development, and we welcome your contributions.\n\nIf you’d like to contribute to this repo, please\nread [the contribution guidelines](https://github.com/ekazaev/route-composer/blob/master/CONTRIBUTING.md).\n\n## Todo\n\n- [ ] Improve the test coverage\n\n### About `UICollectionViewDiffableDataSource`\n\n`ChatLayout` can process any update commands that you send to your `UICollectionView`, so you can use \n`UICollectionViewDiffableDataSource` as well. \n\n### About Supplementary Views\n\nIt can be tempting and it may look like it is the right way to go, but **do not** use supplementary views to decorate your\nmessages or groups of them. `UICollectionView` processes them in a different order: `UICollectionViewCell`s first and \nonly after switches to `UICollectionReusableView`s. You will most likely face some unexpected behaviour during the animation.\n**I strongly advice you against using sections at all.**\n\n### About Texture\n\n`ChatLayout` can be used together with [Texture](https://github.com/TextureGroup/Texture) to improve the auto-layout performance. \nBut keep in mind that it's default wrapper is hardcoded to work exclusively with `UICollectionViewFlowLayout`. \n[See issue](https://github.com/TextureGroup/Texture/issues/1959).\nYou will have to implement `ChatLayoutDelegate` yourself and propagate the node size manually.\n\n### About animation\n\nIf you see a strange or unexpected animation during the updates, check your data model and **the commands you send to the\n`UICollectionView`'s `performBatchUpdates`**. Especialy if you are using some diffing algorithms like [DifferenceKit](https://github.com/ra1028/DifferenceKit).\nIt is very possible that you are sending delete/insert commands when you expect to see reload. The easiest way to check it is by adding\n`print(\"\\(updateItems)\")` into `ChatLayout.prepare(forCollectionViewUpdates:)` method. `ChatLayout` doesn't know what you expected to see. \nIt just processes your changes according to the commands it has received.\n\n## License\n\n`ChatLayout` is distributed under [the MIT license](https://github.com/ekazaev/ChatLayout/blob/master/LICENSE).\n\n`ChatLayout` is provided for your use, free-of-charge, on an as-is basis. We make no guarantees, promises or\napologies. *Caveat developer.*\n\n## Articles\n\nEnglish:\n- [My COVID-19 lockdown project or how I started to dig into a custom UICollectionViewLayout to get a ChatLayout](https://eugenenekhoroshiy.medium.com/my-covid-19-lockdown-project-or-how-i-started-to-dig-into-a-custom-uicollectionviewlayout-to-get-a-d053e1ad3aa0)\n\nRussian:\n  - [Мой Covid-19 lockdown проект, или, как я полез в кастомный UICollectionViewLayout и получил ChatLayout](https://habr.com/ru/post/523492/)\n\n## Sponsor this project\n\nIf you find this library useful, and especially if you are using it in production, please consider sponsoring this \nproject [here](https://github.com/sponsors/ekazaev). I work on `ChatLayout` in my spare time, and your sponsorship would \nhelp me to continue developing and contributing to the Open Source community. Your support will enable me to dedicate \nmore time and resources to this project, ensuring that it remains up-to-date and relevant for years to come. \n\nThank you for your consideration!\n\n## Author\n  \nEvgeny Kazaev, eugene.kazaev@gmail.com. Twitter [ekazaev](https://twitter.com/EKazaev)\n\n*I am happy to answer any questions you may have. Just create a [new issue](https://github.com/ekazaev/ChatLayout/issues/new).*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fekazaev%2FChatLayout","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fekazaev%2FChatLayout","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fekazaev%2FChatLayout/lists"}