Code Monkey home page Code Monkey logo

teacup's Introduction

Teacup

A community-driven DSL for creating user interfaces on iOS and OS X.

Build Status

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!

Quick Install

> gem install teacup

and in your Rakefile

require 'teacup'

10 second primer, iOS

  1. Create a UIViewController subclass:

    class MyController < UIViewController
  2. Assign a stylesheet name:

    class MyController < UIViewController
      stylesheet :main_screen
  3. Create a layout:

    class MyController < UIViewController
      stylesheet :main_screen
    
      def teacup_layout
        subview(UIButton, :hi_button)
      end
    end
  4. 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

  1. Create a TeacupWindowController subclass.

    class MyController < TeacupWindowController
  2. Assign a stylesheet name:

    class MyController < TeacupWindowController
      stylesheet :main_window
  3. Create a layout:

    class MyController < TeacupWindowController
      stylesheet :main_window
    
      def teacup_layout
        subview(NSButton, :hi_button)
      end
    end
  4. 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

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 calls new on the class, nothing special). This argument is required.
    • stylename (Symbol) - This is the name of a style in your stylesheet. It is optional
    • style_classes ([Symbol,...]) - Other stylenames, they have lower priority than the stylename.
    • additional_styles (Hash) - You can pass other styles in here as well, either to override or augment the settings from the Stylesheet. It is common to use this feature to assign the delegate or dataSource.
    • &block - See discussion below
    • Returns the view that was created or passed to layout.
    • only the view arg 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

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 a default stylename
  def button(*args, &block)
    # apply a default stylename
    args = [:button] if args.empty?

    # instantiate a button and give it a style class
    subview(UIButton.buttonWithType(UIButtonTypeCustom), *args, &block)
  end

  # creates a button with an icon image and label
  def button_with_icon(icon, title)
    label = UILabel.new
    label.text = title
    label.sizeToFit

    image_view = UIImageView.new
    image_view.image = icon
    image_view.sizeToFit

    button = UIButton.buttonWithType(UIButtonTypeCustom)
    button.addSubview(image_view)
    button.addSubview(label)

    # code could go here to position the icon and label, or at could be handled
    # by the stylesheet

    subview(button)
  end

end
example use of the helper methods
class MyController < UIViewController

  def teacup_layout
    @button1 = button()
    @button2 = button(:blue_button)
    @button3 = button_with_icon(UIImage.imageNamed('email_icon'), 'Email')
  end

end

The Controller#teacup_layout method is going to be the first or second thing you add to a controller when you are building an app with Teacup. Inside you will add subviews using subview or you can create a view using the layout method (subview delegates most of its work to layout)

After the views have been added and styles have been applied Teacup calls the layoutDidLoad method. If you need to perform some additional initialization on your views, you can do it in this method.

Stylesheets

This is where you will store your styling-related code. Migrating code from your controller or custom view into a stylesheet is very straightforward. The method names map 1::1.

# classic Cocoa/UIKit
def viewDidLoad
  self.view.backgroundColor = UIColor.grayColor
  #         ^.............^
end

# in Teacup
def viewDidLoad
  self.stylesheet = :main
  self.view.stylename = :root
end

Teacup::Stylesheet.new :main do
  style :root,
    backgroundColor: UIColor.grayColor
#   ^.............^
end

Nice! We turned three lines of code into nine! Well, obviously the benefits come in when we have lots of style code, and when you need to do app-wide styling.

You can store stylesheets in any file. It is common to use app/styles.rb or app/styles/main.rb, if you have more than a few of 'em. The Teacup::Stylesheet constructor accepts a stylesheet name and a block, which will contain your style declarations.

Teacup::Stylesheet.new :main_menu do
  style :ready_to_play_button,
    backgroundColor: UIColor.blackColor,
    frame: [[20, 300], [50, 20]]
end

Teacup::Stylesheet[:main_menu]  # returns this stylesheet

Any method that accepts a single value can be assigned in a stylesheet. Please don't abuse this by hiding application logic in your stylesheets - these are meant for design, not behavior.

Limelight syntax

If you want to use a shorter syntax, you can use the "Limelight" inspired syntax:

Teacup::Stylesheet.new :main_menu do
  ready_to_play_button do
    backgroundColor UIColor.blackColor
    frame [[20, 300], [50, 20]]
  end
end

This creates the same style hashes as style :ready_to_play_button, so you have access to all the features that are available in the "traditional" syntax.

Using and re-using styles in a Stylesheet

  • Styles are be applied via stylename (style :label) or class (style UILabel)
  • Styles can extend other styles (style :big_button, extends: :button)
  • A stylesheet can import other stylesheets (import :app)
  • The special Appearance stylesheet can be used to apply styles to UIAppearance (Teacup::Appearance.new)

Let's look at each in turn.

Style via Stylename

This is the most common way to apply a style.

class MainController < UIViewController

  stylesheet :main  # <= assigns the stylesheet named :main to this controller

  def teacup_layout
    subview(UILabel, :h1)  # <= :h1 is the stylename
  end

end

Teacup::Stylesheet.new :main do  # <= stylesheet name

  style :h1,  # <= style name
    font: UIFont.systemFontOfSize(20)  # <= and this style is applied

end

When the stylesheet is applied (at the end of the layout block, when all the views have been added), its font property will be assigned the value UIFont.systemFontOfSize(20).

But we didn't assign any text!

We can tackle this a couple ways. You can apply "last-minute" styles in the layout and subview methods:

def teacup_layout
  subview(UILabel, :h1,
    # the `subview` and `layout` methods can apply styles
    text: "Omg, it's full of stars"
    )
end

In this case though we just have static text, so you can assign the text using the stylesheet:

Teacup::Stylesheet.new :main do

  style :h1,
    font: UIFont.systemFontOfSize(20)

  style :main_header,
    text: "Omg, it's full of stars",
    font: UIFont.systemFontOfSize(20)

end

Extending Styles

Not very DRY though is it!? We have to use a new style (:main_header) because not all our labels say "OMG", but we want to use our font from the :h1 style. We can tell the :main_header style that it extends the :h1 style:

def teacup_layout
  subview(UILabel, :main_header)
end

Teacup::Stylesheet.new :main do

  style :h1,
    font: UIFont.systemFontOfSize(20)

  style :main_header, extends: :h1,
    text: "Omg, it's full of stars"

end

A common style when writing stylesheets is to use variables to store settings you want to re-use.

Teacup::Stylesheet.new :main do
  h1_font = UIFont.systemFontOfSize(20)

  style :h1,
    font: h1_font
  style :main_header, extends: :h1,
    text: "Omg, it's full of stars"
end

And you're not limited to one class that you can extend, it accepts an array

Teacup::Stylesheet.new :main do
  h1_font = UIFont.systemFontOfSize(20)

  style :h1,
    font: h1_font

  style :label,
    textColor: UIColor.black

  style :main_header, extends: [:h1, :label],
    text: "Omg, it's full of stars"
end

Style via View Class

If you need to apply styles to all instances of a UIView/NSView subclass, you can do so by applying styles to a class name instead of a symbol. This feature is handy at times when you might otherwise use UIAppearance (which teacup also supports!).

Teacup::Stylesheet.new :app do

  style UILabel,
    font: UIFont.systemFontOfSize(20)

  style UITableView,
    backgroundColor: UIColor.blackColor

end

Importing stylesheets

We've touched on the ability to write styles, extend styles, and apply styles to a class. Now we can introduce another feature that is even more useful for applying styles to your entire app: import a stylesheet.

When you import a stylesheet, you receive all of its styles and you gain access to its instance variables. This way you can define colors and margins and such in a "parent" stylesheet.

Teacup::Stylesheet.new :app do

  @header_color = UIColor.colorWithRed(7/255.0, green:16/255.0, blue:95/255.0, alpha: 1)
  @background_color = UIColor.colorWithRed(216/255.0, green:226/255.0, blue:189/255.0, alpha: 1)

  style :root,
    backgroundColor: @background_color

  style :header,
    textColor: @header_color

end

Teacup::Stylesheet.new :main do
  import :app

  style :subheader, extends: :header  # <= the :header style is imported from the :app stylesheet

  style :button,
    titleColor: @header_color  # <= @header_color is imported, too
end

Style via UIAppearance

iOS only

And lastly, the UIAppearance protocol is supported by creating an instance of Teacup::Appearance. There is debatable benefit to using UIAppearance, because it will apply styles to views that are outside your control, like the camera/image pickers and email/message controllers.

But, it does come in handy sometimes... so here it is!

Teacup::Appearance.new do

  # UINavigationBar.appearance.setBarTintColor(UIColor.blackColor)
  style UINavigationBar,
    barTintColor: UIColor.blackColor,
    titleTextAttributes: {
      UITextAttributeFont => UIFont.fontWithName('Trebuchet MS', size:24),
      UITextAttributeTextShadowColor => UIColor.colorWithWhite(0.0, alpha:0.4),
      UITextAttributeTextColor => UIColor.whiteColor
    }

  # UINavigationBar.appearanceWhenContainedIn(UINavigationBar, nil).setColor(UIColor.blackColor)
  style UIBarButtonItem, when_contained_in: UINavigationBar,
    tintColor: UIColor.blackColor

  # UINavigationBar.appearanceWhenContainedIn(UIToolbar, UIPopoverController, nil).setColor(UIColor.blackColor)
  style UIBarButtonItem, when_contained_in: [UIToolbar, UIPopoverController],
    tintColor: UIColor.blackColor

end

In your AppDelegate you need to call Teacup::Appearance.apply. It will get called automatically using the UIApplicationDidFinishLaunchingNotification, but that notification is triggered after the method AppDelegate#didFinishLaunching(withOptions:) is called.

app_delegate.rb
class AppDelegate
  def didFinishLaunching(application, withOptions:options)
    Teacup::Appearance.apply

    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
    ctlr = MainController.new
    @window.rootViewController = UINavigationController.alloc.initWithRootController(ctlr)
    @window.makeKeyAndVisible

    true
  end

end

That block is called using the UIApplicationDidFinishLaunchingNotification, but that notification is not called until the end of the application(application,didFinishLaunchingWithOptions:launchOptions) method. This is sometimes after your views have been created, and so they will not be styled. If that is the case, call Teacup::Appearance.apply before creating your rootViewController.

Now go use Teacup!

You have enough information right now to go play with Teacup. Check out the example apps, write your own, whatever. But read on to hear about why Teacup is more than just writing layouts and applying styles.

You should also be sure to read the Misc notes section at the bottom. These aren't very well organized, but the information is important. You can learn about features any time, but learning about the "gotcha"s sooner rather than later could save you a lot of time!

Teacup as a utility

When you are prototyping an app it is useful to bang out a bunch of code quickly, and here are some ways that Teacup might help.

You can use all the methods above without having to rely on the entirety of Teacup's layout and stylesheet systems. By that I mean any time you are creating a view hierarchy don't be shy about using Teacup to do it.

UIView and NSView have the style method, which can be used to group a bunch of customizations anywhere in your code. You don't have to pull out a stylesheet to do it.

# Custom Navigation Title created and styled by Teacup
self.navigationItem.titleView = layout(UILabel,
  text:'Title',
  font: UIFont.systemFontOfSize(12),
  )

# Customize contentView in a UITableViewCell dataSource method
def tableView(table_view, cellForRowAtIndexPath:index_path)
  cell_identifier = 'MyController - cell'
  cell = table_view.dequeueReusableCellWithIdentifier(cell_identifier)

  unless cell
    cell = UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault,
                        reuseIdentifier: cell_identifier)
    layout(cell.contentView) do
      subview(UIImageView, :image)
    end
  end

  return cell
end

# Use the `style` method on a view to apply your styling. This is a one-shot
# styling.
@label.style(textColor: UIColor.blueColor, text: 'Blue Label')

