Layout Component

Why do we need layout components?

  • As we mentioned previously, render components work seamlessly with non-render components.
  • In order to make sure that it works correct, each component has to implement buildComponentTree: correctly and continue the recursion to its children (more details can be found here).
  • We implemented the buildComponentTree: method for all the base components, you don't need (and actually can't) implement this method.
  • The base implementation in CKComponent is for a leaf component.
  • When a component subclass from CKComponent, but has a custom layout with children - it can cause a problem; the infrastructure doesn't know about its children and cannot build the component tree for them.

How do we solve this?

  • We introduced:
    • CKLayoutComponent
  • We converted all the layout components in the infra to CKLayoutComponent. For example:
    • CKInsetComponent : CKLayoutComponent
    • CKFlexboxComponent : CKLayoutComponent
  • We codemoded all the other custom layout components in the code base.
  • We introduced a runtime assert in case you have a render component in the hierarchy and a custom layout component that inherits from CKComponent

When should I use render layout components?

  • In general, we recommend to avoid custom layout component as much as possible. If you can, just use CKFlexboxComponent with absolute layout to describe your layout.
  • If your component must have a custom layout, you can probably use CKLayoutComponent and provide your custom layout.

How to use CKLayoutComponent

  • All you need to do, is to override the CKIterable method sand provide the component's children there.
  • Before the conversion:
@interface GapComponent : CKComponent
@end
@implementation GapComponent
{
CKComponent *_child;
}
+ (instancetype)newWithComponent:(CKComponent *)component
{
auto const c = [super new];
if (c) {
_child = component;
}
return c;
}
- (CKLayout)computeLayoutThatFits:(CKSizeRange)constrainedSize
{
...
}
@end
  • After the conversion:
@interface GapComponent : CKRenderLayoutComponent
@end
@implementation GapComponent
{
CKComponent *_child;
}
+ (instancetype)newWithComponent:(CKComponent *)component
{
auto const c = [super new];
if (c) {
_child = component;
}
return c;
}
- (unsigned int)numberOfChildren
{
return CKIterable::numberOfChildren(_child);
}
- (id<CKMountable>)childAtIndex:(unsigned int)index
{
return CKIterable::childAtIndex(self, index, _child);
}
- (CKLayout)computeLayoutThatFits:(CKSizeRange)constrainedSize
{
...
}
@end