Stylesheets are designed for a low memory footprint, fast startup, and fast operation. Most everything is done at compile-time, as it's all just ruby code. Low magic.
They are called styles and stylesheets, but they are used for layout also. Basically if it changes the way a user sees something, it belongs in a stylesheet.
- RedPotions's stylesheets are very simple, contain no (well, a little) magic, and are fast, as much as possible is done at compile time, not runtime
- As a styling and layout layer, it has the important features without sacrificing speed or simplicity
- You can style for different devices, device sizes, orientations, etc (see test app for an example of this)
- Exceptions tell you exactly where in your stylesheet the exception occurred
- A style is simply a method in the stylesheet class. It is passed a styler
- You can apply a style to a view or views at anytime. They override existing settings
Apply a style when creating the view
append UILabel, :title_label
Apply a style to existing views
find(your_view, your_other_view).apply_style(:title_label)
Apply styles inline (you should use an existing style instead)
def title_label(st)
st.frame = {l: 10, t: 10, w: 200, h: 96}
st.image = image.resource('logo')
end
Lots of methods exist for adding/building/adjusting views add styles. You can apply a style in multiple locations.
Reapply styles - If your view updates
If a view has multiple styles applied to it and then you make some modifications to your view or screen, you can then call reapply_styles
selected view(s) so that all styles will be reapplied again in the correct order from one simple method. "Write once, reapply anytime!"
Most commonly used when new items are dynamically loaded, or if width/height of a view are changed (like on rotation).
def will_animate_rotate(orientation, duration)
reapply_styles
end
Layouts
Anything that can be applied using the grid or rect system (see Layout below). Can be applied with st.frame =
find(your_view).style do |st|
st.frame = {grid: 'a1:c', h: 96}
end
Previous view
In addition to Layout's :below_prev, :right_of_prev, etc. You can get the previous frame and view like so:
def foo(st)
st.frame = {l: 10, t: st.prev_frame.top, w: 200, h: 96}
st.text = st.prev_view.text
end
A simple example of screen & stylesheet
home_screen.rb
class HomeScreen < PM::Screen
title "RedPotion Rocks!"
stylesheet HomeScreenStylesheet
def on_load
append UILabel, :title_label
append UIImageView, :logo
end
end
home_screen_stylesheet.rb
class HomeScreenStylesheet < RubyMotionQuery::Stylesheet
def root_view(st)
st.background_color = color.white
end
def label(styler)
styler.background_color = color.clear
end
def logo(st)
st.frame = {t: 10, w: 200, h: 96, centered: :horizontal}
st.image = image.resource('logo')
st.view.someAttributeNotInStyler = 'foo'
end
def title_label(st)
label st # combine 0 or more styles
st.frame = {l: 5, below_prev: 5, w: 200, h: 20}
st.text = 'Test label'
st.color = color.from_hex('#3F3F3F')
end
end
Aplication stylesheet
You'll probably want an application-wide stylesheet. Simple create one, and then inherit it in all your stylesheets.
If you use potion to create your app, it will automatically create this for you. Even if you don't, I'd create a test app to see how it is all setup: potion new test_app
application_stylesheet.rb
class ApplicationStylesheet < RubyMotionQuery::Stylesheet
def label(styler)
styler.background_color = color.clear
end
end
foo_stylesheet.rb
class FooStylesheet < ApplicationStylesheet
def bar(st)
...
end
end
*** Here is a longer example
class ApplicationStylesheet < RubyMotionQuery::Stylesheet
def application_setup
# App grid, used by default throughout the applicaiton
app.grid.tap do |g|
g.content_left_margin = 10
g.content_right_margin = 10
g.content_top_margin = 74
g.content_bottom_margin = 10
g.num_columns = 12
g.column_gutter = 10
g.num_rows = 18
g.row_gutter = 10
end
# Named fonts
font_family = 'Helvetica Neue'
font.add_named :very_large, font_family, 56
font.add_named :large, font_family, 24
font.add_named :medium, font_family, 18
font.add_named :standard, font_family, 16
font.add_named :small, font_family, 14
font.add_named :tiny, font_family, 12
# Named colors
color.add_named :tint, '#438AF0'
color.add_named :gray, '#DDDDDD'
color.add_named :light_gray, '#EDEDED'
color.add_named :very_light_gray,'#F4F4F4'
# Set other application-wide visual things, such as appearances:
SVProgressHUD.appearance.hudBackgroundColor = color.light_gray
SVProgressHUD.appearance.hudForegroundColor = color.black
end
# Some standard styles you can apply or use in other styles
def standard_button(st)
st.frame = {h: 30}
st.background_color = color.tint
st.color = color.white
st.corner_radius = 5
end
def standard_button_enabled(st)
st.background_color = color.tint
st.color = color.white
st.enabled = true
end
def standard_button_disabled(st)
st.background_color = color.gray
st.color = color.white
st.enabled = false
end
def shadow(st)
st.clips_to_bounds = false
st.view.layer.tap do |l|
l.shadowColor = color.black.CGColor
l.shadowOpacity = 0.2
l.shadowRadius = 1.0
l.shadowOffset = [0.5, 1.0]
end
end
end
Example stylesheet
This stylesheet uses normal rect layout (left, right, etc). It could also be done using the grid.
I purposely used a variety of different things in this stylesheet to show different examples. In real life, I personally would use fr:, not from_right:. Both are fine, but I'd be consistent.
class MainStylesheet < ApplicationStylesheet
def setup
# Add stylesheet specific setup stuff here.
# Add application specific setup stuff in application_stylesheet.rb
@padding = 10
end
def root_view(st)
st.background_color = color.white
end
def logo(st)
st.frame = {top: 77, width: 200, height: 95.5, centered: :horizontal }
st.image = image.resource("logo") # for logo@2x.png or logo.png
end
def make_labels_blink_button(st)
st.frame = {fr: @padding, t: 180, w: 150, h: 20}
# That is exactly the sames as st.frame = {from_right: @padding, top: 180, width: 150, height: 20}
# Each frame/bounds hash option has an abbreviation
# ipad? (and landscape?, etc) is just a convenience methods for
# device.ipad?
# Here is a complete example of different formatting for orientatinos
# and devices
# if ipad?
# if landscape?
# st.frame = {l: 20, t: 120, w: 150, h: four_inch? ? 20 : 30}
# else
# st.frame = {l: 90, t: 120, w: 150, h: four_inch? ? 25 : 35}
# end
# else
# if landscape?
# st.frame = {l: 20, t: 20, w: 150, h: four_inch? ? 22 : 32}
# else
# st.frame = {l: 90, t: 20, w: 150, h: four_inch? ? 30 : 40}
# end
# end
# If you don't want something to be reapplied when reapply_styles is called
unless st.view_has_been_styled?
st.text = "Blink labels"
st.font = font.system(10)
st.color = color.white
# You can hardcode colors in styles like this, but it's better to use named colors
st.background_color = color("ed1160")
end
end
def make_buttons_throb_button(st)
st.frame = {from_right: @padding, below_prev: @padding, width: 150, height: 20}
st.text = "Throb buttons"
st.color = color.black
end
def animate_move_button(st)
st.scale = 1.0
st.frame = {fr: @padding, bp: 5, w: 150, h: 20}
st.text = "Animate move and scale"
st.font = font.system(10)
st.color = color.white
st.background_color = color("ed1160")
st.z_position = 99
st.color = color.white
end
def section(st)
st.frame = {fb: @padding, w: 270, h: 110}
st.frame = if landscape? && iphone?
{left: @padding }
else
{centered: :horizontal}
end
st.z_position = 1
st.background_color = color.battleship_gray # This is a named color
end
def section_label(st)
label st # This is an example of calling a style that you created in the ApplicationStylesheet
st.color = color.white
end
def popup_section(st)
t = (landscape? && iphone?) ? 100 : 180
st.frame = {l: @padding, t: t, w: 100 + (@padding * 2), h: 60}
st.background_color = color.blue
end
def popup_text_label(st)
label st # This is an example of calling a style that you created in the ApplicationStylesheet
st.text_alignment = :left
st.color = color.black
st.font = font.system(10)
st.text = "This is a Popup, woot!"
# If the styler doesn't have the method, you can add it or
# just use st.view to get the actual object
st.view.lineBreakMode = NSLineBreakByWordWrapping
end
end