UITableViews

Teacup is designed to be used in coordination with the controller life cycle, but there are other life cycles that need to be considered as well. UITableViews maintain a "queue" of cells that can be reused, and they need to be restyled when the cell is created and re-used.

The solution is to apply the styles and layout constraints inside the tableView:willDisplayCell:forRowAtIndexPath: delegate method. In your delegate, if you include the Teacup::TableViewDelegate module, you'll get this behavior for free, and if you override this method, you can call super to have the Teacup method run.

class TableViewController < UITableViewController
  include Teacup::TableViewDelegate

  stylesheet :table

  def tableView(table_view, cellForRowAtIndexPath:index_path)
    cell = table_view.dequeueReusableCellWithIdentifier('cell id')

    layout(cell.contentView, :root) do
      cell.title_label = subview(UILabel, :title_label, :text => "title #{index_path.row}")
      cell.details_label = subview(UILabel, :details_label, :text => "details #{index_path.row}")
      cell.other_label = subview(UILabel, :other_label, :text => "other #{index_path.row}")
    end

    return cell
  end

  # This method is implemented by the Teacup::TableViewDelegate.  If you need
  # to implement it, be sure to call super.
  # def tableView(tableView, willDisplayCell:cell, forRowAtIndexPath:indexPath)
  #   super
  # end
end

Constraints and styles get applied before the view appears, even if the cell is reused later.

More Teacup features

There are a few (OK, a bunch) more features that Teacup provides that deserve discussion:

  • Styling View Properties
  • Orientation Styles
  • View Class Additions
  • Style Handlers
  • Frame Calculations
  • Auto-Layout & Motion-Layout
  • Stylesheet Extensions

Styling View Properties

Styling a UIView is fun, but a UIView is often composed of many objects, like the layer, or maybe an imageView or textLabel and so on. You can style those, too!

# UITableViewCells have a contentView, a backgroundView, imageView, textLabel,
# detailTextLabel, and a layer! whew!
style :tablecell,
  layer: {  # style the layer!
    shadowRadius: 3
  },
  backgroundView: {  # style the background!
    backgroundColor: UIColor.blackColor
  },
  imageView: {  # style the imageView!
    contentMode: UIViewContentModeScaleAspectFill
  }

Orientation Styles

iOS only

There's more to stylesheets than just translating UIView setters. Teacup can also apply orientation-specific styles. These are applied when the view is created (using the current device orientation) and when a rotation occurs.

Teacup::Stylesheet.new :main do

  # this label hides when the orientation is landscape (left or right)
  style :label,
    landscape: {
      hidden: true
    },
    portrait: {
      hidden: false
    }

end

Combine these styles with Frame Calculations to have you view frame recalculated automatically.

Animation additions

We've already seen the Teacup related properties:

  • stylename, the primary style name
  • style_classes, secondary style names
  • style, apply styles directly

Each of these has a corresponding method that you can use to facilitate animations.

  • animate_to_stylename(stylename)
  • animate_to_styles(style_classes)
  • animate_to_style(properties)

On OS X you have to use the view.animator property to perform animations. This is supported, but it's kind of "hacky".

Style Handlers

This feature is used extensively by sweettea to make a more intuitive stylesheet DSL

Teacup is, by itself, pretty useful, but it really does little more than map Hash keys to UIView setters. That's great, because it keeps the system easy to understand. But there are some methods in UIKit that take more than one argument, or could benefit from some shorthands.

This is where Teacup's style handlers come in. They are matched against a UIView subclass and one or more stylenames, and they are used to apply that style when you use it in your stylesheet.

# this handler adds a `:title` handler to the UIButton class (and subclasses).
Teacup.handler UIButton, :title do |target, title|
  target.setTitle(title, forState: UIControlStateNormal)
end

# ...
subview(UIButton,
  title: 'This is the title'  # <= this will end up being passed to the handler above
  )

layout(UINavigationItem,
  title: 'This is the title'  # <= but this will not!  the handler above is restricted to UIButton subclasses
  )

Other built-in handlers are defined in z_handlers.rb. Another useful one is the ability to make view the same size as its parent, and located at the origin.

style :container,
  frame: :full  # => [[0, 0], superview.frame.size]

Frame Calculations

These are super cool, just don't forget your autoresizingMasks

When positioning views you will often have situations where you want to have a view centered, or 8 pixels to the right of center, or full width/height. All of these relationships can be described using the Teacup.calculate method, which is called automatically in any method that modifies the frame or center.

frame, origin, size
top/y, left/x, right, bottom, width, height
center_x/middle_x, center_y/middle_y, center
Teacup::Stylesheet.new :main do

  style :button,
    left: 8, top: 8,  # easy enough!
    width: '100% - 16',  # woah!  (O_o)
    height: 22

  style :top_half,
    frame: [[0, 0], ['100%', '50%']]
  style :bottom_half,
    frame: [[0, '50%'], ['100%', '50%']]

end

When this code executes, the string '100% - 16' is translated into the formula 1.00 * target.superview.frame.size.width - 16. If the property is related to the height or y-position, it will be calculated based on the height.

The frame calculations must be a string of the form /[0-9]+% [+-] [0-9]+/. If you need more "math-y-ness" than that, you can construct strings using interpolation.

margin = 8

style :button,
  left: margin, top: margin,
  width: "100% - #{margin * 2}",
  height: 22

# just for fun, let's see what it would take to add a margin between these two views.
style :top_half,
  frame: [[0, 0], ['100%', "50% - #{margin / 2}"]]
style :bottom_half,
  frame: [[0, "50% + #{margin / 2}"], ['100%', "50% - #{margin / 2}"]]

One more example: The real power of the frame calculations comes when you remember to set springs and struts. You can have a view "pinned" to the bottom if you remember to set the autoresizingMask.

Teacup::Stylesheet.new :main do

  style :button,
    # fixed width / height
    height: 22, width: 200,
    center_x: '50%',
    top: '100% - 30',  # includes an 8px margin from the bottom
    autoresizingMask: (UIViewAutoresizingFlexibleLeftMargin |
                       UIViewAutoresizingFlexibleRightMargin |
                       UIViewAutoresizingFlexibleTopMargin)
    # see the autoresizing extension below for an even better way to write this.
end

Auto-Layout

This is another much bigger topic than it is given space for here

Teacup includes an Auto-Layout constraint DSL that you can use in your stylesheets. These methods are added to the Stylesheet class, so unless you are in the context of a stylesheet, you will have to create your constraints in longhand (you can still use the Teacup::Constraint class to help you!).

I won't sugar-coat it: Auto-Layout is hard. Much harder than using frames and springs and struts. And honestly, I recommend you try using the Teacup.calculate features mentioned above, they will take you far.

But at the end of the day, once you really understand the auto-layout system that Apple released in iOS 6, you can build your UIs to be responsive to different devices, orientations, and sizes. UIs built with auto-layout not usually need to adjust anything during a rotation. The constraints take care of it all. It's impressive.

Here's a quick example that creates this shape. The edges are bound to the superview's frame.

+-----+----------------+
|     |                |
|  A  |     B          |
|     |          +-----| <\
|     |          |  C  |  |_ 50% of B's height, minus 10 pixels
+-----+----------+-----+ </
^--+--^          ^--+--^
   |_fixed (100)    |_fixed (100)
Teacup::Stylesheet.new do
  style :A,
    constraints: [
      # these first three are all fixed, so super easy
      constrain_left(0),
      constrain_width(100),
      constrain_top(0),
      # here we go, here's a real constraint
      constrain(:bottom).equals(:superview, :bottom),
    ]

  style :B,
    constraints: [
      # B.left == A.right
      constrain(:left).equals(:A, :right),
      # B.height == A.height
      constrain(:height).equals(:A, :height),
      constrain(:right).equals(:superview, :right),
    ]

  style :C,  # <= this looks like a very grumpy style :C
    constraints: [
      constrain_width(100),
      # pin to bottom-right corner
      constrain(:right).equals(:superview, :right),
      constrain(:bottom).equals(:superview, :bottom),
      # 50% B.height - 10
      constrain(:height).equals(:B, :height).times(0.5).minus(10),
    ]

end

Writing views this way will either make your brain hurt, or make the math-nerd in you chuckle with glee. In this example you could go completely with just frame calculation formulas and springs and struts. Your frame code would still be cluttered, just cluttered in a different way.

If you need to reset the list of constraints managed by Teacup, you can call reset_constraints before you add the new styles to a UIView. This can be useful when you need to define a new set of layout constraints for a dynamic set of views.

This works on OS X and iOS, and you don't have to go changing the idea of "top" and "bottom" even though OS X uses reversed frames.

Motion-Layout

If you are using Nick Quaranto's motion-layout gem, you can use it from within any class that includes Teacup::Layout. Then benefit is that the Teacup stylenames assigned to your views will be used in the dictionary that the ASCII-based system relies on.

def teacup_layout
  subview(UIView, :view_a)
  subview(UIView, :view_b)
  subview(UIView, :view_c)

  # if you need to apply these to a different view, or if you want to assign
  # different names to use in the ASCII strings
  # auto(layout_view=self.view, layout_subviews={}, &layout_block)

  auto do
    metrics 'margin' => 20
    vertical "|-[view_a]-margin-[view_b]-margin-[view_c]-|"
    horizontal "|-margin-[view_a]-margin-|"
    horizontal "|-margin-[view_b]-margin-|"
    horizontal "|-margin-[view_c]-margin-|"
  end
end

Stylesheet extensions

Auto-Layout is just one Stylesheet extension, there are a few others. And if you want to write your own, just open up the Teacup::Stylesheet class and start adding methods.

Autoresizing Masks

If you've used the SugarCube uiautoresizingmask methods, you'll recognize these. They are handy, and hopefully intuitive, shorthands for common springs and struts.

In previous versions of Teacup these were available without needing the autoresize prefix. The old methods are still available, but deprecated.

# keeps the width and height in proportion to the parent view
style :container,
  autoresizingMask: autoresize.flexible_width | autoresize.flexible_height

# the same, but using block syntax
style :container,
  autoresizingMask: autoresize { flexible_width | flexible_height }

# the same again, using a shorthand
style :container,
  autoresizingMask: autoresize.fill

The autoresize methods are grouped into four categories: flexible, fill, fixed, and float. The flexible methods correspond 1::1 with the UIViewAutoresizing* constants.

The fill methods (fill,fill_top,fill_bottom,fill_left,fill_right) will stretch the width, or height, or both. The location specifies where the view is pinned, so fill_top will stretch the width and bottom margin, but keep it the same distance from the top (not necessarily at the top, but a fixed distance). fill_right will pin it to the right side, stretch the height, and have a flexible left margin.

The fixed methods pin the view to one of nine locations:

top_left    |  top_middle   |    top_right
------------+---------------+-------------
middle_left |     middle    | middle_right
------------+---------------+-------------
bottom_left | bottom_middle | bottom_right

e.g. fixed_top_left, fixed_middle, fixed_bottom_right

The float methods fill in the last gap, when you don't want your view pinned to any corner, and you don't want it to change size.

# incidentally:
float_horizontal | float_vertical == fixed_middle

Device detection

iOS only

Because the stylesheets are defined in a block, you can perform tests for device and screen size before setting styles. For instance, on an ipad you might want to have a larger margin than on the iphone.

The Stylesheet device methods will help you create these conditions:

Teacup::Stylesheet.new do
  if device_is? iPhone
    margin = 8
  elsif device_is? iPad
    margin = 20
  end

  style :container,
    frame: [[margin, margin], ["100% - #{margin * 2}", "100% * #{margin * 2}"]]
end

Multiple calls to style will add those styles, not replace. So this code works just fine:

