Skip to content
ryansobol edited this page Aug 14, 2012 · 4 revisions

Styles

In this first release, the Teacup::Stylesheet class supports named-styles only. There has been talk of applying styles by view class and supporting nesting of styles, and having rules applied using CSS-like precedence calculations, but that is simply beyond the scope of "initial launch".

These ideas are not off the table, but they are tabled for now. We would like a cool down period where we use the Stylesheets in their simple form for now, and later we will evaluate how people are actually using these, and see how we can make things better for them. Iterative development FTW!

Stylesheets make heavy use of sugarcube, so check that out if some of this looks like magic.

Example

app/styles/stylesheets.rb
class MyStylesheet < Teacup::Stylesheet
  style :shadow, {
    # converted to UIColor using sugarcube, though the explicit call to
    # `.uicolor` is not necessary.  teacup will notice the type mismatch and
    # call `.uicolor` on *any* value that is passed.
    shadow_color: :black.uicolor,
    shadow_offset: 3,
    shadow_radius: 1,
  }

  style :icon, { extends: :shadow
    frame: [[2, 2], [40, 40]],
  }

  # curlies are optional, though I personally prefer them.
  style :title,
    font: :bold.uifont(14),  # again, from sugarcube
    frame: [[44, 0], [276, 20]]

  style :info, {
    font: :small.uifont,
    # again, as long as sugarcube supports it, you're golden!  you can monkey
    # patch any object or class that you would like to use here.  but if it is a
    # convenient abstraction, please consider sending a pull request to
    # sugarcube.
    font_color: :gray,
  }

  style :date, { extends: :info
    textAlignment: :left.uialignment,
    frame: [[44, 20], [138, 12]],
  }

  style :more_info, { extends: :info
    textAlignment: :right,
    frame: [[138, 20], [138, 12]],
  }

  style :amount, {
    frame: [[5, 44], [310, 15]],
  }

  style :event, {
    frame: [[5, 59], [310, 15]],
  }

  style :who_participated, {
    frame: [[0, 74], [320, 420]],
  }

  style :ok_button, {
    frame: [[0, 310], [20, 10]],
    text: "OK",
  }
end


class MyIpadStylesheet < MyStylesheet
  style :who_participated {
    frame: [[0, 74], [768, 960]],
  }
end

Notes

All the code related to color, font, and position is moved into a stylesheet, and by changing the stylesheet of your view, you can reposition and restyle elements based on orientation, device, or theme choice.

Layouts

The best way to think of a layout file is as a lightweight .nib file. You subclass a UIView class, give it a view method, and create your subviews inside that. When you instantiate your view - from a Controller, or from within your layout method - you can apply a stylesheet.

You can extend from any UIView subclass, but the most common are containers like UIView and UITableCellView. The layout will be added to your view at the end of initialization, so as soon as you create your view, you can access any subviews that you make available using attr_accessor.

Example

app/views/layouts.rb
class MainLayout < UIView
  include Teacup::Layout

  attr_accessor :amount, :event, :ok_button

  layout do
    subview(UIImage, :icon)
    subview(UILabel, :title)
    subview(UILabel, :date)
    subview(UILabel, :more_info)
    subview(UILabel, :who_participated)

    self.ok_button = subview(UIButton, :ok_button)
    self.amount = subview(UITextField, :amount)
    self.event = subview(UITextField, :event)
  end
end

Notes

All that we do here is create our view hierarchy, and apply styles by name. In this example, we expose three views: amount, event, and ok_button. The controller, after creating this view, can attach target/action events to those views, or bind change events to redraw the view.

Controllers

You're on your own! teacup is, and will probably remain, a way to create and style views. Here is an example of creating and interacting with the view from a controller.

Example

app/controllers/main.rb
class MainViewController < UIViewController
  def viewDidLoad
    layout = UIView.alloc.initWithFrame(UIScreen.mainScreen.applicationFrame)
    if iPad?
      layout.stylesheet = MyIpadStylesheet
    else
      layout.stylesheet = MyStylesheet
    end
    @amount.text = layout.amount
    @event.text = layout.event

    layout.amount.delegate = self
    layout.event.delegate = self
    layout.ok_button.addTarget(self, action: :click, forControlEvents:UIControlEventTouchUpInside)

    view << layout
  end

  def textFieldShouldReturn(textField)
    if textField == @amount
      @event.becomeFirstResponder
    else
      @event.resignFirstResponder
    end
  end

  def click
    if @amount.text.to_f > 0
      alert('w00t!, amount is !');
    else
      alert('ZOMG, amount is negative');
    end
  end
end

Controller/Layout/Stylesheet hybrids

But I am le tired! I do not want to create all these classes!

As @ConradIrwin put it "modularity is fashionable". You do not have to break up your code in the way we have here. You can, if it floats your boat, put all this code back in your controller, and you can still benefit from what we think is a more rubyesque style of coding.

Example

app/controller.rb
class MainViewController < UIViewController

  def viewDidLoad
    subview(UILabel, {
      text: "I am le tired",
      fontColor: UIColor.blueColor
      })
  end

end

The traditional rubymotion code to do this looks like this:

class MainViewController < UIViewController

  def viewDidLoad
    label = UILabel.alloc.init
    label.text = "I am le tired"
    label.fontColor = UIColor.blueColor
    self.view.addSubview(label)
  end

end

Just as many lines of code - it is purely a matter of taste which you prefer.

Clone this wiki locally