robb / Cartography

A declarative Auto Layout DSL for Swift :iphone::triangular_ruler:
Other
7.35k stars 525 forks source link

Make LayoutProxy functions not internal? #277

Open ninjudd opened 6 years ago

ninjudd commented 6 years ago

Hey there. I love Cartography!

But, I'm trying to add some extensions to LayoutProxy, and I'm running into trouble because all the functions are internal.

Any chance you'd be open to making them public?

corujautx commented 6 years ago

Which functions are you thinking of making public and what extension are you thinking of adding to LayoutProxy? I'm think of refactoring it a bit to add type safety (aka Edge will be turned into a protocol instead, and we will have TopEdge, BottomEdge... as type-safe edges to make sure Edges, Point and Size are initialized with the correct types).

I did not expose those functions because it means that people would have access to an API that would not be meaningful to them externally.

PS: if you are thinking of being capable of constraining view controllers, I've been thinking of a similar idea, creating a layout proxy that would not be positionable but would instead offer properties as view, topLayoutGuide and bottomLayoutGuide so we could ditch car_topLayoutGuide and car_bottomLayoutGuide type erasures.

ninjudd commented 6 years ago

We have a two extensions that I added in my fork that we use extensively throughout our code. Here is an example of the usage:

    constrain(statusView) { statusView in
      statusView.edges(.bottom, .left, .right) == statusView.superview!.edges(.top, .left, right).inset(-4, 0, 0)
      statusView.width == 70
      statusView.height == 30
    }

The edges function takes a variable number of LayoutAttributes and returns an Edges object. Inset is an extension to Edges since the built-in inset function requires an Edges object with exactly 4 edges.

corujautx commented 6 years ago

Hmm, I'm thinking of avoiding this in Edges because it semantically represents the edges of a certain layout element (its top, leading, bottom and trailing edges respectively).

What you do basically exposes to runtime errors, as the edges on each side have to match the edges on the other side otherwise we would have a fatal error. I kinda wanna avoid this type of things, plus I feel it's not semantically representative.

One option maybe is creating an == operator for arrays of Property? e.g. [statusView.bottom, statusView.left, statusView.right] == [statusView.superview!.bottom, statusView.superview!.left, statusView.superview!.right] but that still is prone to runtime errors and I have no clue what is the proper behavior to be applied to operators like + and * in an expression like that.

ninjudd commented 6 years ago

Yeah. Type safety is a good thing. I'd love a way to use the shortened syntax for anchoring multiple edges while still having it be type safe. But there will be a lot of permutations if there is a separate type for top, bottom, leading, trailing, etc.

Are you sure you need that many types? Wouldn't just two types (HorizontalEdge and VerticalEdge) be sufficient?