Teacup::Stylesheet.new do
  style :logo,
    origin: [0, 0]

  if device_is? iPhone
    style :logo, image: UIImage.imageNamed "small logo"
  elsif device_is? iPad
    style :logo, image: UIImage.imageNamed "big logo"
  end
end

Rotation helpers

iOS only

Because you can animate changes to the stylename or style_classes, you can make it pretty easy to apply rotation effects to a UIView or CALayer. The style_classes property is especially useful for this purpose.

style :container,
  frame: :full

# UIView transforms

style :rotated,
  transform: transform_view.rotate(pi / 2)  # pi and transform_view are methods on Stylesheet

style :not_rotated,
  transform: transform_view.rotate(0)

# CALayer transforms

style :rotated,
  layer: { transform: transform_layer.rotate(pi / 2) }

style :not_rotated,
  layer: { transform: transform_layer.rotate(0) }

These work even better when used with the geomotion methods that extend CGAffineTransform and CATransform3D.

style :goofy,
  transform: CGAffineTransform.rotate(pi / 2).translate(100, 0).scale(2)
style :regular,
  transform: CGAffineTransform.identity

# CALayer uses CATransform3D objects
style :regular,
  layer: {
    transform: CATransform3D.rotate(pi / 2)
  }

Showdown

As a recap, here is a translation of traditional Cocoa code done using Teacup.

No cool tricks here, just some plain ol' Cocoa.

#
# Traditional Cocoa
#
class SomeController < UIViewController

  def viewDidLoad
    @field = UITextField.new
    @field.frame = [[10, 10], [200, 50]]
    @field.textColor = UIColor.redColor
    view.addSubview(@field)

    @search = UITextField.new
    @search.frame = [[10, 70], [200, 50]]
    @search.placeholder = 'Find something...'
    @search.textColor = UIColor.redColor
    view.addSubview(@search)
  end

  # perform the frame changes depending on orientation
  def willAnimateRotationToInterfaceOrientation(orientation, duration:duration)
    case orientation
    when UIInterfaceOrientationLandscapeLeft, UIInterfaceOrientationLandscapeRight
      @field.frame = [[10, 10], [360, 50]]
      @search.frame = [[10, 70], [360, 50]]
    else
      @field.frame = [[10, 10], [200, 50]]
      @search.frame = [[10, 70], [200, 50]]
    end
  end

end

#
# Teacup
#

class SomeController < UIViewController

  stylesheet :some_view

  def teacup_layout
    root(:root)
    subview(UITextField, :field)
    @search = subview(UITextField, :search)
  end

end

Teacup::Stylesheet.new(:some_view) do

  style :root,       # enable landscape rotation (otherwise only portrait is enabled)
    landscape: true  # this must be on the root-view, to indicate that this view is
                     # capable of handling rotations

  style :field,
    left:   10,
    top:    10,
    width:  200,
    height: 50,
    landscape: {
      width: 360  # make it wide in landscape view
    }

  style :search, extends: :field,
    left: 10,
    top: 70,
    placeholder: 'Find something...'

  style UITextField,                # Defining styles based on view class instead
    textColor: UIColor.redColor     # of style name.

end

The Nitty Gritty

Regarding Style Precedence

You need to be careful when extending styles and using orientation styles because the precedence rules take some getting used to. The goal is that you can have all your style code in the stylesheets. But you also need to be able to animate your views, and rotating the device should not go reseting everything.

So here's what happens.

When your controller is loaded, viewDidLoad is called, and that's where Teacup creates the view hierarchy and applies the styles. It is at the end of the method that the styles are applied - not until all the views have been added. The current device orientation will be used so that orientation-specific styles will be applied.

Now Teacup goes quiet for a while. Your app chugs along... until the user rotates the device.

If you have orientation-specific styles, they will get applied. But the original styles (the "generic" styles) will not.

However, there's a way around that, too. If you call restyle! on a UIView, that will reapply all the original stylesheet styles - orientation and generic styles.

With me so far? Orientation styles are reapplied whenever the device is rotated. But generic styles are only applied in viewDidLoad and when restyle! is called explicitly.

How does the :extends property affect things?

If your stylesheet defines orientation-specific styles and "generic" styles, the orientation-specific styles win. But if you extend a style that has orientation-specific styles, your local generic styles will win.

The more "local" styles always win - and that applies to styles that you add using the subview/layout methods, too. The only time it doesn't really apply is if you apply styles using UIView#style or UIView#apply_stylename. Those are one-shot (they can get overwritten when restyle! is called).

There are also times when you either want (or must) override (or add to) the stylesheet styles. For instance, if you want to assign the delegate or dataSource properties, this cannot be done from a Stylesheet. But that's okay, because we have a chance to add these styles in the subview and layout methods.

def teacup_layout
  subview(UITableView, delegate: self)
end

Styles applied here are one-shot. It is the exact same as assigning the stylename and style_classes and then calling style. Because the stylesheet is not necessarily applied immediately, these styles could be overwritten before they take effect.

def teacup_layout
  table_view = subview(UITableView, :tableview, delegate: self,
    font: UIFont.boldSystemFontOfSize(10)  # the stylesheet could override this during rotation
    )
end

def layoutDidLoad
  table_view.apply_stylename(:tableview_init)  # this will only get applied once
end

The idea here is that the closer the style setting is to where the view is instantiated, the higher the precedence.

More examples!

class MyController < UIViewController
  stylesheet :my_sheet
  def teacup_layout
    subview(UILabel, :label, text: 'overrides')
  end
end
Teacup::Stylesheet.new :my_sheet do
  style :generic_label,
    text: 'portrait',
    # these get applied initially, but after being rotated they will not get
    # applied again
    font: UIFont.boldSystemFontOfSize(10),
    textColor: UIColor.grayColor,
    landscape: {
      font: UIFont.boldSystemFontOfSize(12),
      textColor: UIColor.whiteColor,
    }  # this style should add a `portrait` setting that restores the font and color

  style :label, extends: :generic_label,
    font: UIFont.systemFontOfSize(10),  # this will override all the font settings
end

Advanced Teacup Tricks

There are times when you might wish teacup "just worked", but please remember: Teacup is not a "blessed" framework built by Apple engineers. We have access to the same APIs that you do. That said, here are some use-cases where you can most definitely use teacup, but you'll need to do a little more leg work.

Trust your parent view - by using springs and struts

...not autolayout

It's been mentioned a few times in this document that Teacup will create & style views in the viewDidLoad method. That means that the superview property of the controller's view will, necessarily, not be set yet. viewDidLoad is called after the view is instantiated (in loadView), and it hasn't been added as a subview yet.

Auto-Layout is based on the relationship between two views - often a container and child view. It's an amazing system, but if that parent view isn't available, well, you're not gonna have much success.

In the case of a UIViewController your "container" is the self.view property, which by default has sensible springs setup so that it stretches to fill the superview. It's not until you go messing with the self.view property, or are not in the context of a UIViewController that things get hairy.

If this is the case, you should get some pretty obvious warning messages, something along the lines of Could not find :superview.

Including Teacup::Layout on arbitrary classes

I don't know about you, but I often write helper classes for tableviews that appear on many screens in an app. You should not shy away from adding teacup's Layout module to these helper classes.

If you are using your controller as your table view dataSource, the subview and layout methods continue to work as you expect them to. This is for the case when you are using a helper class as the dataSource and/or delegate.

class TableHelper
  include Teacup::TableViewDelegate
  include Teacup::Layout

  stylesheet :table_helper

  def tableView(table_view, cellForRowAtIndexPath:index_path)
    cell_identifier = 'MyController - cell'
    cell = table_view.dequeueReusableCellWithIdentifier(cell_identifier)

    unless cell
      cell = UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault
                          reuseIdentifier: cell_identifier)

      layout(cell.contentView) do
        subview(UIImageView, :image)
      end
      # cell.contentView and all child classes will "inherit" the :table_helper stylesheet
    end

    return cell
  end

end

Calculating values using lambda

All styles can accept a proc, and before the style is applied the proc is called and you can calculate a value at runtime. There are times when this is simply convenient, and there are times when it is necessary.

Convenient: when a value is based on some other view's value

# calculate 'backgroundColor' based on another view
style :some_view,
  backgroundColor: ->(view) { view.backgroundColor = view.superview.someOtherColor }

Required: when you need to instantiate an object, like a view

# calculate 'backgroundColor' based on another view
style :some_view,
  # there's no "only run once" idiom in teacup, so we need to make sure not to
  # create this view *every* time :some_view is restyled.
  leftView: ->(view) { view.leftView || UILabel.alloc.initWithFrame(...) }

SugarCube + Teacup = Sweettea

SugarCube was born of a desire to make Teacup stylesheets more readable, less cluttered with Apple's verbose method names and constants. Sweettea takes this a step further, by implementing a wealth of Teacup handlers that translate Symbols to constants and provide useful shorthands.

style :button,
  normal: { image: 'button-white' },
  highlighted: { image: 'button-white-pressed' },
  title: 'Submit',
  shadow: {
    opacity: 0.5,
    radius: 3,
    offset: [3, 3],
    color: :black,
  },
  font: 'Comic Sans'

style :label,
  font: :bold,
  alignment: :center,
  color: :slateblue

Sweettea also offers some convenient styles that you can extend in your base class. You might want to either specify the Sweettea version you are using in your Gemfile, or copy the stylesheet so that changes to Sweettea don't affect your project. Once that projet is at 1.0 you can rely on the styles not changing.

# buttons! :tan_button, :black_button, :green_button, :orange_button,
# :blue_button, :white_button, :gray_button
style :submit_button, extends: :white_button

# label sets more sensible defaults than a "raw" UILabel (like clear background)
style :header, extends: :label

# inputs!  these are not styled, they just setup keyboard and autocomplete
# settings
# :name_input, :ascii_input, :email_input, :url_input, :number_input,
# :phone_input, :secure_input
style :login_input, extends: :email_input
style :password_input, extends: :secure_input

Misc notes

Multiple calls to style with the same stylename combines styles, it doesn't replace the styles.


Styles are not necessarily applied immediately. They are applied at the end of the outermost layout/subview method, including the UIViewController##layout block. If you call stylename= or stylesheet= outside a layout/subview block, your view will be restyled immediately.


Restyling a view calls restyle! on all child views, all the way down the tree. Much care has been taken to call this method sparingly within Teacup.


Any styles that you apply in a layout/subview method are not retained, they are applied immediately, and so the stylesheet can (and usually do) override those styles if there is a conflict. Only styles stored in a stylesheet are reapplied (during rotation or in restyle!).


Stylesheets should not be modified once they are created - they cache styles by name (it is a per orientation cache).


You can add and remove a style_class using add_style_class and remove_style_class, which will call restyle! for you if style_classes array was changed.


If you need to do frame calculations outside of the stylesheet code, you should do so in the layoutDidLoad method. This is not necessary, though! It is usually cleaner to do the frame calculations in stylesheets, either using geomotion, frame calculations, or auto-layout.


Within a subview/layout block views are added to the last object in Layout#superview_chain. Views are pushed and popped from this array in the Layout#layout method, starting with the top_level_view. If you include Teacup::Layout on your own class, you do not have to implement top_level_view unless you want to use the subview method to add classes to a "default" target.


When UIView goes looking for its stylesheet it does so by going up the responder chain. That means that if you define the stylesheet on a parent view or controller, all the child views will use that same stylesheet by default. It also means you can assign a stylesheet to a child view without worrying what the parent view's stylesheet is.

Caveat! If you implement a class that includes Teacup::Layout, you can assign it a stylesheet. That stylesheet will be used by views created using layout or subview even though your class is probably not part of the responder chain. Saying that UIView inherits its stylesheet from the responder chain is not accurate; it actually uses teacup_responder, which defaults to nextResponder, but it is assigned to whatever object calls the layout method on the view.


