EzDev.org

componentkit

componentkit - A React-inspired view framework for iOS. ComponentKit | A React-inspired view framework for iOS


Why is Facebook using UICollectionView instead of UITableView for their News Feed

If someone doesn't know, Facebook open sourced a new library called componentkit

The documentation says

ComponentKit really shines when used with a UICollectionView.

Facebook developed this lib for their news feed. That means that they are using the UICollectionView for it and not what I thought the UITableView.

Why is Facebook using the UICollectionView instead of the UITableView?

I mean the news feed is actually a table view or not?

Do you have any idea? What do you think about it?


Source: (StackOverflow)

How to use CKStackLayoutComponent to align two labels horizontally?

I've just started playing with ComponentKit and I'm having some difficulties aligning 2 labels horizontally. I want the first one close to the left margin and the second close to the right.

With auto layout, I can do it with this set of constraints:

H:|-0-[_label1]-[_label2]-0-|

Everything I tried doesn't seem to be working, I always get the same result: both labels left-aligned.

This is the super-simple component:

+ (instancetype)newWithText1:(NSString *)text1 text2:(NSString *)text2
{
  return [super
          newWithComponent:
          [CKStackLayoutComponent
           newWithView:{}
           size:{}
           style:{
             .direction = CKStackLayoutDirectionHorizontal,
             .alignItems = CKStackLayoutAlignItemsCenter
           }
           children:{
             {
               [CKLabelComponent
                newWithLabelAttributes:{
                  .string = text1,
                  .font = [UIFont systemFontOfSize:20],
                  .color = [UIColor blackColor]
                }
                viewAttributes:{
                  {@selector(setBackgroundColor:), [UIColor clearColor]}
                }],
               .alignSelf = CKStackLayoutAlignSelfStretch
             },
             {
               [CKLabelComponent
                newWithLabelAttributes:{
                  .string = text2,
                  .font = [UIFont systemFontOfSize:20],
                  .color = [UIColor redColor],
                  .alignment = NSTextAlignmentRight
                }
                viewAttributes:{
                  {@selector(setBackgroundColor:), [UIColor clearColor]}
                }],
               .alignSelf = CKStackLayoutAlignSelfStretch
             }
           }]];
}

If anyone has any advice that would be greatly appreciated.


Source: (StackOverflow)

Implementing simple uitableviewcell/collectionviewcell with Facebook Component Kit

Currently, I have already implemented with normal tableviewcell like this.

enter image description here

I am now studying about component kit and wana do sth like that view. I am reading through their example WildeGuess. There are very little tutorial or no tutorial for using component kit. How shall I implement to put label in left corner and imageview in left corner also (I can put imageview in the container as in example)?


Source: (StackOverflow)

CKComponentKit CollectionView issue

I am currently experimenting with ComponentKit and I am having an issue. I want to use the CKCollectionView I created on a main view. When I had the CKCollectionView to its container in my MainView(which contains a ScrollView) I am having 2 issues. The first one, if I manually create the frame of the CollectionView, here what it gives me.(Blue square is the "UICollectionViewControllerWrapperView")

UICollectionViewControllerWrapperView

But, what I really want to do is set the frame using Masonry and constraints, allowing me to set the frame equal to its container (the yellow square). But when I do that, it FILLS the entire Blue rectangle height with empty cells, like in the picture below:

Issue with Constraints

So it seems like my actual cells (or Components) have a size of 647 and they are causing problems.But at this point I am lost and was wondering if anybody had a similar issue before :).

Here is my code:

The actual component that creates the cell:

@implementation SSOUpcomingAuctionComponent


