Compose is a library that helps you compose complex and dynamic views.

Related tags

Layout Compose
Overview

Pod Platform Language Build Status Pod License

Version Carthage Compatible

Compose is a data driven library that will help compose your complex and dynamic Views.

It helps you create complex and dynamic Views using easy and simple composition of data structures. It is really easy to use and extend.

And it also is a breeze to implement on existing projects.

First you will create units that encapsulates the data they will display

let info = "Info to be displayed"
let infoUnit = LabelUnit(text: info, traits: [.height(40)])

and then, you can add this unit to a container:

container.state = [infoUnit]

you can also conditionaly add this unit to the container, like:

var units: [ComposingUnit] = [....]
units.add(if: someCondition) {
	return LabelUnit(text: info, traits: [.height(40)])
}
container.state = units

So, instead of dealing with many dataSource/delegate methods you can just create an array of ComposingUnits and assign it to a state property of a container.

So, what are ComposingUnits?

The protocol ComposingUnit is the heart of this framework.

You can make any class or structure conform to it, taking the advantages of Value Type and Reference Type when they best suits your needs. Also, this class/structure should hold all the data that it will display. With this approach, we don't need to hold a reference to the models that generated these units.

Let's say we want to display a list of all the feed items we have.

let feedItems: [FeedItem] = ... //You can grab an array from CoreData, JSON, Realm, anywhere...
var feedUnits: [ComposingUnit] = feedItems.map { FeedUnit(id: $0.uniqueId, title: $0.title, image: $0.image, likeCount:Int) }
container.state = feedUnits

So after we create the feedUnits array we don't need feedItems anymore and we can easily use our feedUnits in any thread. And we can add any other ComposingUnit to this array, allowing us to display a view totally different to a feed item in the same list

feedUnits.add(if: feedUnits.count > 4) {
	return SeeMoreFeedsUnit(feedsCount: feedUnits.count)
}

Handling selection and other delegates callbacks

To handle cell selection or other delegate callbacks, all your class/structure has to do is implement an applicable Protocol. For cell selection it is the SelectableUnit protocol. This protocol defines a method that will be called once the cell has been selected.

You can check all extension protocols here

Grouping units to represent a single unit

You can also use a CollectionStackUnit to group some units together as a single unit

var units: [ComposingUnit] = [...] //create somewhere
units.add(ifLet: data.optionalFeedItem) { feedItem in
	var innerUnits: [ComposingUnit] = [HeaderUnit(text: feedItem.title)]
	innerUnits.add(ifLet: feedItem.image) { image in
		return FeedImageUnit(image: image)
	}
	innerUnits.add(ifLet: feedItem.video) { video in
		return FeedVideoUnit(video: video)
	}
	innerUnits.append(ActionBarUnit(likes: feedItem.likes, comments: feedItem.comments))
	let unit = CollectionStackUnit(identifier: feedItem.uniqueIdentifier, direction: .vertical, traits: [], units: innerUnits)
	return unit
}

Maybe you could also create a function that returns this item based on a FeedItem

	func FeedUnit(from: FeedItem)-> CollectionStackUnit {
		var innerUnits: [ComposingUnit] = [HeaderUnit(text: feedItem.title)]
		innerUnits.add(ifLet: feedItem.image) { image in
			return FeedImageUnit(image: image)
		}
		innerUnits.add(ifLet: feedItem.video) { video in
			return FeedVideoUnit(video: video)
		}
		innerUnits.append(ActionBarUnit(likes: feedItem.likes, comments: feedItem.comments))
		let unit = CollectionStackUnit(identifier: feedItem.uniqueIdentifier, direction: .vertical, traits: [], units: innerUnits)
		return unit
	}
	
var units: [ComposingUnit] = [...] //create somewhere
units.add(ifLet: data.optionalFeedItem) { feedItem in
	return FeedUnit(from: feedItem)
}

Dynamic cell size calculation

We use a struct called DimensionUnit to represent a cell width/height calculation. A DimensionUnit can calculate a dimension using:

  • Static values: It will ignore it's container size and always return this static value
  • Percent based values: It will return a percentual of it's container dimension
  • Total value based: It will return it's container dimension minus a static value
  • Custom based: It will execute a closure passing it's container size as parameter.

Using DimensionUnit we can easily express our units height and width.

ComposingContainer

In order to display an array of ComposingUnits you will need an UIView that conforms to ComposingContainer.

We provide two default containers in the framework: ComposingCollectionView and ComposingTableView. Both have automatic detection of inserts/updates/deletes in their state they are displaying.

Tests

It gets really simple to test your interface, as you can test the presence of some specific unit, and you don't need to render your interface.

let dummyItem = FeedItem(...)
var feedUnit = FeedUnit(from: dummyItem)
XCTAssert(feedUnit.units.count == 3)
dummyItem.image = nil
dummyItem.video = nil
feedUnit = FeedUnit(from: dummyItem)
XCTAssert(feedUnit.units.count == 2)

Examples

We provide some cool examples in our Example project.