If you use Teacup::Appearance but it is not styling the first screen of your app (but, strangely, does style all other screens), try calling Teacup::Appearance.apply before creating you create the rootViewController (in your AppDelegate)..


The Dummy

If you get an error that looks like this:

Objective-C stub for message `setHidesWhenStopped:' type `v@:c' not
precompiled. Make sure you properly link with the framework or library that
defines this message.

You probably need to add your method to dummy.rb. This is a compiler issue, nothing we can do about it except build up a huge dummy.rb file that has just about every method that you would want to style. There is a [dummy.rb file for iOS][], and one for OS X.

If you need to add this method to your project, please give back to the community by forking teacup and adding this method to the dummy.rb file. It's easy! Create a subclass, define a method called dummy, and call the "not precompiled" message inside it. That will trigger the compiler to include this method signature.

For instance, lets say you are styling a UIPickerView and you get the error:

Objective-C stub for message `setShowsSelectionIndicator:' type ...

You would open up dummy.rb and add the following code:

class DummyPickerView < UIPickerView
private
  def dummy
    setShowsSelectionIndicator(nil)
  end
end

Recompile your project, and you should be good to go!

Teacup is a Community Project!

Teacup was born out of the #rubymotion irc chatroom in the early days of RubyMotion. Its design, direction, and priorities are all up for discussion!

I'm Colin T.A. Gray, the maintainer of the Teacup project. I hope this tool helps you build great apps!

teacup's People

Contributors

alexrothenberg avatar ashish-sjc avatar beakr avatar blaix avatar brandondrew avatar colinta avatar conradirwin avatar farcaller avatar fcheung avatar foeken avatar gantman avatar jamonholmgren avatar jdewind avatar jsteinberg avatar jvonkluck avatar kastiglione avatar markvillacampa avatar miry avatar neiltron avatar padi avatar riklomas avatar robertlowe avatar rweald avatar ryansobol avatar smtm avatar spllr avatar sxross avatar thevole avatar voke avatar wink avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

teacup's Issues

Feature Request Auto Layout Quarters

I'd like Quarter positions in Auto layout. Tried to do a quick fork, but I see you're directly correlating attributes so new concepts have no place.

so center_x correlates to NSLayoutAttributeCenterX

I think it would be clean/clear/idiomatic for there to be quarters and perhaps eventually a grid system for AutoLayout. I added the attribute, but it fails since it's unsupported directly, it bombs out constraint.rb:234

I have no problem adding a .times(0.5) but I think aligning it with the quarter1_x or some other predefined grid logic, might be advantageous and more legible. I'm more than willing to help, but I'm at a loss as to how you'd want to add these (non-apple) supported alignments, or even if they are something you'd want.

Handling UISwitch from Teacup Styles

Trying to set UISwitch default to on. layout code is:

@switch = subview(UISwitch, :custom_switch)

in viewDidLoad I can do this with

@switch.on = true

but removing that and trying to do it in Teacup with

style :custom_switch,
  on: true

results in the following error:
Objective-C stub for message setOn:' typev@:c' not precompiled. Make sure you properly link with the framework or library that defines this message.

iOS7 Beta3 crash

Just upgraded to iOS7 (beta 3) and my application was crashing on startup. After many hours, I have tracked the issue to be a problem with teacup.

Here is my code:

######Gemfile######
source 'https://rubygems.org'

gem 'rake', '10.0.0'
gem 'teacup', '2.1.0'

######Rakefile######
# -*- coding: utf-8 -*-
$:.unshift("/Library/RubyMotion/lib")
require 'motion/project/template/ios'
require 'bundler'

Bundler.require

Motion::Project::App.setup do |app|
  # Use `rake config' to see complete project settings.
  app.name = 'teacupboy'
end

######App Delegate######
class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
    mine = TapController.alloc.init
    @window.rootViewController =    UINavigationController.alloc.initWithRootViewController(mine)
    true
  end
end

######TapController.rb######
class TapController < UIViewController
  def viewDidLoad
    super

    self.view.backgroundColor = UIColor.redColor
  end
end

######Stack######
Jul  8 23:56:26 Steves-iPhone teacupboy[1064] <Warning>: undefined method `each' for nil:NilClass (NoMethodError)
Jul  8 23:56:26 Steves-iPhone teacupboy[1064] <Error>: *** Terminating app due to uncaught exception 'NoMethodError', reason: 'undefined method `each' for nil:NilClass (NoMethodError)
    '
    *** First throw call stack:

Addition to the 10 second primer

For all the n00bs like myself out there, it'd be awesome if you could mention adding the following line to your Rakefile:

app.files += Dir.glob(File.join(app.project_dir, 'styles/**/*.rb'))

can I access variable from the controller within the stylesheet ?

Hi

if I do in the controller :

myTextField=NSTextField.new
myTextField.title="some long text"
myTextField.sizeToFit

@myview=subview(NSView, :myView) do
@myTextField=subview(myTextField, :styleTextField)
end

In the stylesheet, is it possible to get
@myTextField.frame.size.height, so as to be able to set the height of :myView ?

I want to use subviews to work with Constraints.

Hope the question makes sense and you can look into it

Style undefined

I'm getting an exception when trying to call the style method like in the docs, thusly:

def viewDidLoad
view.addSubview(Teacup.style(:field, UITextField.new))
true
end

undefined method `style' for Teacup:Class (NoMethodError)

Has anyone seen this?

Proposal for special class-specific setters.

I've mentioned this before, want to bring it up again.

Easiest example is UIButton. Setting title: calls setTitle(value, forState: UIControlStateNormal). But what about setImage(value, forState: UIControlStateNormal) and setImage(value, forState: UIControlStateHighlighted)?

I propose that we fix this by adding a way to attach procs to labels, and those become the new "setter" for that property.

UIButton.apply :title { |view, value|
  view.setTitle(value, forState: UIControlStateNormal)
}

style :my_button,
  title: 'Click me'

This would have the side effect (benefit?) that you could create your own shorthands, and we wouldn't have to add them to core teacup (which would just continue to grow - 😒)

MyView.apply :hidden { |view, is_hidden|
  self.layer.opacity = is_hidden ? 0 : 1
}

uninitialized constant CGRect (NameError) when building for OSX

I get a NameError when trying to use teacup in OSX.

With the simple-osx sample app:
When I run rake I get the following output

2013-05-26 23:10:04.944 teacup-osx[96600:707] Could not connect the action buttonPressed: to target of class NSApplication
2013-05-26 21:43:39.501 teacup-osx[15048:707] handler.rb:60:in `block in apply:': uninitialized constant CGRect (NameError)
        from handler.rb:54:in `apply:'
        from handler.rb:35:in `block in apply_hash:'
        from handler.rb:34:in `apply_hash:'
        from layout.rb:158:in `layout:'
        from layout.rb:231:in `subview:'
        from layout.rb:171:in `layout:'
        from teacup_controller.rb:101:in `teacupDidLoad'
        from ns_view_controller.rb:14:in `loadView'
        from layout.rb:171:in `layout:'
        from teacup_controller.rb:101:in `teacupDidLoad'
        from ns_window_controller.rb:6:in `windowDidLoad'
        from app_delegate.rb:10:in `buildWindow'
        from app_delegate.rb:5:in `applicationDidFinishLaunching:'

Frame calculations and auto-layout with OSX

Hi guys

I'm struggling to position my views correctly with teacup 2.1.2 (OSX app). I understand that the coordinate system is inverted compared to iOS. Still I have a suspicion that some of the helper and layout methods are not working as expected.

My strategy so forth has been to place views with frame: and beg that OSX would arrange them nicely using springs and struts - autoresizingMask:. If I only use frame:, views are placed in correct coordinates. Using autoresizingMask appears to have no or random results.

This is frustrating, because it's often basic problems: How do you pin a bar to the top of a window? I would expect the following to work:

style :header,
    top: 0, 
    width: '100%',
    height: 50,
    autoresizingMask: autoresize.fill_top
end

But it doesn't, the view is at the bottom. Increasing the top: value moves the view upwards in the coordinate system.

For some views, I've had moderate success with constraints, however at some point the layout breaks and it's difficult to debug why.

What is the best layout technique for OSX?
And how much of Frame calculations, Auto layout and Autoresizing mask is supported?

Excuse me for the broad question, it's hard to be more concrete without loads of examples.

My conclusion has been that it is possible to position views only using frame:, but it's extremely cumbersome to calculate everything from the bottom of a window and the result is not very dynamic.

Misleading documentation bug in wiki page

In the Layouts section of the Teacup wiki page, there's an example layout (app/views/layouts.rb) that subclasses UIView. The preceding text also says:

You can extend from any UIView subclass, but the most common are containers like UIView and UITableCellView [sic]. 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.

I spent a few hours struggling to get this to work before discovering there's no built-in support for layouts in UIView or UITableViewCell. Layouts work great for subclasses of UIViewController, but the layout methods are completely absent from z_core_extensions/ui_view.rb.

So is the the documentation incorrect, or is the code incomplete? My own preference is that it would be nice to define layouts as a UIView and UITableViewCell (especially the latter) without adding that code to the controller.

Using the new "auto" feature

When do you call the new "auto" method?

I am trying to call it in the viewDidLoad method, i.e.

def viewDidLoad
super
auto do
horizontal "|-(>=1)-[logo(==262)]-(>=1)-|"
end
end

Getting a bad memory access error.

Thanks

specifying size with percentage

There are times where I need a subview to expand and fill the parent view, i.e. subview.frame = parent.bounds

I believe it would be convenient to have something like this in stylesheets:

  style :overlay,
    height: 100%,
    width: 100%,
    backgroundColor: UIColor.colorWithRed(0, green:0, blue:0, alpha:0.5)

where width: 100% means subview.frame.size.width == subview.superview.frame.size.width

any thoughts?

Calling systomFontOfSize on UIFont from Teacup::Stylesheet.new block

Can someone try and reproduce this bug?

If I set font: UIFont.systemFontOfSize(17) in a stylesheet, the simulator crashes.

If, instead, I move the instance_eval &block code in stylesheet.rb to the query method, it works fine.

def initialize(name=nil, &block)
  if name
    @name = name.to_sym
    Teacup::Stylesheet[@name] = self
  end

  # we just store the block for now, because some classes are not "ready"
  # for instance, calling `UIFont.systemFontOfSize()` will cause the
  # application to crash.  We will lazily-load this block in `query`, and
  # then set it to nil.
  @block = block
end

def query(stylename)
  if @block
    instance_eval &@block
    @block = nil
  end
  # ...

Very odd, and I don't like the fix, but if this is happening elsewhere, it'll have to stay in some form. O_o?

Orientation styles doesn't work

I can't get orientation styles to work when the iphone re-orientates. They work well enough when initializing the view, but not when the view re-orientates.

style :label,
    landscape: {
      hidden: true
    },
    portrait: {
      hidden: false
    }

I'm handling orientation manually btw.

Any ideas of why this might happen?

[REQ] Loaded View Symbol

Caution

This may already exist in some way, and I'm not seeing it.

Current Behaviour

During a layout block you can specify a symbol of the loading UIView for styling.

class RootController < UIViewController
  stylesheet :root_style

  layout :root do
    #...
  end
end

This is great. Though it tightly couples the stylesheet to that particular layout block.

Request

I'm requesting a level of abstraction between the two. A special Teacup keyword that identifies the loading view.

EXAMPLE

Teacup::Stylesheet.new :root_style do

  style Teacup::LoadingView,
    backgroundColor: 'background'.uicolor

end

Benefit 1: Styles that import could abstract away application wide styles on the view. Like the above background pattern.

Benefit 2: Would make Teacup play nicely with other forms of loading (Like ProMotion) where the layout block is inaccessible and attributes would have to manually be set.

EXAMPLE the will_appear code could be removed and placed in the abstracted stylesheet.

class HomeScreen < PM::Screen
  stylesheet :home_screen

    def will_appear
      #the following line could be eliminated!
      set_attributes self.view, stylename: :home_view
    end
end

Follow Up

Please let me know if you have questions/concerns with this philosophy. I'd be more than happy to help if you could point me in the right direction of how you'd see this behavior implemented.

Views not released after modal ViewController dismissed.

Hi!

I'm using Teacup for the first time and I noticed that in several situations my views are not released when the ViewController is.
For instance, when I dismiss a modal ViewController, all the views stay alive.

I made a sample app to illustrate the issue:
https://github.com/n-studio/TeacupMemoryLeak

There is a with-Teacup and a without-Teacup version. The without-Teacup version works perfectly, whereas in the with-Teacup version Instruments shows me still living UIViews.

Am I doing something wrong?

Using center_x with extended size does not work

If I set a size in one style and then extend it in another then the center_x frame calculation does not work.

For example:

  style :button,
    size: [90, 90],
    backgroundColor: UIColor.redColor

  style :my_button, extends: :button,
    top: 0,
    center_x: 80, # <-- This results in a frame of [[80, 0], [90, 90]] instead of [[35, 0], [90, 90]]
    title: 'My Button'

Backwards Constraints

I don't know if you did this on purpose or not but I feel like when I say I want something positioned 10pts above another element I'd expect to do it like this:

constrain_above(:another_element, 10)

as opposed to

constrain_above(:another_element, -10)

This happens with constrain_right and constrain_above.

I think it was a mistake because you don't have to use negatives either for constrain_to_right or constrain_to_left to have them work the way you'd expect.

I apologize if you meant to do it this way, it just seems a little off to me.

Teacup Instance Variable Inheritance Feature

Would be great if stylesheets that imported a base stylesheet automatically inherit their parent's instance variables.

e.g. uses sugarcube
File base.rb

Teacup::Stylesheet.new :base do
  @back_color = 0xc3a477.uicolor 
end

File settings.rb

Teacup::Stylesheet.new :settings do
  import :base

  # already has @back_color = 0xc3a477.uicolor 
end

`autorotateMask': undefined local variable or method `orientation'