+ (instancetype)newWithProduct:(Product *)product {

    SSOProductHeaderComponent *head = [SSOProductHeaderComponent newWithTitle:product.time];
    SSOProductPictureComponent *body = [SSOProductPictureComponent newWithImage:product.image];
    SSOProductShareComponent *footer = [SSOProductShareComponent newWithPrice:product.price];

    return [super newWithComponent:[CKInsetComponent newWithInsets:UIEdgeInsetsMake(0, 0, 0, 0)
                                                         component:[CKStackLayoutComponent newWithView:{
                                                             [UIView class], {
                                                                 { @selector(setBackgroundColor:), [UIColor whiteColor] }
                                                                 , { CKComponentViewAttribute::LayerAttribute(@selector(setCornerRadius:)), @6.0 }
                                                                 , { @selector(setClipsToBounds:), @YES }
                                                             }
                                                         } size:{}
                                                        style:{
                                                             .alignItems = CKStackLayoutAlignItemsCenter, .direction = CKStackLayoutDirectionVertical,
                                                             .spacing = 2
                                                         } children:{{
                                                                         head,
                                                                     },
                                                                     {body},
                                                                     {footer}}]]];
}

#pragma mark - ProviderMethod

@end

The CollectionViewController:

#import "UpcomingAuctionViewController.h"
#import "SSOUpcomingAuctionComponent.h"
#import <ComponentKit/ComponentKit.h>
#import "Product.h"
#import "ProductPages.h"
#import "ProductModelController.h"
#import <ChameleonFramework/Chameleon.h>
#import "Masonry.h"

@interface UpcomingAuctionViewController () <CKComponentProvider, UICollectionViewDelegateFlowLayout>
@end

@implementation UpcomingAuctionViewController {
    CKCollectionViewDataSource *_dataSource;
    CKComponentFlexibleSizeRangeProvider *_sizeRangeProvider;
    ProductModelController *_productModelController;
}

- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout {
    if (self = [super initWithCollectionViewLayout:layout]) {
        _sizeRangeProvider = [CKComponentFlexibleSizeRangeProvider providerWithFlexibility:CKComponentSizeRangeFlexibleWidth];
        _productModelController = [[ProductModelController alloc] init];
        self.automaticallyAdjustsScrollViewInsets = NO;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    self.collectionView.backgroundColor = [UIColor flatWhiteColorDark];
//    [self.collectionView setFrame:CGRectMake(0, 0, 320, 180)];
        [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
          make.edges.equalTo(self.view);
        }];
    [self.collectionView setScrollEnabled:YES];

    self.collectionView.delegate = self;

    // Creates and sets our dataSource to our CKCollectionViewDataSource, THE MAIN ACTOR/INFRASTRUCTURE that takes our MODEL, creates a COMPONENT STACKS then
    // transforms it into a VIEW HIERARCHY.
    _dataSource = [[CKCollectionViewDataSource alloc] initWithCollectionView:self.collectionView
                                                 supplementaryViewDataSource:nil
                                                           componentProvider:[self class]
                                                                     context:nil
                                                   cellConfigurationFunction:nil];

    // The following block of code adds a section at 0 and two items at 0 and 1.
    CKArrayControllerSections sections;
    // insert section 0
    sections.insert(0);
    [_dataSource enqueueChangeset:{
        sections, {}
    } constrainedSize:{}];
    [self enqueueProductPages:[_productModelController fetchNewUpcomingProductsWithCount:14]];
}

- (void)enqueueProductPages:(ProductPages *)productPage {

    NSArray *products = productPage.products;
    NSInteger position = productPage.position;

    CKArrayControllerInputItems items;

    for (NSInteger i = 0; i < products.count; i++) {
        items.insert([NSIndexPath indexPathForRow:position + i inSection:0], products[i]);
    }
    [_dataSource enqueueChangeset:{
        {}
        , items
    } constrainedSize:[_sizeRangeProvider sizeRangeForBoundingSize:self.collectionView.bounds.size]];
}

#pragma mark - CKComponentProvider

// Method that our componentProvider class NEED to implement
+ (CKComponent *)componentForModel:(Product *)product context:(Product *)context {
    return [SSOUpcomingAuctionComponent newWithProduct:product];
}

#pragma mark - UICollectionViewDelegateFlowlayout

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout *)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return [_dataSource sizeForItemAtIndexPath:indexPath];
}

- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
    [_dataSource announceWillAppearForItemInCell:cell];
}

- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
    [_dataSource announceDidDisappearForItemInCell:cell];
}

And finally the mainViewController:

/**
 *  Initialize upcoming auctions container controller
 */
