A UICollectionView can have almost any arrangement of elements. Elements can have both fixed sizes and dynamic ones. In this publication, attention will be paid only to those UICollectionViewLayout, whose element sizes are fixed and set by a specific algorithm (a
typical example is the location of icons on your iPhone’s Home screen ). An attempt will be made to describe the approach to the formation of a single UICollectionViewLayout.
The main feature of such UICollectionViewLayout is that in each such arrangement, we can distinguish a certain pattern by which the dimensions and positions of the elements are repeated. In the following we will call it a
template . Accordingly, to make a UICollectionViewLayout, you need to create an algorithm that generates a template. Repeat pattern example:
All similar UICollectionViewLayout will differ from each other only by the algorithm of forming the sizes and positions of the collection elements. Hence, there is a desire to make such a UICollectionViewLayout, which will require only an algorithm for generating a template, and it will take all further actions.
')
If you look closely at the previous example, you can see that inside the template there are such
blocks of elements that are repeated several times during the template. Taking into account the possibility of repeating blocks within a template, a logical step would be to split the template into an array of blocks.
Putting all the ideas together and describing all the protocols involved in the formation of a template of a thing, we got something like the following:
Protocols (swift code)public protocol SquareMosaicBlock { // CGRect func frames() -> Int // [CGRect] func frames(origin: CGFloat, width: CGFloat) -> [CGRect] } public protocol SquareMosaicPattern { // var blocks: [SquareMosaicBlock] { get } } public protocol SquareMosaicLayoutDataSource: class { func pattern() -> SquareMosaicPattern }
- Are there enough SquareMosaicPattern, SquareMosaicBlock protocols to describe any arrangement of elements?
- The location of the elements of the collection in the image above is based only on these two protocols. Therefore, most likely, any location can be described only by these protocols "
To give an example of the implementation of the UICollectionViewLayout itself, which uses these protocols, and to make its detailed review is not included in the plans for this publication. Anyone can independently analyze the approach and implementation by going to the
repository (
all the logic is described only on ~ 100 lines of code ). Also there you can find an example of use.
However, to give an example of the formation of objects corresponding to the protocols of the template and block should certainly be. For clarity, the locations of the elements for the collection, which is shown in the pictures, will be described.
Objects corresponding to protocols (swift code) struct SnakeSquareMosaicPattern: SquareMosaicPattern { var blocks: [SquareMosaicBlock] { return [ OneTwoSquareMosaicBlock(), ThreeRightSquareMosaicBlock(), TwoOneSquareMosaicBlock(), ThreeRightSquareMosaicBlock() ] } } public struct OneTwoSquareMosaicBlock: SquareMosaicBlock { public func frames() -> Int { return 3 } public func frames(origin: CGFloat, width: CGFloat) -> [CGRect] { let sideMin = width / 3.0 let sideMax = width - sideMin var frames = [CGRect]() frames.append(CGRect(x: 0, y: origin, width: sideMax, height: sideMax)) frames.append(CGRect(x: sideMax, y: origin, width: sideMin, height: sideMin)) frames.append(CGRect(x: sideMax, y: origin + sideMax - sideMin, width: sideMin, height: sideMin)) return frames } } public struct TwoOneSquareMosaicBlock: SquareMosaicBlock { public func frames() -> Int { return 3 } public func frames(origin: CGFloat, width: CGFloat) -> [CGRect] { let sideMin = width / 3.0 let sideMax = width - sideMin var frames = [CGRect]() frames.append(CGRect(x: 0, y: origin, width: sideMin, height: sideMin)) frames.append(CGRect(x: 0, y: origin + sideMax - sideMin, width: sideMin, height: sideMin)) frames.append(CGRect(x: sideMin, y: origin, width: sideMax, height: sideMax)) return frames } } public struct ThreeRightSquareMosaicBlock: SquareMosaicBlock { public func frames() -> Int { return 3 } public func frames(origin: CGFloat, width: CGFloat) -> [CGRect] { let side = width / 3.0 var frames = [CGRect]() frames.append(CGRect(x: side + side, y: origin, width: side, height: side)) frames.append(CGRect(x: side, y: origin, width: side, height: side)) frames.append(CGRect(x: 0, y: origin, width: side, height: side)) return frames } }
As a result, we have a completely universal class
SquareMosaicLayout . Based on it, you can create different sets of UICollectionView element locations. Animated transition from one template to another. Reuse blocks of different templates to create new ones.
The next stage of development will be the creation and replenishment of pre-installed sets of blocks and templates with a variety of arrangements of elements. I would also like to add support for various types of animations and SupplementaryViews / DecorationViews
Conclusion: The most important thing that has been achieved is that the insides of the SquareMosaicLayout class do not need to be changed to compose a new arrangement of elements.
PS Available for installation via cocoapods:
pod 'SquareMosaicLayout'