Advanced Views Edit on GitHub

Back in Views we glossed over CKComponentViewClass and CKComponentViewAttribute. But there’s a surprising amount of power hiding here!

CKComponentViewClass describes how to create a UIView. Usually you pass an Objective-C class like [UIImageView class], and ComponentKit will automatically create a view by invoking the designated initializer -initWithFrame:.

But of course, not all views can be created with -initWithFrame:. For these cases, there’s an advanced constructor that takes a function pointer:

CKComponentViewClass(UIView *(*factory)(void));

This allows you to use almost any view with ComponentKit. Here’s one example:

static UIView *authorViewForOscarWilde(void) {
  return [[AuthorView alloc] initWithName:@"Oscar Fingal O'Flahertie Wills Wilde"];
}
// ...
[CKComponent newWithView:{&authorViewForOscarWilde} size:{50, 50}]

However, the factory takes no arguments; you can't pass any properties from a particular component to a view's -init, since views must be arbitrarily recycled between components. If your view takes properties in -init, one possible pattern is to create a wrapping view with setter methods that tear down and re-create the problematic view as a subview.

CKComponentViewAttribute has similar underpinnings. Usually you pass a SEL, but under the hood they’re basically just arbitrary blocks paired with a unique identifier:

struct CKComponentViewAttribute {
  std::string identifier;
  void (^applicator)(id view, id value);
  void (^unapplicator)(id view, id value);
  void (^updater)(id view, id oldValue, id newValue);
};

This allows you to express arbitrarily complex operations on the view, like “call this method with these N arguments”. The only restriction is that you must box up the inputs to the block in a single id so ComponentKit can determine if it has changed across recyclings.

How does passing a Class or SEL work for these classes? They each have a single-argument constructor that takes a Class/SEL and creates an object that creates the right thing. C++ implicitly calls this constructor when you pass a Class/SEL.