- (void)initializeUpcomingAuctionsContainerView {

    UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
    [flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    [flowLayout setMinimumInteritemSpacing:0];
    [flowLayout setMinimumLineSpacing:10];
    flowLayout.sectionInset = UIEdgeInsetsMake(0, 15, 0, 0);

    self.upcomingAuctionsVC = [[UpcomingAuctionViewController alloc] initWithCollectionViewLayout:flowLayout];

    [self addChildViewController:self.upcomingAuctionsVC];

    [self.bottomView addSubview:self.upcomingAuctionsVC.view];

    [self.upcomingAuctionsVC didMoveToParentViewController:self];
}

@end

Thanks for the help everyone and have a great day.


Source: (StackOverflow)

remove cell separator in ComponentKit containerView

How do you remove the cell separator in ComponentKit's containerview? I have tried setting the min properties to 0 and changing inlay and such. I keep getting the separator showing up/


Source: (StackOverflow)

How to create a CKComponentViewAttribute for a method with multiple arguments

I want to create a component for a UIButton subclass. To setup the button i want to set the image. My problem is I don't know how to make a CKComponentViewAttribute which takes the two arguments needed for UIButton method setImage:forState:.

I tried this:

[CKComponent newWithView:{
  [KGHitTestingButton class],
  {
    {CKComponentActionAttribute(@selector(onMenuTap))},
    {@selector(setImage:forState:), [UIImage imageNamed:@"location_action"]}, @(UIControlStateNormal)},
             }
  } size:{.width = 30, .height = 30}]

But that won't compile. The same with this version:

    {@selector(setImage:forState:), @[[UIImage imageNamed:@"location_action"]}, @(UIControlStateNormal)]},
             }

I read here http://componentkit.org/docs/advanced-views.html that I need to box my inputs into a single object. How do I do that in this case?

Update: I found the following hint here https://github.com/facebook/componentkit/issues/265 :

"CKComponentViewAttribute can be initialized with an arbitrary block. Here's an example:

static const CKComponentViewAttribute titleShadowColorAttribute = {"MyComponent.titleShadowColor", ^(UIButton *button, id value){
  [button setTitleShadowColor:value forState:UIControlStateNormal];
}};

"

And this is how I finally managed to solve my problem:

static const CKComponentViewAttribute imageAttribute = {"LocationActionButton.imageAttribute", ^(UIButton *button, id value){
    [button setImage:value forState:UIControlStateNormal];
}};
CKComponent *locationActionButton = [CKComponent newWithView:{
    [UIButton class],
    {
        {@selector(setBackgroundColor:), [UIColor redColor]},
        {imageAttribute, [UIImage imageNamed:@"location_action"]}
    }
} size:{.width = 30, .height = 30}];

You can do it inline to make it shorter:

CKComponent *locationActionButton = [CKComponent newWithView:{
    [UIButton class],
    {
        {@selector(setBackgroundColor:), [UIColor redColor]},
        {{"LocationActionButton.imageAttribute", ^(UIButton *button, id value){
            [button setImage:value forState:UIControlStateNormal];
        }}, [UIImage imageNamed:@"location_action"]}
    }
} size:{.width = 30, .height = 30}];

Source: (StackOverflow)

iOS Creating attributed label like TTTAttributedLabel in CKLabelComponent

I can easily use TTTAttributedlabel to have tappable url, name, etc.

1) How can I create something similar to that in CKLabelComponent?

2) Do I need to use CKTextComponent?

[CKLabelComponent newWithLabelAttributes:{
                               .string = @"This shall be context This shall be context This shall be context This shall be context",
                               .font = [UIFont fontWithName:@"Baskerville" size:14]
                           }
                           viewAttributes:{
                               {@selector(setBackgroundColor:), [UIColor clearColor]},
                               {@selector(setUserInteractionEnabled:), @NO},
                           }
                           size:{ }]

Source: (StackOverflow)

ComponentKit vs AsyncdisplayKit

I'm developing a social app, there is a main feature, "feeds".

so far It works with auto layout, everything is fine, but considering the cost of advanced development and maintenance in the future, I am looking for other async UI solutions.

I think ComponentKit, AsyncDisplayKit are what I looking for

but which one is better for me ?