this code from my controller

def supportedInterfaceOrientations
self.autorotateMask
end

iOS 6, teacup 2.1.9

full message
ui_view_controller.rb:59:in autorotateMask': undefined local variable or methodorientation' for #SignupController:0xb2ef300 (NameError)
from signup_controller.rb:19:in supportedInterfaceOrientations' from app_delegate.rb:12:inapplication:didFinishLaunchingWithOptions:'

[HELP] failed on set font/titleColor for UIButton

  style :clickme,
        origin: [10, 10], extends: :rounded_button,
        title: "Click Me!"

  style :rounded_button,
        width: 300,
        height: 50,
        #font: "Helvetica".uifont(20),
        #titleColor: "#FFFFFF".uicolor,
        backgroundColor: UIColor.tealColor,
        layer: {
            #shadowColor: "#c6c6c6".uicolor,
            shadowOpacity: 0.5,
            shadowOffset: CGSizeMake(2.0, 4.0),
            shadowRadius: 3.0,
            masksToBounds: false,
            cornerRadius: 8
        },
        clipsToBounds: true

image

  1. if i set font on, the shadow will disappear ... why?
  2. how to set titleColor、shadowPath accurately?something like the code in https://github.com/bennyguitar/Colours-for-iOS/blob/master/ColoursDemo/ColoursDemo/ViewController.m

thank you :)

Initialization troubles with teacup 1.2.9 and ruby 2.0.0

Updating to teacup 1.2.9 on RubyMotion 1.35, it's compiling and requiring but failing to initialize, even after "rake clean", simulator reset, clean checkout and build, etc. Downgrading to 1.2.8 gets it working again. Other gems in the project (bubble-wrap, motion-settings-bundle, motion-cocoapods) aren't having trouble and have been updated OK. Poked around for a bit but couldn't make sense of it. No special detect_dependencies settings in my Rakefile ... it's just doing whatever it does.

I couldn't come up with any good hypothesis as to why, even after reading the commits between 1.2.8 and 1.2.9.

