Teacup
This project has been sunset in favor of MotionKit
Install / Use
/learn @colinta/TeacupREADME
Teacup
A community-driven DSL for creating user interfaces on iOS and OS X.
Using Teacup, you can create and style layouts and keeping your code dry. The goal is to offer a rubyesque (well, actually a rubymotion-esque) way to create interfaces programmatically.
A note about Teacup and MotionKit
Read a letter from Colin T.A. Gray regarding the future of Teacup and introducing MotionKit, its successor.
Check out some sample apps!
- iOS
- OS X
- Tweets - ported from RubyMotionSamples
- simple
Quick Install
> gem install teacup
and in your Rakefile
require 'teacup'
10 second primer, iOS
-
Create a
UIViewControllersubclass:class MyController < UIViewController -
Assign a stylesheet name:
class MyController < UIViewController stylesheet :main_screen -
Create a layout:
class MyController < UIViewController stylesheet :main_screen def teacup_layout subview(UIButton, :hi_button) end end -
Create the stylesheet (in
app/styles/or somewhere near the controller)Teacup::Stylesheet.new :main_screen do style :hi_button, origin: [10, 10], title: 'Hi!' end
10 second primer, OS X
Pretty much the same! Note that on OS X, view coordinates are based on having the origin in the bottom-left corner, not the upper-left like it is on every other GUI system ever. :-|
You should use the TeacupWindowController parent class instead of NSWindowController
-
Create a
TeacupWindowControllersubclass.class MyController < TeacupWindowController -
Assign a stylesheet name:
class MyController < TeacupWindowController stylesheet :main_window -
Create a layout:
class MyController < TeacupWindowController stylesheet :main_window def teacup_layout subview(NSButton, :hi_button) end end -
Create the stylesheet (in
app/styles/or somewhere near the controller)Teacup::Stylesheet.new :main_window do style :hi_button, origin: [10, 10], title: 'Hi!' end
Teacup
Teacup's goal is to facilitate the creation and styling of your view hierarchy. Say "Goodbye!" to Xcode & XIB files!
Teacup is composed of two systems:
-
Layouts A DSL to create Views and to organize them in a hierarchy. You assign the style name and style classes from these methods.
-
Stylesheets Store the "styles" that get applied to your views. The stylesheet DSL is meant to resemble CSS, but is targeted at iOS, and so the precedence rules are very different.
Teacup supports [Pixate][] and [NUI][], too, so you can use those systems for styling and Teacup to manage your view hierarchy and apply auto-layout constraints. Teacup can also integrate with the [motion-layout][] gem!
Changes in 3.0
There is one significant change in version 3.0. In every version of Teacup
prior (from 0.2.0 to 2.3.0) the controller layout was usually created by calling
a class method called layout. It was discovered, embarrassingly late, that
this system is causing memory leaks. To fix it we had to remove this feature
altogether. So if you are looking at old Teacup examples, you will see this
block syntax that is no longer offered. It is easy to update to 3.0, though:
# <= 2.3.0
class MyController < UIViewController
layout(:root_stylename) do # <= this block is what caused the memory leak!
# teacup code goes here
end
end
# 3.0
class MyController < UIViewController
def teacup_layout # in 3.0 we just changed it to be a method
# introduced in 3.0, this is how you assign a stylename to the root view
root(:root_stylename, { background: UIColor.blueColor })
# teacup code goes here - no other code changes necessary
end
# actually, this method still works as long as you don't pass a block. It's
# the same as calling `root(stylename, {})`
layout(:root_stylename, {})
end
Table of Contents
- Layouts — define your views
- Stylesheets — style your views
- UITableViews - This is important if you are using styles and constraints in a
UITableViewDelegate. - More Teacup features
- Showdown — Cocoa vs Teacup
- The Nitty Gritty — some implementation details and gotchas
- Advanced Teacup Tricks
- Misc notes
- The Dummy — fixes “uncompiled selector” errors
Layouts
The Teacup::Layout module is mixed into UIViewController and UIView on
iOS, and NSWindowController, NSViewController, and NSView on OS X. These
classes can take advantage of the view-hierarchy DSL.
You saw an example in the primer, using the
UIViewController/NSWindowController class method layout and the
teacup_layout method. You could just as easily use Teacup's DSL to create
your views from a loadView method, for instance you might want to use a
custom view subclass as your root view. An example might look like this:
# controller example
class MyController < UIViewController
def loadView
# we will create the controller's view, assigning it the stylename :root
self.view = layout(FancyView, :root) do
# these subviews will be added to `self.view`
subview(UIToolbar, :toolbar)
subview(UIButton, :hi_button)
end
end
end
You can use very similar code in your view subclasses.
# view example
#
# if you use Teacup in all your projects, you can bundle your custom views with
# their own stylesheets
def MyView < UIView
def initWithFrame(frame)
super.tap do
self.stylesheet = :my_stylesheet
subview(UIImageView, :image)
end
end
end
The layout and subview methods are the work horses of the Teacup view DSL.
layout(view|ViewClass, stylename, style_classes, additional_styles, &block)view|ViewClass- You can layout an existing class or you can have Teacup create it for you (it just callsnewon the class, nothing special). This argument is required.stylename(Symbol) - This is the name of a style in your stylesheet. It is optionalstyle_classes([Symbol,...]) - Other stylenames, they have lower priority than thestylename.additional_styles(Hash) - You can pass other styles in here as well, either to override or augment the settings from theStylesheet. It is common to use this feature to assign thedelegateordataSource.&block- See discussion below- Returns the
viewthat was created or passed tolayout. - only the
viewarg is required. You can pass any combination of stylename, style_classes, and additional_styles (some, none, or all).
subview(view|UIViewClass, stylename, style_classes, additional_styles, &block)- Identical to
layout, but adds the view to the current target
- Identical to
The reason it is so easy to define view hierarchies in Teacup is because the
layout and subview methods can be "nested" by passing a block.
subview(UIView, :container) do # create a UIView instance and give it the stylename :container
subview(UIView, :inputs) do # create another container
# these views will be added to the :inputs view
@email_input = subview(UITextField, :email_input)
@password_input = subview(UITextField, :password_input)
end
# this view will be added to :container
subview(UIButton.buttonWithType(UIButtonTypeRoundedRect), :submit_button)
end
These methods are defined in the Layout module. And guess what!? It's easy
to add your own view helpers! I refer to this as a "partials" system, but
really it's just Ruby code (and isn't that the best system?).
# the methods you add here will be available in UIView/NSView,
# UIViewController/NSViewController/NSWindowController, and any of your own
# classes that `include Teacup::Layout`
module Teacup::Layout
# creates a button and assigns