both of them are supported async ui layout, and network image placeholder and cache ?

what's the difference between ComponentKit and AsyncDisplayKit ?

which one is better for complex feeds design ?

include : swift native / friendly, async ui layout, image cache & placeholder, complex feed style (horizontal cells in vertical cell) ... etc


Source: (StackOverflow)

How to assign UIGestureRecognizerDelegate to CKComponentController

I use the next code to setup UIPanGestureRecognizer in my component:

MyComponent *c =[super newWithView:{
  [UIView class],
  {
    {
      CKComponentGestureAttribute(
      [UIPanGestureRecognizer class],
      &setupPanRecognizer,
      @selector(panGesture:context:),
      {}
      )
    },
    {
      @selector(setUserInteractionEnabled:), @YES
    }
  }
  }
  component: [MyOtherComponent newOtherComponentWithMode:model context:context]];

I process panGesture:context in MyComponentController object.

My problem is that UIPanGestureRecognizer blocks feed view from scrolling. In order to fix this I want to use UIGestureRecognizerDelegate protocol and allow both (scroll view and my pan) recognizers to work simultaneously.

My question is how can I assign my component controller as a delegate for UIPanGestureRecognizer? setupPanRecognizer is just a C function and it does not have reference to MyComponentController object or even the component itself.

The only way I see now is to get list of gesture recognisers somewhere in didUpdateComponent method in my controller, find the right one and assign delegate there. Does ComponentKit provide any solution to this?


Source: (StackOverflow)

CKComponent update without model change

I have some kind of feed with story elements which can be liked, just like Facebook has.

My first step was just using the story model object to get the liked status and show that in a CKButtonComponent. The problem with that solution is that the button does not represent the expected new like status fast enough. Instead I'm making a web service call, which response changes the story and then the button gets changed.

To get a faster button state change, I build a CKComponentController for that CKComponent which updates a state of the component when the button gets pressed:

- (void)toggleLike {
    [self.component updateState:^(id oldState){
        if ([oldState integerValue] == LikeComponentStateInitial || [oldState integerValue] == LikeComponentStateNotLiked) {
            return @(LikeComponentStateLiked);
        }
        return @(STGLikeComponentStateNotLiked);
    }];
    [WebService toggleLikeForStory:...];
}

Additionally I'm making a web service call. The problem with that solution is that when I press the like-Button multiple times fast enough the app crashes with the message:

** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Unhandled component action toggleLike following responder chain nil'

The crash happens at an Assert in void CKComponentActionSend(CKComponentAction action, CKComponent *sender, id context, CKComponentActionSendBehavior behavior) in the CKComponentAction class. I don't know why this happens only when i hit the button fast enough multiple times. It seems to have something to do with reload of all components which happens when the new story data get saved from the web service response. The reload of all components and at the same time the CKButtonComponent getting the touch event seems to lead to the crash.

The CKComponentAction of the CKButtonComponent is the described CKComponentController method toggleLike.

So what would be a good solution in this case? Is there a way to do this without changing the story model object?

I have also the feeling that my approach to use the state to override some model data (like status) is generally not really a good idea.


Source: (StackOverflow)

ComponentKit and FLUX architecture - avoid unnecessary re-rendering of the UI

I'm using Facebook ComponentsKit to generate my views.

I now am migrating to a "flux" architecture for changing app state and triggering view updates.

The main problem I have is that not all state changes should trigger a UI update. I don't understand the "generic" mechanism to avoid that.

Basically the app state is a "big" "JSON" representing a "view model" (Parsed to native typed objects). The JSON contains all of the view declarations and their initial values. (The JSON is quite complex)

For example a simplified JSON representing a view hierarchy containing a "pager" component and a navigation "next" button :

{
   ... views ...
        {
           "pager" : {
                "id" : "pager-id-xxx",
                "currentPage" : 0,
                "pages" : [
                      ....pages....
                      {},
                      {},
                      ....pages....
                ]
            },
            ...
            "navigation-next-button" : {
                 "id" : "navigation-next-button-id-xxxx",
                 "target" : "pager-id-xxx"
            }
        },
   ... views ...
}

My "Flux" abstraction looks like:

