-
Notifications
You must be signed in to change notification settings - Fork 83
Teacup
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.
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
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.
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
.
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
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.
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.
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
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.
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.