To run, clone this repo, and open the Example/Compose_Example.xcodeproj. You don't need to do any pod install or any configuration to run this project

Installation

Cocoapods

Compose is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "Compose"

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate Compose into your Xcode project using Carthage, specify it in your Cartfile:

github "VivaReal/Compose" ~> 1.0

Run carthage update to build the framework and drag the built Compose.framework into your Xcode project.

Manual

You can download this repo, drag the Compose.xcodeproj inside your project and link the Compose framework

Documentation

You can find all documentation about Compose here: Documentation

Author

Bruno Bilescky, [email protected]

License

Compose is available under the MIT license. See the LICENSE file for more info.

You might also like...
An experiment creating a particle emitter using the new TimelineView and Canvas views in SwiftUI
An experiment creating a particle emitter using the new TimelineView and Canvas views in SwiftUI

Particle Emitter An experiment creating a particle emitter using the new Timelin

Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast
Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast

Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS/CALayer]

Simple static table views for iOS in Swift.
Simple static table views for iOS in Swift.

Simple static table views for iOS in Swift. Static's goal is to separate model data from presentation. Rows and Sections are your “view models” for yo

The fast path to autolayout views in code
The fast path to autolayout views in code

NorthLayout The fast path to autolayout views in code Talks https://speakerdeck.com/banjun/auto-layout-with-an-extended-visual-format-language at AltC

Apple provides us two ways to use UIKit views in SwiftUI

RepresentableKit Apple provides us two ways to use UIKit views in SwiftUI: UIVie

Instagram clone, the main focus of the app is the seamless swipe between views that we see on instagram
Instagram clone, the main focus of the app is the seamless swipe between views that we see on instagram

InstaSwipe Instagram clone, the main focus of the app is the seamless swipe betw

StoryboardUsingCustomViews - Storyboard Using Custom Views
StoryboardUsingCustomViews - Storyboard Using Custom Views

Storyboard Using Custom Views Vista creada con: Storyboard + Constraints + Progr

ResponderChainDemo - Learned how to use responder chain for communication between the views
ResponderChainDemo - Learned how to use responder chain for communication between the views

ResponderChainDemo Learned how to use responder chain for communication between

SwiftUI views that arrange their children in a flow layout.
SwiftUI views that arrange their children in a flow layout.

SwiftUI Flow SwiftUI views that arrange their children in a flow layout. HFlow A view that arranges its children in a horizontal flow. Usage ScrollVie

Swift-picker-views - inline single and multi picker views for UIKit. Without tableview! Easy and simple

swift-picker-views Inline single and multiple picker views for UIKit. No tablevi

IBRAHIM YILMAZ 2 Jan 31, 2022
A Swift utility to make updating table views/collection views trivially easy and reliable.

ArrayDiff An efficient Swift utility to compute the difference between two arrays. Get the removedIndexes and insertedIndexes and pass them directly a

Adlai Holler 100 Jun 5, 2022
iOS App that helps you breath properly.

Breathing App iOS App that helps you breath properly. I created this iOS app in my Intro to iOS Develepmont class at Hunter College. I am not a profes

Kevin Salamanca 0 Dec 24, 2021
Very simple swipe-to-dismiss, supporting Auto Layout and dynamic heights

PanelPresenter Add swipe-dismiss logic to your view controller, supporting Auto Layout and dynamic heights. Installation Add this package to your proj

Pim 3 Aug 23, 2022
✂ Easy to use and flexible library for manually laying out views and layers for iOS and tvOS. Supports AsyncDisplayKit.

ManualLayout Table of Contents Installation Usage API Cheat Sheet Installation Carthage Add the following line to your Cartfile. github "isair/ManualL

Baris Sencan 280 Sep 29, 2022
Simple Catalyst example (Mac idiom) of a grid-based app populated with photos, with dynamic cell layout switching

Catalyst Photo Grid Simple Catalyst example (Mac idiom) of a grid-based app populated with photos that can animate its cells between two different lay

Steven Troughton-Smith 56 Nov 14, 2022
🇰🇷 An app that helps non Korean speakers to learn Hangul easily and effectively.

?? HangulKing HangulKing is the fastest way to the throne of Hangul, the Korean alphabets! HangulKing helps the users learn Hangul easily, providing m

개발자아카데미_포스텍 7 Nov 26, 2022
Powerful autolayout framework, that can manage UIView(NSView), CALayer and not rendered views. Not Apple Autolayout wrapper. Provides placeholders. Linux support.

CGLayout Powerful autolayout framework, that can manage UIView(NSView), CALayer and not rendered views. Has cross-hierarchy coordinate space. Implemen

Koryttsev Denis 45 Jun 28, 2022
Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS/CALayer]

Extremely Fast views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainabl

layoutBox 2.1k Dec 22, 2022
Expose layout margins and readable content width to SwiftUI's Views

SwiftUI Layout Guides This micro-library exposes UIKit's layout margins and readable content guides to SwiftUI. Usage Make a view fit the readable con

Thomas Grapperon 26 Dec 23, 2022