// "Action" portion
@interface ChangePageAction

@property id destinationId; // very simplified action. wraps the destination "id"

@end

@implementation ChangePageReducer

-(JSON)reduce:(JSON)initialJSON  action:(ChangePageAction *)changePageAction {
      // the "reduce" portion finds the node of the pager (in the JSON) and changes the value by +1
      // something like:
      // find the node in the JSON with the changePageAction.destinationID
    Node *nodeCopy = getNodeCopy(initialJSON,changePageAction.destinationId);
    replaceValue(nodeCopy,nodeCopy.currentPage + 1);
    // according to FLUX doctrine we are supposed to return a new object
    return jsonCopyByReplacingNode(nodeCopy); // a new JSON with the updated values 
}

// the navigation button triggers the new state
@implementation NavigationNextButton {
   id _destination; // the target of this action
   FluxStore _store; // the application flux store
}

-(void)buttonPressed {
   ChangePageAction *changePage = ...
   [_store dispatch:changePage];

}
@end

In my "view controller" I now get a "update state" callback

@implementation ViewController 

-(void)newState:(JSON)newJSON {
    // here all of the view is re-rendered
    [self render:JSON];


   //The issue is that I don't need or want to re-render for every action that is performed on the state. 
   // many states don't evaluate to a UI update
   // how should I manage that?
}
@end

Source: (StackOverflow)

How do you configure a Component's underlying layer?

For example: how might you set the border width for a CKButtonComponent? When using views directly, you would specify it like this:

view.layer.borderWidth = xx


Source: (StackOverflow)

How to right align for the first component

I am trying to implement the following layout:

     Name: James
   Gender: Male
Followers: Brian, Jason and Mike.

I can imagine that the top level is to use CKStackLayout, but for each entry, e.g. "Name: James", how can I make the Name right align within a Box?

I was using something like the following, wrap the text within a fixed width component and set the label component to use NSTextAlignmentRight alignment, but it doesn't work and both left and right are just 'left-align':

static CKComponent *singleLine(NSString *left, NSString *right)
{
  CKStackLayoutComponent *stackComponent =
  [CKStackLayoutComponent
   newWithView:{}
   size:{}
   style:{
     .direction = CKStackLayoutDirectionHorizontal,
     .alignItems = CKStackLayoutAlignItemsStretch,}
       children:{
         {.component = [CKStaticLayoutComponent 
                        newWithView:{}
                        size:{.width = CKRelativeDimension(10)}]},
         {.component =
           [CKStaticLayoutComponent
            newWithView:{}
            size:{.width = CKRelativeDimension(88)}
            children:{
            {
            .component =
            [CKLabelComponent
             newWithLabelAttributes:{
               .string = left,
               .alignment = NSTextAlignmentRight,
               .font = [UIFont boldSystemFontOfSize:14.0],
               .maximumNumberOfLines = 1
             }
             viewAttributes:{}],
          }
        }],
     },
     {
       .component =
       [CKLabelComponent
        newWithLabelAttributes:{
          .string = right,
          .alignment = NSTextAlignmentLeft,
          .font = [UIFont systemFontOfSize:14.0],
          .maximumNumberOfLines = 1
        }
        viewAttributes:{}],
       .spacingBefore = 10.0
     }
   }];

  return stackComponent;
}

Thanks!


Source: (StackOverflow)

CKComponent handle tap gesture

Is it possible to have an CKComponent without an view to handle a tap gesture?

I just found this solution WITH a view:

...

+ (instancetype)newWithViewModel:(NewsComponentViewModel *)viewModel
{
    CKComponent *comp = ...;
    return [super
            newWithView:{
                [UIView class],
                {CKComponentTapGestureAttribute(@selector(didTapView))}
            }
            component:comp];
}

Source: (StackOverflow)

ComponentKit how push view controllers CKComponentController

Hi just stated to use the ComponentKit library given from facebook and i have been going through all their documentation but i could not find how to use their CKComponentController class.

Like how to push a view controller and about the navigation of various view controllers.

if any of you are aware of how to use the CKComponentController please let me know i'm a bit stuck due to less documentation

Thank You. Imran.


Source: (StackOverflow)