I finally got a chance to check out Thomas Kadauke's TinyMon app that he has graciously posted on github. For one, it's certainly a well designed, well written app! The tests in particular are commendable! They put my apps to shame...

This little guy caught my eye: https://github.com/tkadauke/TinyMon/blob/master/lib/teacup_gradient_handler.rb

I don't think I've seen someone use this feature of teacup before! It is a perfect use of a Teacup handler (and I'm encouraging Thomas to add this to sweettea!)

So, I thought I would explain a little bit about what Teacup handlers are, how you write them, and I'll write a bit more about sweettea, and why it is REALLY what you should be using in conjunction with teacup (even if you don't use any other sugarcube features).


What teacup does do

Teacup does, basically, two things:

  1. Provides a DSL to create a view hierarchy (usually from within your controller). This is designed to be terse, so that your controller isn't cluttered with lots of UIView-creation code, and you can assign views to instance variables with ease.

    class MyController < UIViewController
    
      stylesheet :user_form
      layout do
        @container = subview(UIView, :container) do
          subview(UILabel, :full_name_label)
          @full_name_input = subview(UITextField, :full_name_input)
          subview(UIButton, :submit)
        end
      end
    
    end
    
  2. Provides a DSL to style those views using a CSS-like language, which usually takes place in Teacup::Stylesheet objects (but doesn't have to, you know!). There are a few "magical" keys, but for the most part they are UIKit methods. Also, the Stylesheet class defines a few helper methods

    Teacup::Stylesheet.new :user_form do
      import :app_styles
    
      style :container,
        frame: [[0, 0], app_size],  # `app_size` takes the status bar into account
        autoresizingMask: flexible_width | flexible_height,  # some resizing mask helpers
        backgroundColor: UIColor.lightGrayColor  # traditional UIKit
    
      style :full_name_label, extends: :label,
        frame: [[20, 20], [100, 20]],
        text: 'Your name:'
    
      style :full_name_input, extends: :input,
        frame: [[110, 20], [200, 20]],
        placeholder: 'Englebert Humperdink'
    
    end
    

extends: is one "magical" key, constraints: is another. I'm not gonna go into constraints here - check out my post about them.

What teacup can do

Teacup doesn't just map the style key to a method name. It maintains a list of "handlers", and if you know about these and understand them, you can write stylesheets that are much more expressive!

There aren't very many built-in handlers; this keeps the teacup library lean-and-mean. The ones that are there are related to positioning, and some very basic UIButton handlers, which are offered as examples of what a handler can do (the original teacup implementation, written by Conrad Irwin had these helpers as special cases - much later, they were re-implemented as handlers). Here are all the built in handlers:

style :button,
  # size:
  width: 100,
  height: 20,

  # x-position
  left: 10,
  x: 10,  # same thing
  right: 10,  # you must have a superview and width defined already
  # same as `x: 220` on an iphone
  center_x: app_size.width / 2,  # easy centering - again, you need the width
  middle_x: 160,  # alias

  # y-position
  top: 10,
  y: 10,  # alias
  bottom: 10,  # either 430 or 538 on an iphone w/ status bar visible
  center_y: app_size.height / 2,
  middle_y: app_size.height / 2,

  # assign size or origin
  size: [100, 20],
  origin: [10, 10],
  center: [app_size.width / 2, app_size.height / 2],  # this isn't an alias!  center IS a property on UIView.  here for completeness

  # assign the frame - this is an interesting handler, because it assigns the frame,
  # but offers some shorthands and introspection capabilities... read on!
  frame: [[10, 10], [100, 20]],

  # UIButton specific
  title: 'Press Me',  # setTitle('Press Me', forState:UIControlStateNormal)
  titleColor: UIColor.blueColor,
  titleFont: UIFont.systemFontOfSize(16),
  font: UIFont.systemFontOfSize(16)  # alias

These handlers don't just assign the top/left/size/whatever - they can perform some introspection on the receiver, depending on the argument!

style :button:
  x: '10%',  # 320width screen => x: 32
  center_x: '50%',  # centered!
  width: 90%,
  frame: :full  # takes up the entire bounds of the superview

Enter SugarCube, stage right

SugarCube offers a TON of shorthands, many of them are related to styling (this is not an accident, obviously). But you still have to pass the right type to these methods, so it's shorter but still not a DSL:

style :label,
  textAlignment: :center.uitextalignment,  # this is only a modest "improvement"
  textColor: :blue.uicolor                 # (and many would argue that point!)

Nice, but it doesn't really look like a stylesheet yet. It's still a verbose iOS-y jumble...

Enter sweettea, stage left

In a nutshell, sweettea adds handlers to teacup that interface with sugarcube.

style :label,
  alignment: :center,
  color: :blue

Is that much better!? I think so! It's not magic, either - the handlers are expliticly defined in the sweettea handlers file. Keep that file handy!

I can't go through all the sweettea shortcuts here, for the sake of brevity, but I'm updating the README today, once I'm done with this post.

Enter YOU, stage center!

Now we can understand how tkadauke's gradient handler works!

# the class `UIView` indicates that this handler can be applied to all subclasses
# of UIView. :gradient is the name of the key in the `style` declaration
# associated with this handler.
Teacup.handler UIView, :gradient { |layer|
  # `self` is the UIView being styled,
  # `layer` is the options you pass in your stylesheet
  gradient = CAGradientLayer.layer  # create a gradient layer
  gradient.frame = self.bounds      # have it fill the view layer
  layer.each do |key, value|        # apply all the options in `layer`
    gradient.send("#{key}=", value)
  end
  self.layer.insertSublayer(gradient, atIndex:0)  # add it to the layers
}

(btw, this is going to get added to sweettea SOON, in one form or another)

The sweettea handlers take Teacup out of just a DSL/wrapper for UIKit, into a full-blown framework to help you style your UIViews. I've heard a lot of comparisons between teacup and Pixate recently, but if you're not using teacup handlers, you're only touching the surface of what teacup can do to help you!