ComponentKit learning framework

First, the characteristics

Three characteristics:

  1. Description: the longitudinal or transverse characteristics of stuffing through the stack, the die layout of a sub-element to tell us of how to arrange an element A in A.
  2. Functional: to ensure that the data flow is unidirectional, i.e. the data decision Component. Such as the equation "1 + X", if the "X = 2", the result is the corresponding 3 is fixed. If the data is determined, then the result is the same. When the data is changed, corresponding to be re-rendered Component (will try to re-render the underlying implementation less).
  3. Combinable: Some part of the written component, other parts can be reused.

Reviews: Benefits unidirectional data stream that what determines what data view, may ignore the status of many various interactions generated during development, just need to focus on the data layer layout equation written (Functional) it seems like you can do it once and for all. Because of this, ComponentKit destined to be a lot of trouble in writing animation, because the data is continuous change.

As for animation, FB reply:

Dynamic gesture-driven UIs are currently hard to implement in ComponentKit; consider using AsyncDisplayKit.

 

二, API

1⃣️CKComponent

Component is immutable, and it can be created in any thread, the main thread to avoid the emergence of competition.

Two main API:

/** Returns a new component. */

+ (instancetype)newWithView:(const CKComponentViewConfiguration &)view

                       size:(const CKComponentSize &)size;



/** Returns a layout for the component and its children. */

- (CKComponentLayout)layoutThatFits:(CKSizeRange)constrainedSize

                         parentSize:(CGSize)parentSize;

To create a Component, one for typesetting.

 

2⃣️Composite Components

Key: any self-definition Component not inherit CKComponent, but inherited Composite Components. Do not be a simple demand directly inherit and override the parent class method, but should be used to reach a rhetorical (decorative design mode).

 

Here are examples of bad code, and the Recommended Code:

// 不推荐的坏代码:

@implementation HighlightedCardComponent : CardComponent

- (UIColor *)backgroundColor

{

  // This breaks silently if the superclass method is renamed.

  return [UIColor yellowColor];

}

@end

// 推荐代码:

@implementation HighlightedCardComponent : CKCompositeComponent

+ (instancetype)newWithArticle:(CKArticle *)article

{

  return [super newWithComponent:

          [CardComponent

           newWithArticle:article

           backgroundColor:[UIColor yellowColor]]];

}

@end

3⃣️Views

Create an element of class methods

+ (instancetype)newWithView:(const CKComponentViewConfiguration &)view

                       size:(const CKComponentSize &)size;

The first parameter tells what the layer class with CK, CK second parameter tells how to configure this layer class.

For chestnut:

[CKComponent 

 newWithView:{

   [UIImageView class],

   {

     {@selector(setImage:), image},

     {@selector(setContentMode:), @(UIViewContentModeCenter)} // Wrapping into an NSNumber

   }

 }

 size:{image.size.width, image.size.height}];

Null value may also be set, For chestnut:

[CKComponent newWithView:{} size:{}];

// 更为直接

[CKComponent new];

4⃣️Layout && Layout Components

And the layoutSubViews UIView CK corresponds to the layoutThatFits:

This introduces several commonly used Layout Components

  • CKStackLayoutComponent horizontal or vertical pile subelements
  • CKInsetComponent retraction and retraction similar Big Apple
  • CKBackgroundLayoutComponent expansion elements at the bottom as a background
  • CKOverlayLayoutComponent extension element of the cover layer as a mask
  • Arranged centrally in the space CKCenterLayoutComponent
  • CKRatioLayoutComponent there is an element of proportional
  • CKStaticLayoutComponent sub-element may specify an offset

 

Third, the responder chain && Tap event && gesture support

1⃣️ responder chain

The responder strand FB Apple similar, but the two are separated.

FB is substantially responder chain:

Son component -> son componentController (if any) -> Father component -> father componentController (if any) -> (... recursive blabla) -> [bridge] by CKComponentActionSend -> (process: find attached that View, find the bottom of the leaf nodes ViewA by the View -> (upward traverse ViewA father ViewB -> (... recursive blabla)

Here UIResponder a point is not a subclass of component, it can not become the first responder ~

 

2⃣️ click event

Solving occurs on UIControl view click event is as simple as a SEL bind to CKComponentActionAttribute can, UIControlEvent when receiving external trigger:

@implementation SomeComponent



+ (instancetype)new

{

  return [self newWithView:{

    [UIButton class],

    {CKComponentActionAttribute(@selector(didTapButton))}

  }];

}



- (void)didTapButton

{

  // Aha! The button has been tapped.

}



@end

3⃣️ gesture

UIControl above to apply for general View will have a more direct binding properties, such as tap gesture SEL bound to CKComponentTapGestureAttribute, code is as follows:

@implementation SomeComponent



+ (instancetype)new

{

  return [self newWithView:{

    [UIView class],

    {CKComponentTapGestureAttribute(@selector(didTapView))}

  }];

}



- (void)didTapView

{

  // The view has been tapped.

}



@end

4⃣️Component Actions

In short, the element Action mechanism is through binding SEL no brain, down the chain response element of the response can be found in the SEL.

 

Fourth, iOS Support containers view ( the UITableView , UICollectionView )

1⃣️ Overview:

FB广告:ComponentKit really shines when used with a UICollectionView.

The reason why specially emphasized, because any one of the APP special What are inseparable from UITableView or UICollectionView. UITableView or UICollectionView long as it has the capability of independent development.

 

FB advocated advantages:

  1. Automatic reuse
  2. Smooth sliding experience -> CK itself to ensure that the non-UI related calculations all in the second thread
  3. This module is responsible for the data source CKComponentDataSource.

 

PS: The main function CKComponentDataSource module:

  1. Providing input data and operation instruction data source
  2. Source data generated after the change the layout of the background
  3. UITableView available or provide output data source UICollectionView

 

CKComponentCollectionViewDataSource

CKComponentCollectionViewDataSource CKComponentDataSource the simple package.

Existence value:

  1. Let UICollectionView responsible for timely add / insert / update "line," "section."
  2. Responsible for providing the "line" and "block" layout information to UICollectionView.
  3. UICollectionView visible rows speaking synchronous call cellForItemAtIndexPath:
  4. Guaranteed to return the configured cell

Here UICollectionView and CKCollectionViewDataSource performance data is still one-way.

 

2⃣️ basis

Component Provider

CKCollectionViewDataSource threw responsible for each data element (component) self-Config. That is, when a certain element (Component) data configuration is required, the classes will be provided by the data source CKCollectionViewDataSource method provided by passing CKComponentProvider:

@interface MyController <CKComponentProvider>

    ...

    @end



    @implementation MyController

    ...

    + (CKComponent *)componentForModel:(MyModel*)model context:(MyContext*)context {

        return [MyComponent newWithModel:model context:context];

    }

    ...
  • Not block a class method To ensure data is immutable
  • This context can be any immutable object, which is brought into CKCollectionViewDataSource. It is generally: 1) Device Type 2), such external image-dependent Downloader

3⃣️ create CKCollectionViewDataSource:

- (void)viewDidLoad {

    ...

    self.dataSource = _dataSource = [[CKCollectionViewDataSource alloc] initWithCollectionView:self.collectionView supplementaryViewDataSource:nil componentProvider:[self class] context:context cellConfigurationFunction:nil];

}

4⃣️ Add / Modify

Model will need to do is bind with indexPath:

- (void)viewDidAppear {

        ...

        CKArrayControllerSections sections;

        CKArrayControllerInputItems items;

        // Don't forget the insertion of section 0

        sections.insert(0);

        items.insert({0,0}, firstModel);

        // You can also use NSIndexPath

        NSIndexPath indexPath = [NSIndexPath indexPathForItem:1 inSection:0];

        items.insert(indexPath, secondModel);

        [self.dataSource enqueueChangeset:{sections, items} constrainedSize:{{0,0}, {50, 50}}];

    }

For example indexPath (0, 0), model is a string "I'm 0 0 line segment", told CKCollectionViewDataSource to bind them together (that is, binding 0 0 line segment and Component elements).

5⃣️ typesetting

Paste the code:

- (CGSize)collectionView:(UICollectionView *)collectionView

                 layout:(UICollectionViewLayout *)collectionViewLayout

                 sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

        return [self.dataSource sizeForItemAtIndexPath:indexPath];

    }

6⃣️ event processing

- (void)dataSource:(CKCollectionViewDataSource *)dataSource didSelectItemAtIndexPath:(NSIndexPath *)indexPath{

        MyModel *model = (MyModel *)[self.dataSource modelForItemAtIndexPath:indexPath];

        NSURL *navURL = model.url;

        if (navURL) {

            [[UIApplication sharedApplication] openURL:navURL];

        }
}

V. (data source) to change the set of API

Here mainly refers to the API to interact with the data source portion, divided into three categories:

  1. Action (for insert rows / delete / update for insertion segment / delete)
  2. Specify the location (row / segment position designation)
  3. Allocation data (used threw Component)

Paste the code:

CKArrayControllerInputItems items;

// Insert an item at index 0 in section 0 and compute the component for the model @"Hello"

items.insert({0, 0}, @"Hello");

// Update the item at index 1 in section 0 and update it with the component computed for the model @"World"

items.update({0, 1}, @"World");

// Delete the item at index 2 in section 0, no need for a model here :)

Items.delete({0, 2});


Sections sections;

sections.insert(0);

sections.insert(2);

sections.insert(3);


[datasource enqueueChangeset:{sections, items}];

It should be noted that:

  1. The order in which commands are added to the changeset doesn't define the order in which those changes will eventually be applied to the UICollectionView (same for UITableViews).

         In other words: The order of addition changeset does not represent change the order on the final UICollectionView final application.

     2. Remember initialization time to execute sections.insert (0);

     3. Because all change sets are asynchronous computation, it is necessary to pay attention to the problem of data is not synchronized with the UI may occur

        3.1 always datasource as the sole criterion, do not try to get model data from a source like the following example once in _listOfModels:

@implementation MyAwesomeController {

    CKComponentCollectionViewDataSource *_datasource;

    NSMutableArray *_listOfModels;

}

_datasource main building is an example of, _listOfModels is a small three.

Stick with:

[datasource objectAtindexPath:indexPath];

        3.2 Do not perform like: items.insert ({0, _datasource.collectionView numberOfItemsInSection}); statement, because you want to insert the location may not be where you want to insert.

Published 85 original articles · won praise 28 · Views 150,000 +

Guess you like

Origin blog.csdn.net/judgejames/article/details/104228180