2013-04-07 14:35:00.272 NFC Actions[66058:c07] uninitialized constant Teacup::Layout (NameError)
2013-04-07 14:35:00.275 NFC Actions[66058:c07] *** Terminating app due to uncaught exception 'NameError', reason: 'uninitialized constant Teacup::Layout (NameError)
'
*** First throw call stack:
(0x2864012 0x4bbe7e 0x232c14 0xe3f2 0xdc2d)
libc++abi.dylib: terminate called throwing an exception
((null))> *** simulator session ended with error: Error Domain=DTiPhoneSimulatorErrorDomain Code=1 "The simulated application quit." UserInfo=0x10012b720 {NSLocalizedDescription=The simulated application quit., DTiPhoneSimulatorUnderlyingErrorCodeKey=-1}
rake aborted!
Command failed with status (1): [DYLD_FRAMEWORK_PATH="/Applications/Xcode.a...]

method `responds_to?` returns `false` for `setReturnKeyType`

setReturnKeyType is a method on UITextField, so it should be possible to set it in a stylesheet.

But responds_to? returns false for this method... I've filed a ticket with rubymotion / Laurent, but is there anything we want to do about this?

One solution would be to detect "special" setters, similar to what we do for setting the title on a UIButton (it calls setTitle(forState:)).

{frame: :full} not using parent frame size

I have the following view structure:

  0: . UIWindow(#10881e70, [[0.0, 0.0], [320.0, 480.0]])
  1: `-- UILayoutContainerView(#86b1770, [[0.0, 0.0], [320.0, 480.0]])
  2:     +-- UITransitionView(#86b2790, [[0.0, 0.0], [320.0, 431.0]])
  3:     |   `-- UIViewControllerWrapperView(#10881740, [[0.0, 20.0], [320.0, 411.0]])
  4:     |       `-- UIView(#a3085f0, [[0.0, 0.0], [320.0, 411.0]])
  5:     |           +-- UIImageView(#a3089c0, [[0.0, 0.0], [320.0, 460.0]]) stylename: :home_image
  6:     |           `-- UILabel(#a312160, [[0.0, 0.0], [320.0, 460.0]], text: "Ze title") stylename: :title_label
  7:     `-- UITabBar(#86b1830, [[0.0, 431.0], [320.0, 49.0]])
  ...

And the following stylesheet:

Teacup::Stylesheet.new :home_style do
  style :home_image,
    frame: :full

  style :title_label,
    frame: :full
end

As you can see, the UIView has the correct measurements, it fits exactly between the navigation and the tab bar. The childviews are however not the same size, but are bigger.

I tried using:

  layout do
    @home_image = subview(UIImageView, :home_image)
    @title_label = subview(UILabel, :title_label)
  end

and also:

def viewDidLoad
  layout(self.view) do
    @home_image = subview(UIImageView, :home_image)
    @title_label = subview(UILabel, :title_label)
  end
end

But to no avail.

Is there something I am doing incorrectly, or is this a bug?

Final proposal code does not work

I'm trying the code from final proposal but I receive errors during build.

./app/styles/stylesheets.rb:12: syntax error, unexpected tIDENTIFIER, expecting '}'
frame: [[2, 2], [40, 40]],
^
./app/styles/stylesheets.rb:12: syntax error, unexpected ',', expecting keyword_end
./app/styles/stylesheets.rb:30: syntax error, unexpected tIDENTIFIER, expecting '}'
textAlignment: :left.uialignment,
^
./app/styles/stylesheets.rb:31: syntax error, unexpected tLABEL, expecting '='
frame: [[44, 20], [138, 12]],
^
./app/styles/stylesheets.rb:31: syntax error, unexpected ',', expecting keyword_end
./app/styles/stylesheets.rb:35: syntax error, unexpected tIDENTIFIER, expecting '}'
textAlignment: :right,
^
./app/styles/stylesheets.rb:36: syntax error, unexpected ',', expecting keyword_end
./app/styles/stylesheets.rb:60: syntax error, unexpected ':', expecting '}'
frame: [[0, 74], [768, 960]],
^
./app/styles/stylesheets.rb:60: syntax error, unexpected ',', expecting '}'

rake aborted!

Command failed with status (): [/usr/bin/env VM_KERNEL_PATH="/Library/Ruby...]

Before i send a pull request...

...wanted to have some "documented" chatting on github about my no_const branch. It brings in tons of changes.

minor changes

  1. Adds a stylesheet method to TeacupViewController. You can set a "default" stylesheet name or instance.

    class MyViewController < TeacupViewController
     stylesheet :my_view
     layout do
       # ...
     end
    end
  2. Makes style name optional, which then defaults to :__root__, and sets the default stylename on the root view to :__root__ as well, so you can style the top-level-view by declaring style do ... end.

    Teacup::Stylesheet.new :my_view
     # this becomes the style on the root view
     style({
       backgroundColor: UIColor.blueColor,
     })
    end
  3. Stylename is also optional in the Teacup::Layout#layout method.

  4. Added an instance_eval-style declaration syntax, which we got rid of long ago, but it's growing on me again.

    # new style:
    style :label do
     textColor  UIColor.blueColor
     left   10
     top    10
     width  100
     height 20
     landscape do
       left   10
       top    30
       width  150
       height 20
     end
    end
    
    # old style:
    style :label, {
     textColor  UIColor.blueColor
     left:   10,
     top:    10,
     width:  100,
     height: 20,
     landscape: {
       left:   10,
       top:    30,
       width:  150,
       height: 20,
     }
    }

    This uses a Style class that inherits from Hash and accepts a block. method_missing assigns values to self[method_name]

major changes

  1. Instead of Teacup::Stylesheet::Ipad, the stylesheets are available using Teacup::Stylesheet[:stylesheet_name].

  2. The stylesheet setter accepts a :name, and later looks it up. This removes the dependency that Stylesheets be loaded before controllers. This code is in the Teacup::View#stylesheet getter.

  3. Adds per-orientation-based styling! This is a great feature, but a lot of things had to change to support it.

    Namely, adding shouldAutorotate... to UIViewController as a module doesn't work, the method doesn't get called. So I changed it to TeacupViewController. Given that this adds the class/instance methods layout and such I think this is a good thing, actually.

  4. UIView includes Teacup::View, and TeacupViewController includes Teacup::Layout. This is most controversial change; let's discuss!

Project description

Can I suggest updating the project description? "A DSL written by and for the rubymotion community." sounds a bit vague. The line from the README, "A community-driven DSL for creating user interfaces on the iphone" is better (although it's probably worth changing iPhone to iOS).

Handlers being called 8x for every object placed

I've noticed both in my own project and an isolated test that handlers are being called 8x in a stylesheet even if the call is made just once in the stylesheet and the object is added just once in the controller.

I created a gist with sample code to demonstrate the problem.

This is a problem because:

  1. Operations are being called unnecessarily and repeatedly (potential performance issue),
  2. Handlers were a way I had hoped I could use to call initialization and setup routines on my custom views (but this won't work if they are being called 8x). For example, I created a custom carousel view that requires the image names to load.

Here's the gist: https://gist.github.com/3961878

OSX samples won't run

I was experimenting with teacup but couldn't make anything work. So I decided to go back to basics and work with the samples, but none of the osx samples will run. iOS samples builds just fine.

Both osx samples crash at:

[...]
       Run ./build/MacOSX-10.8-Development/teacup-osx.app/Contents/MacOS/teacup-osx
2013-07-23 22:21:27.440 teacup-osx[51469:707] handler.rb:60:in `block in apply:': uninitialized constant NSRect (NameError)
    from handler.rb:54:in `apply:'
    from handler.rb:35:in `block in apply_hash:'
    from handler.rb:34:in `apply_hash:'
    from ns_view.rb:25:in `apply_style_properties:'
    from teacup_view.rb:304:in `style:'
    from ns_view.rb:29:in `style:'
    from layout.rb:161:in `layout:'
    from layout.rb:235:in `subview:'
    from layout.rb:175:in `layout:'
    from teacup_controller.rb:101:in `teacupDidLoad'
    from ns_view_controller.rb:14:in `loadView'
    from layout.rb:175:in `layout:'
    from teacup_controller.rb:101:in `teacupDidLoad'
    from ns_window_controller.rb:6:in `windowDidLoad'
    from app_delegate.rb:10:in `buildWindow'
    from app_delegate.rb:5:in `applicationDidFinishLaunching:'

Have I messed up my system somehow? And on a general note, how mature is teacup for osx purposes? I'm having serious issues getting my views in order.

teacup 2.1.1
rubymotion 2.4
teacup sample latest commit c9ea695

Contrain times with minus value;

Wants:

right to be proportion of 0.2 of the superview

constrain(:right).equals(:root, :right).times(-0.2)

this should cool right?

Tests with iOS7 DP3?

Every teacup project I run in iOS7 DP3 fails with the following.

** Terminating app due to uncaught exception 'NoMethodError', reason: 'teacup_view.rb:125:in `restyle!:': undefined method `each' for nil:NilClass (NoMethodError)
    from teacup_view.rb:125:in `restyle!:'
    from teacup_controller.rb:106:in `teacupDidLoad'
    from ui_view_controller.rb:6:in `viewDidLoad'
    from app_delegate.rb:6:in `application:didFinishLaunchingWithOptions:'

Gradient spec causes a crash

$ rake spec
     Build ./build/iPhoneSimulator-6.1-Development
   Compile ./spec/gradient_spec.rb
      Link ./build/iPhoneSimulator-6.1-Development/teacup_spec.app/teacup
    Create ./build/iPhoneSimulator-6.1-Development/teacup_spec.dSYM
  Simulate ./build/iPhoneSimulator-6.1-Development/teacup_spec.app
Any class that includes Teacup::Layout
  - should be able to have a stylesheet
  - should be able to layout and create views
  - should have a container and label that are styled

Gradient
  - should insert gradient layer when gradient style is set
$ rake spec debug=1
     Build ./build/iPhoneSimulator-6.1-Development
  Simulate ./build/iPhoneSimulator-6.1-Development/teacup_spec.app
/Users/watson/src/teacup/5806: No such file or directory
Attaching to process 5806.
Reading symbols for shared libraries . done
0x8fe71030 in __dyld__dyld_start ()
Function "rb_exc_raise" not defined.
Breakpoint 1 (rb_exc_raise) pending.
Function "malloc_error_break" not defined.
Breakpoint 2 (malloc_error_break) pending.
Reading symbols for shared libraries ....................................................................................................................... done
Breakpoint 1 at 0x124e96
Pending breakpoint 1 - "rb_exc_raise" resolved
Breakpoint 2 at 0x92fe4c97
Pending breakpoint 2 - "malloc_error_break" resolved
Reading symbols for shared libraries ......... done
Reading symbols for shared libraries . done
Reading symbols for shared libraries .. done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Any class that includes Teacup::Layout
  - should be able to have a stylesheet
  - should be able to layout and create views
  - should have a container and label that are styled

Gradient
  - should insert gradient layer when gradient style is set
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x24448918
0x017d358c in CGColorSpaceGetModel ()
(gdb) 

Undefined method `stylesheet` for UITableViewController

When trying to use stylesheet on a UITableViewController, an exception is thrown....

*** Terminating app due to uncaught exception 'NoMethodError', reason: 'undefined method `stylesheet' for MyController:Class (NoMethodError)
# my_controller.rb
class MyController < UITableViewController
  stylesheet :iphone
end

# style/iphone.rb
Teacup::Stylesheet.new :iphone do
  style :root,
    backgroundColor: UIColor.redColor,
    rowHeight: 100
end

Workaround for now:

class MyController < UITableViewController
  attr_accessor :stylesheet

  def viewDidLoad
    @stylesheet = :iphone
    super
  end
end

App failed to launch in time

I was using teacup 0.2.x successfully in an app and wanted to upgrade to 0.3.1 today. In the simulator everything works fine, but when I deploy the app to a device (iPhone 4, iPad 3) and launch it, it does nothing for 20 seconds and then crashes. I then tried your sample app (Hai) with the same result.

RubyMotion 1.19
Teacup 0.3.1
BubbleWrap 1.1.2
OSX 10.8.0

The device log looks like this:
(the interesting part being com.yourcompany.Hai failed to launch in time)

Incident Identifier: 9E2F0C99-6519-4AA1-9402-8F473CD08856
CrashReporter Key:   09bfa2fba1ed0d94c1cdcea3e8b04fc067f17874
Hardware Model:      iPad3,3
Process:         Hai [15665]
Path:            /var/mobile/Applications/B8FFC759-6BBC-4E30-9FD1-5746BD91BE64/Hai.app/Hai
Identifier:      Hai
Version:         ??? (???)
Code Type:       ARM (Native)
Parent Process:  launchd [1]

Date/Time:       2012-07-30 17:49:39.502 +0200
OS Version:      iPhone OS 5.1.1 (9B206)
Report Version:  104

Exception Type:  00000020
Exception Codes: 0x8badf00d
Highlighted Thread:  0

Application Specific Information:
com.yourcompany.Hai failed to launch in time

Elapsed total CPU time (seconds): 21.240 (user 21.240, system 0.000), 53% CPU 
Elapsed application CPU time (seconds): 19.855, 50% CPU

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0:
0   libunwind.dylib                 0x31ba2a72 unwind_phase2 + 54
1   libunwind.dylib                 0x31ba2c0e _Unwind_SjLj_Resume + 122
2   Hai                             0x000e215c 0x10000 + 860508
3   Hai                             0x000d7cfa 0x10000 + 818426
4   Hai                             0x00055064 0x10000 + 282724
5   Hai                             0x000dfdc6 0x10000 + 851398
6   Hai                             0x000134a8 0x10000 + 13480
7   Hai                             0x00013fe8 0x10000 + 16360
8   Hai                             0x000dfe00 0x10000 + 851456
9   Hai                             0x000134a8 0x10000 + 13480
10  Hai                             0x00013c4c 0x10000 + 15436
11  Hai                             0x000e2016 0x10000 + 860182
12  Hai                             0x000d7cfa 0x10000 + 818426
13  Hai                             0x0007e3ee 0x10000 + 451566
14  Hai                             0x0007bcbe 0x10000 + 441534
15  Hai                             0x000ae48a 0x10000 + 648330
16  Hai                             0x0007d4a4 0x10000 + 447652
17  Hai                             0x000dfdc6 0x10000 + 851398
18  Hai                             0x000134a8 0x10000 + 13480
19  Hai                             0x00013a24 0x10000 + 14884
20  Hai                             0x000dfdec 0x10000 + 851436
21  Hai                             0x00032c5c 0x10000 + 142428
22  Hai                             0x00034c64 0x10000 + 150628
23  Hai                             0x000dfdec 0x10000 + 851436
24  Hai                             0x00032c5c 0x10000 + 142428
25  Hai                             0x00033bec 0x10000 + 146412
26  Hai                             0x000dfdd8 0x10000 + 851416
27  Hai                             0x00032c5c 0x10000 + 142428
28  Hai                             0x000333c8 0x10000 + 144328
29  Hai                             0x000dfdd8 0x10000 + 851416
30  Hai                             0x0001aa68 0x10000 + 43624
31  Hai                             0x0001adb8 0x10000 + 44472
32  Hai                             0x000dfe00 0x10000 + 851456
33  Hai                             0x0001aa68 0x10000 + 43624
34  Hai                             0x0001b9b4 0x10000 + 47540
35  Hai                             0x000dfdec 0x10000 + 851436
36  Hai                             0x0004fc14 0x10000 + 261140
37  Hai                             0x000e1922 0x10000 + 858402
38  Hai                             0x000dfb74 0x10000 + 850804
39  Hai                             0x0001aa68 0x10000 + 43624
40  Hai                             0x0001b0b0 0x10000 + 45232
41  Hai                             0x000dfe00 0x10000 + 851456
42  Hai                             0x00037818 0x10000 + 161816
43  Hai                             0x00038a5c 0x10000 + 166492
44  Hai                             0x00038bec 0x10000 + 166892
45  UIKit                           0x34f3fc84 -[UIViewController view] + 160
46  Hai                             0x000e472c 0x10000 + 870188
47  Hai                             0x000e02b8 0x10000 + 852664
48  Hai                             0x00037818 0x10000 + 161816
49  Hai                             0x000391bc 0x10000 + 168380
50  Hai                             0x000dfdd8 0x10000 + 851416
51  Hai                             0x0004fec8 0x10000 + 261832
52  Hai                             0x0004ffa4 0x10000 + 262052
53  UIKit                           0x34f49d82 -[UIViewController _isSupportedInterfaceOrientation:] + 62
54  UIKit                           0x34f7b1d0 -[UIViewController _preferredInterfaceOrientationGivenCurrentOrientation:] + 132
55  UIKit                           0x350c2676 -[UIWindow setRootViewController:] + 78
56  Hai                             0x0001ca1c 0x10000 + 51740
57  Hai                             0x000e02b8 0x10000 + 852664
58  Hai                             0x0004be44 0x10000 + 245316
59  Hai                             0x0004c140 0x10000 + 246080
60  UIKit                           0x34f3eca4 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1176
61  UIKit                           0x34f387d6 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 402
62  UIKit                           0x34f06abc -[UIApplication handleEvent:withNewEvent:] + 1004
63  UIKit                           0x34f06560 -[UIApplication sendEvent:] + 48
64  UIKit                           0x34f05f34 _UIApplicationHandleEvent + 5820
65  GraphicsServices                0x32aa3224 PurpleEventCallback + 876
66  CoreFoundation                  0x329d351c __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32
67  CoreFoundation                  0x329d34be __CFRunLoopDoSource1 + 134
68  CoreFoundation                  0x329d230c __CFRunLoopRun + 1364
69  CoreFoundation                  0x3295549e CFRunLoopRunSpecific + 294
70  CoreFoundation                  0x32955366 CFRunLoopRunInMode + 98
71  UIKit                           0x34f37864 -[UIApplication _run] + 544
72  UIKit                           0x34f34cce UIApplicationMain + 1074
73  Hai                             0x00012644 0x10000 + 9796
74  Hai                             0x000122e4 0x10000 + 8932

Thread 1 name:  Dispatch queue: com.apple.libdispatch-manager
Thread 1:
0   libsystem_kernel.dylib          0x34ec63a8 kevent + 24
1   libdispatch.dylib               0x3112af04 _dispatch_mgr_invoke + 708
2   libdispatch.dylib               0x3112ac22 _dispatch_mgr_thread + 30

Thread 2 name:  WebThread
Thread 2:
0   libsystem_kernel.dylib          0x34ec6004 mach_msg_trap + 20
1   libsystem_kernel.dylib          0x34ec61fa mach_msg + 50
2   CoreFoundation                  0x329d33ec __CFRunLoopServiceMachPort + 120
3   CoreFoundation                  0x329d2124 __CFRunLoopRun + 876
4   CoreFoundation                  0x3295549e CFRunLoopRunSpecific + 294
5   CoreFoundation                  0x32955366 CFRunLoopRunInMode + 98
6   WebCore                         0x356f3c9c RunWebThread(void*) + 396
7   libsystem_c.dylib               0x31dbd72e _pthread_start + 314
8   libsystem_c.dylib               0x31dbd5e8 thread_start + 0

Unknown thread crashed with unknown flavor: 5, state_count: 1

Binary Images:
   0x10000 -   0x113fff +Hai armv7  <385e619e9ff93232bab5eba42d9d7c4e> /var/mobile/Applications/B8FFC759-6BBC-4E30-9FD1-5746BD91BE64/Hai.app/Hai
0x2fe0f000 - 0x2fe30fff  dyld armv7  <77eddfd654df393ba9c95ff01715fd08> /usr/lib/dyld
0x30010000 - 0x3001bfff  AccountSettings armv7  <373e59421d983c93931cfbad87b1ae35> /System/Library/PrivateFrameworks/AccountSettings.framework/AccountSettings
0x30169000 - 0x301aefff  GeoServices armv7  <a26be2e76e8730ab91a16502aba376be> /System/Library/PrivateFrameworks/GeoServices.framework/GeoServices
0x301af000 - 0x3021ffff  CoreImage armv7  <86ac6f5a267637b6b7f8a831dfc7c64b> /System/Library/Frameworks/CoreImage.framework/CoreImage
0x30309000 - 0x3030afff  CoreSurface armv7  <97f871f09f503c98a6371c2b657430d8> /System/Library/PrivateFrameworks/CoreSurface.framework/CoreSurface
0x30375000 - 0x30375fff  Accelerate armv7  <55b24cf91a8b3532bde6733c96f14c08> /System/Library/Frameworks/Accelerate.framework/Accelerate
0x30388000 - 0x30399fff  libxpc.dylib armv7  <ccf25b1e49ce3b2fa58d8c8546755505> /usr/lib/system/libxpc.dylib
0x303b2000 - 0x30431fff  libsqlite3.dylib armv7  <bf01f5ed47b033d8bde30d735ff44416> /usr/lib/libsqlite3.dylib
0x30432000 - 0x30469fff  Security armv7  <eea56f71fde83c2981f9281dc7823725> /System/Library/Frameworks/Security.framework/Security
0x305c4000 - 0x30600fff  AppSupport armv7  <311eac85b2a433a884dacba77217b49e> /System/Library/PrivateFrameworks/AppSupport.framework/AppSupport
0x3060a000 - 0x30610fff  liblaunch.dylib armv7  <aa2bcba6fc7a36a191958fef2e995475> /usr/lib/system/liblaunch.dylib
0x30611000 - 0x30611fff  libkeymgr.dylib armv7  <ebd2dddf55d83cf48a18913968775960> /usr/lib/system/libkeymgr.dylib
0x30941000 - 0x30955fff  PersistentConnection armv7  <54091a638f8731cd85ccf00fa06972c3> /System/Library/PrivateFrameworks/PersistentConnection.framework/PersistentConnection
0x30b85000 - 0x30b86fff  libsystem_sandbox.dylib armv7  <6a8f2f33c7543808a0f4599101c3b61a> /usr/lib/system/libsystem_sandbox.dylib
0x30daa000 - 0x30e02fff  CoreAudio armv7  <be335e8eb6f93594b028a6ddd503a183> /System/Library/Frameworks/CoreAudio.framework/CoreAudio
0x30e03000 - 0x30e12fff  GenerationalStorage armv7  <d84c3fd0e7bd36e78c256f2f4c5a4e91> /System/Library/PrivateFrameworks/GenerationalStorage.framework/GenerationalStorage
0x31095000 - 0x31097fff  MobileInstallation armv7  <215d93dbb0f63cbf828f9126eb7b5349> /System/Library/PrivateFrameworks/MobileInstallation.framework/MobileInstallation
0x31127000 - 0x3113dfff  libdispatch.dylib armv7  <9ecfaef4110a3bf9a92d12f0fe8d1d78> /usr/lib/system/libdispatch.dylib
0x31172000 - 0x31176fff  libAccessibility.dylib armv7  <9a17d07b5a3b38cfafdf16f78c99b572> /usr/lib/libAccessibility.dylib
0x31177000 - 0x3119cfff  OpenCL armv7  <f4b08361179a3f6bb033415b0d7c6251> /System/Library/PrivateFrameworks/OpenCL.framework/OpenCL
0x311cd000 - 0x311cefff  libsystem_blocks.dylib armv7  <9fdc27af7350323bbc7d98e14e027907> /usr/lib/system/libsystem_blocks.dylib
0x31340000 - 0x3135dfff  libsystem_info.dylib armv7  <50863bcbf478323e96a8e5b1a83ea6f9> /usr/lib/system/libsystem_info.dylib
0x3135e000 - 0x3136ffff  DataAccessExpress armv7  <e6144ba265da3bb7b9a263aa1a29b054> /System/Library/PrivateFrameworks/DataAccessExpress.framework/DataAccessExpress
0x31819000 - 0x31819fff  vecLib armv7  <a2cfe25e77aa36bfb4a30b2d0d2dd465> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/vecLib
0x31b9d000 - 0x31b9ffff  libCoreVMClient.dylib armv7  <d4d4aa3090c83e87bcb15ed00b93fd5c> /System/Library/Frameworks/OpenGLES.framework/libCoreVMClient.dylib
0x31ba2000 - 0x31ba2fff  libunwind.dylib armv7  <e0a73a57795f3e1698a52ebe6fc07005> /usr/lib/system/libunwind.dylib
0x31ba3000 - 0x31ba6fff  CaptiveNetwork armv7  <f5cc4b97ce9432da9426f12621453325> /System/Library/PrivateFrameworks/CaptiveNetwork.framework/CaptiveNetwork
0x31c4c000 - 0x31c51fff  libcopyfile.dylib armv7  <52e874396c393ed29099789ce702cfe2> /usr/lib/system/libcopyfile.dylib
0x31d61000 - 0x31dabfff  ManagedConfiguration armv7  <f1fbb825def23043830a095b953a9c94> /System/Library/PrivateFrameworks/ManagedConfiguration.framework/ManagedConfiguration
0x31daf000 - 0x31e3bfff  libsystem_c.dylib armv7  <f859ce1ad1773f0ba98d7c6e135b7697> /usr/lib/system/libsystem_c.dylib
0x31e3f000 - 0x31e40fff  libdnsinfo.dylib armv7  <9aede8d6579d3430ac39ae5f95cce498> /usr/lib/system/libdnsinfo.dylib
0x31e48000 - 0x31f39fff  QuartzCore armv7  <35d64a9da5523ae08c9e41511fd3061b> /System/Library/Frameworks/QuartzCore.framework/QuartzCore
0x3215e000 - 0x3220bfff  libxml2.2.dylib armv7  <58d47f064e0232119f4b838ad659f9c1> /usr/lib/libxml2.2.dylib
0x3220c000 - 0x3220dfff  libremovefile.dylib armv7  <402f8956975d3b6fb86ab9b31a43242c> /usr/lib/system/libremovefile.dylib
0x32245000 - 0x32245fff  liblangid.dylib armv7  <644ff4bcfbf337b5b5859e3f0fc0a9a8> /usr/lib/liblangid.dylib
0x324db000 - 0x32525fff  libvDSP.dylib armv7  <441b42aca07b3da39feab25f8349918f> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libvDSP.dylib
0x32613000 - 0x32701fff  libiconv.2.dylib armv7  <2cfefe2ad1d335dd9549562910e7a2e2> /usr/lib/libiconv.2.dylib
0x3270a000 - 0x32758fff  CoreLocation armv7  <44550ebedf23334d85441d9743b74e03> /System/Library/Frameworks/CoreLocation.framework/CoreLocation
0x327ab000 - 0x327b4fff  libMobileGestalt.dylib armv7  <4a15e845dc6f3a4a980de66c1cc44c42> /usr/lib/libMobileGestalt.dylib
0x32946000 - 0x32a5dfff  CoreFoundation armv7  <6d450fe923d7387f8b01845e0edd713d> /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
0x32a86000 - 0x32a8afff  IOMobileFramebuffer armv7  <42dbc26828e934acabb4f3b0a35d8250> /System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/IOMobileFramebuffer
0x32a9e000 - 0x32aa8fff  GraphicsServices armv7  <cb64e146a8ee3fda9e80ffae1ccc9c5a> /System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices
0x32aac000 - 0x32d6dfff  libLAPACK.dylib armv7  <0e94e9a7e7a334649afaccae0f1215a2> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libLAPACK.dylib
0x33217000 - 0x33260fff  AddressBook armv7  <b17a2962e9043e0385c3c2c652155f2b> /System/Library/Frameworks/AddressBook.framework/AddressBook
0x3332f000 - 0x33337fff  MobileWiFi armv7  <b76c3e9fb78234c392058250d4620e72> /System/Library/PrivateFrameworks/MobileWiFi.framework/MobileWiFi
0x33342000 - 0x33358fff  libmis.dylib armv7  <258bc92be5823b239b4412dd42cb4807> /usr/lib/libmis.dylib
0x33359000 - 0x33378fff  libSystem.B.dylib armv7  <0c55744b6f7335eebba4ca2c3d10b43c> /usr/lib/libSystem.B.dylib
0x33379000 - 0x3337efff  libsystem_dnssd.dylib armv7  <27bb5462450732e380f5a2c170546e93> /usr/lib/system/libsystem_dnssd.dylib
0x3347c000 - 0x33482fff  liblockdown.dylib armv7  <9e45ce468a6f31e5b8263f2c224aa800> /usr/lib/liblockdown.dylib
0x337c5000 - 0x3386ffff  libBLAS.dylib armv7  <bf822cc1a3243ae7b104cf73ca22d352> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libBLAS.dylib
0x33892000 - 0x339d7fff  CoreGraphics armv7  <903545b89a7f311d95100ac7d1d44709> /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics
0x33b74000 - 0x33b7bfff  libc++abi.dylib armv7  <bab4dcbfc5943d3fbb637342d35e8045> /usr/lib/libc++abi.dylib
0x33b96000 - 0x33b99fff  CoreTime armv7  <a398de5ba1e43a11b7008e9bb5a7f6fe> /System/Library/PrivateFrameworks/CoreTime.framework/CoreTime
0x33b9a000 - 0x33ba7fff  libbsm.0.dylib armv7  <750a0de73a733019a77144b805d4d2f8> /usr/lib/libbsm.0.dylib
0x33be4000 - 0x33c35fff  libstdc++.6.dylib armv7  <c352af5a742e3c7a8d4d7e5f6f454793> /usr/lib/libstdc++.6.dylib
0x33d44000 - 0x33d44fff  libCVMSPluginSupport.dylib armv7  <a80aaa9989483ce3a496a061fd1e9e0a> /System/Library/Frameworks/OpenGLES.framework/libCVMSPluginSupport.dylib
0x33df1000 - 0x33e11fff  libxslt.1.dylib armv7  <39348471007e39dab80af68b08390456> /usr/lib/libxslt.1.dylib
0x33e15000 - 0x33eedfff  vImage armv7  <caf3648be2933384b6aa1ae7408ab4f0> /System/Library/Frameworks/Accelerate.framework/Frameworks/vImage.framework/vImage
0x33efb000 - 0x33f01fff  libnotify.dylib armv7  <9406297de3e43742887890662a87ab53> /usr/lib/system/libnotify.dylib
0x33f14000 - 0x33f1cfff  ProtocolBuffer armv7  <0e846afacf823d2b8c029cc3010a8253> /System/Library/PrivateFrameworks/ProtocolBuffer.framework/ProtocolBuffer
0x3432b000 - 0x3486ffff  FaceCoreLight armv7  <f326d88709683520b251dc53cb847c11> /System/Library/PrivateFrameworks/FaceCoreLight.framework/FaceCoreLight
0x34870000 - 0x34886fff  DictionaryServices armv7  <6ed2e967136f37d4a4b9b318d6c43b83> /System/Library/PrivateFrameworks/DictionaryServices.framework/DictionaryServices
0x3499b000 - 0x34a61fff  libobjc.A.dylib armv7  <90014d1bc583366d85622e43097df416> /usr/lib/libobjc.A.dylib
0x34a8d000 - 0x34a93fff  MobileIcons armv7  <ed1b46f917903c9b9baaa2be4392dafe> /System/Library/PrivateFrameworks/MobileIcons.framework/MobileIcons
0x34a94000 - 0x34a9efff  libbz2.1.0.dylib armv7  <40e4045fb79e382b8833707746cf28b1> /usr/lib/libbz2.1.0.dylib
0x34aa0000 - 0x34aa0fff  libgcc_s.1.dylib armv7  <eb82984fa36c329387aa518aa5205f3d> /usr/lib/libgcc_s.1.dylib
0x34aa5000 - 0x34aedfff  CoreMedia armv7  <e274e1b894753b2eb05cf7b22a36d0c1> /System/Library/Frameworks/CoreMedia.framework/CoreMedia
0x34aee000 - 0x34af5fff  AssetsLibraryServices armv7  <38132ecfd74b325fb1a4142bab663c19> /System/Library/PrivateFrameworks/AssetsLibraryServices.framework/AssetsLibraryServices
0x34af6000 - 0x34af9fff  NetworkStatistics armv7  <7848d8ebad99367cb4f7f4e3fe88e5d6> /System/Library/PrivateFrameworks/NetworkStatistics.framework/NetworkStatistics
0x34bca000 - 0x34c1bfff  CoreText armv7  <5bfac4ee88d03d5b87a1f105abb7756c> /System/Library/Frameworks/CoreText.framework/CoreText
0x34d01000 - 0x34d1afff  libRIP.A.dylib armv7  <1828cddc5dd93c61afbefb59587d7f8a> /System/Library/Frameworks/CoreGraphics.framework/Resources/libRIP.A.dylib
0x34d1b000 - 0x34d59fff  IOKit armv7  <fcda71d29d6136dfbd84c1725f4998e5> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit
0x34d5a000 - 0x34d5efff  IOSurface armv7  <443ac3aab9283da480dd9dcda3c5c88e> /System/Library/PrivateFrameworks/IOSurface.framework/IOSurface
0x34ec5000 - 0x34edbfff  libsystem_kernel.dylib armv7  <311f379a9fde305d80c1b22b7dd2e52a> /usr/lib/system/libsystem_kernel.dylib
0x34edc000 - 0x34ef2fff  EAP8021X armv7  <952fcfdec0633aff923768fca1a26fcb> /System/Library/PrivateFrameworks/EAP8021X.framework/EAP8021X
0x34ef3000 - 0x34efdfff  libvMisc.dylib armv7  <e8248c797b9b363594bb652ddf7ce16d> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libvMisc.dylib
0x34f03000 - 0x353a5fff  UIKit armv7  <cd513a2f22f53d698c3e10f6fe48a63e> /System/Library/Frameworks/UIKit.framework/UIKit
0x35507000 - 0x3554afff  libcommonCrypto.dylib armv7  <95b49daf4cf038b6bea8010bba3a1e26> /usr/lib/system/libcommonCrypto.dylib
0x3557a000 - 0x35589fff  SpringBoardServices armv7  <a2363f8ed49932dba415d2d4cd32fb74> /System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices
0x3558a000 - 0x3558efff  libGFXShared.dylib armv7  <998fccc16cf735dbb62324202995e193> /System/Library/Frameworks/OpenGLES.framework/libGFXShared.dylib
0x3564a000 - 0x35e09fff  WebCore armv7  <2690c38c9c5f3c09975d619dd1dfbed7> /System/Library/PrivateFrameworks/WebCore.framework/WebCore
0x35e0a000 - 0x35edafff  WebKit armv7  <3c5dd2ec46fe3e189c25bba78ad88fa1> /System/Library/PrivateFrameworks/WebKit.framework/WebKit
0x35edb000 - 0x35f27fff  CoreTelephony armv7  <b8f80d5d594c31d2b5d8fba9fdedb7e1> /System/Library/Frameworks/CoreTelephony.framework/CoreTelephony
0x35f37000 - 0x35f7bfff  MobileCoreServices armv7  <757226927a873d5492be721908077b48> /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices
0x35f7f000 - 0x35f82fff  libsystem_network.dylib armv7  <356cb66612e836968ef24e6e5c3364cc> /usr/lib/system/libsystem_network.dylib
0x35f83000 - 0x35fccfff  libc++.1.dylib armv7  <5b690e5dd5a43a7fb166ade9fe58a7a4> /usr/lib/libc++.1.dylib
0x35fde000 - 0x36016fff  VideoToolbox armv7  <9f25f38d1cd13a1daff99cfde8884410> /System/Library/PrivateFrameworks/VideoToolbox.framework/VideoToolbox
0x36017000 - 0x3613cfff  JavaScriptCore armv7  <2ffc6c87b94434288366bd53765ee267> /System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore
0x366c2000 - 0x36799fff  CFNetwork armv7  <765a472c824830eea91b8f02d12867e4> /System/Library/Frameworks/CFNetwork.framework/CFNetwork
0x3679a000 - 0x3679bfff  libdyld.dylib armv7  <977b0ad6f2f433108b4a0324a57cd2ab> /usr/lib/system/libdyld.dylib
0x36818000 - 0x36891fff  ProofReader armv7  <6db611d8df6530d480f97a40bc519f70> /System/Library/PrivateFrameworks/ProofReader.framework/ProofReader
0x36909000 - 0x36ac6fff  ImageIO armv7  <02e3578171fa3b6a969b244275fd2bab> /System/Library/Frameworks/ImageIO.framework/ImageIO
0x36ac7000 - 0x36aeafff  Bom armv7  <c3435ecd2e5839f89de51edad0e1bb00> /System/Library/PrivateFrameworks/Bom.framework/Bom
0x36aeb000 - 0x36b20fff  SystemConfiguration armv7  <4464a4e3bb3f32f7abaa35ebf31fda49> /System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration
0x36d11000 - 0x36ef5fff  AudioToolbox armv7  <c91e27850452330ea804db6408840fd2> /System/Library/Frameworks/AudioToolbox.framework/AudioToolbox
0x37028000 - 0x37034fff  CoreVideo armv7  <364fa32d513f3c11b50970120545f1a8> /System/Library/Frameworks/CoreVideo.framework/CoreVideo
0x37043000 - 0x371c1fff  Foundation armv7  <c40ddb073142315bb4ebb214343d0b7f> /System/Library/Frameworks/Foundation.framework/Foundation
0x37471000 - 0x37474fff  libcompiler_rt.dylib armv7  <b2c05d8601c13be884097192dca4e187> /usr/lib/system/libcompiler_rt.dylib
0x37503000 - 0x37507fff  AggregateDictionary armv7  <3a3a33f3a05538988c6e2bb363dc46a8> /System/Library/PrivateFrameworks/AggregateDictionary.framework/AggregateDictionary
0x3759d000 - 0x3759efff  DataMigration armv7  <d77f0e8f39ee37f5a2ac713a3fd9e693> /System/Library/PrivateFrameworks/DataMigration.framework/DataMigration
0x3759f000 - 0x375dffff  libGLImage.dylib armv7  <40448706190031f6b0d9636cc11ee81d> /System/Library/Frameworks/OpenGLES.framework/libGLImage.dylib
0x376e5000 - 0x376ebfff  MobileKeyBag armv7  <e1f06241ef0e3f0aae00f15df572077e> /System/Library/PrivateFrameworks/MobileKeyBag.framework/MobileKeyBag
0x37746000 - 0x37749fff  libmacho.dylib armv7  <e52b77623bd031bc807e77029566c777> /usr/lib/system/libmacho.dylib
0x3774a000 - 0x37756fff  libCRFSuite.dylib armv7  <bdb2b4d1a78c39c1ba60d791207aed2a> /usr/lib/libCRFSuite.dylib
0x37757000 - 0x37792fff  libCGFreetype.A.dylib armv7  <55941c96cf1f3b048e72a148c4496c16> /System/Library/Frameworks/CoreGraphics.framework/Resources/libCGFreetype.A.dylib
0x377b9000 - 0x377bdfff  libcache.dylib armv7  <d6a7436ed8dc33d795c9b42baf864882> /usr/lib/system/libcache.dylib
0x378a5000 - 0x378bafff  libresolv.9.dylib armv7  <66f7557fa4b43979b186e00271839fdb> /usr/lib/libresolv.9.dylib
0x378c0000 - 0x37a09fff  libicucore.A.dylib armv7  <b70646b63f1f3b33896dd8cb91b8dab1> /usr/lib/libicucore.A.dylib
0x37a4b000 - 0x37a6ffff  PrintKit armv7  <08509c7bc915358b953de6f5cbef5c56> /System/Library/PrivateFrameworks/PrintKit.framework/PrintKit
0x37ae0000 - 0x37aecfff  libz.1.dylib armv7  <36ce86a3dc8c344596c8c325615f374b> /usr/lib/libz.1.dylib
0x37aed000 - 0x37afcfff  OpenGLES armv7  <e80acc691001301e96101bb89d940033> /System/Library/Frameworks/OpenGLES.framework/OpenGLES

Any idea what's happening here?

teacup 0.3.11 breaks UIGlossyButton on upon styling

Upgrading from 0.3.10 to 0.3.11 caused some odd behavior for me.

Using:

app.pods do
  pod 'UIGlossyButton'
end

This code:

@log_in_button = subview UIGlossyButton.alloc.initWithFrame([[50, 8], [220, 37]]),
  title: 'Log In'

Would produce a touchable button.

Since 0.3.11, the button doesn't respond to touches.

  • UIButton still works fine
  • UIGlossyButton works fine if I don't use teacup to set any properties

I poked around the code a bit and it seemed to be coming from Teacup::Layout.layout:81, which looked pretty odd to me...

if properties
  view.style(properties) if properties
end

I traced that to Teacup.apply (or apply_hash) at UIView#style:120-124, which also looked pretty odd to me...

def style(properties, orientation=nil)
  Teacup.apply_hash self, properties
  properties.each do |key, value|
    Teacup.apply self, key, value
  end
  ...
end

But I stopped there, so I'm not sure exactly what's causing it.

Style is not applied at Startup

Hi,

I'm using teacup to customize my controls including an UINavigationBar.

It works but at startup the style is not applied. If the UINavigationBar hides and shows back, then the style is applied.

I have created a small app which presents my issue.
Just start the application, and set the focus to the search field and then cancel.

It's probably due to iOS which does some caching on views.

But I've no idea how to fix it. :-)

https://github.com/Arkan/promotion_with_teacup

Thanks

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can image, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post aobut this project for more information.

Sample not working

(main)> 2012-06-04 17:43:12.166 Hai[20510:f803] *** Terminating app due to uncaught exception 'NoMethodError', reason: 'stylesheet.rb:93:in initialize:': undefined methodinclude' for Teacup::Stylesheet::IPadVertical:Teacup::Stylesheet (NoMethodError)

device undeclared before use in ui_view_controller.rb autorotateToOrientation

Whilst running an iOS 6 app under iOS 7, my application crashes when the keyboard is to be displayed. Examining the logs shows that this is due to an uncaught exception 'NameError' reason: "undefined local variable or method `device'"

This is because in UIViewController.autorotateToOrientation, device isn't declared before its first use.

Adding device = UIDevice.currentDevice.userInterfaceIdiom beforehand resolves this issue.

how to set the UITextField delegate

I write like this, but not work

style UITextField,
text: '',
backgroundColor: UIColor.whiteColor,
width: 150,
height: 30,
delegate: self

def textFieldShoudReturn(textfield)
textfield.resignFirstResponder
true
end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.