Coder Social home page Coder Social logo

motion-kit's Introduction

MotionKit

Join the chat at https://gitter.im/motion-kit/motion-kit

Build Status Version

The RubyMotion layout and styling gem.

  1. Crossplatform compatibility: iOS, OSX, tvOS and planned support for Android
  2. Simple, easy to learn DSL
  3. Crossframework compatibility:
  4. Easily extendable to support custom, mini-DSLs
  5. Non-polluting
  6. ProMotion/RMQ/SugarCube-compatible (kind of goes hand-in-hand with being non-polluting)
  7. Styles and layouts are "just code" (not hash-based like in Teacup)
  8. Written by the authors of ProMotion and Teacup

What happened to Teacup??

You can read all about why Colin decided that Teacup needed to be replaced with a new project, rather than upgraded or refactored.

If you need to update your app to use MotionKit, see READMORE.md for an example of migrating stylesheets, styles, and constraints.

Usage

In your Gemfile

gem 'motion-kit'

From your controller you will instantiate a MotionKit::Layout instance, and request views from it. layout.view is the root view, and it's common to assign this to self.view in your loadView method. You'll also want to hook up your instance variables, using layout.get(:id) or using instance variables.

class LoginController < UIViewController
  def loadView
    @layout = LoginLayout.new
    self.view = @layout.view

    @button = @layout.get(:button)  # This will be created in our layout (below)
    @button = @layout.button  # Alternatively you can use instance variables and accessor methods
  end

  def viewDidLoad
    @button.on(:touch) { my_code } # Mix with some SugarCube for sweetness!
    rmq(@button).on(:touch) { my_code } # and of course RMQ works just as well
  end
end

Lay out your subviews with a clean DSL

In a layout class, the layout method is expected to create the view hierarchy, and it should also take care of frames and styling. You can apply styles here, and it's handy to do so when you are creating a quick mock-up, or a very small app. But in a real application, you'll want to include a Stylesheet module so your layout isn't cluttered with all your styling code.

Here's a layout that just puts a label and a button in the middle of the screen:

class SimpleLayout < MotionKit::Layout
  # this is a special attr method that calls `layout` if the view hasn't been
  # created yet. So you can call `layout.button` before `layout.view` and you
  # won't get nil, and layout.view will be built.
  view :button

  def layout
    add UILabel, :label

    @button = add UIButton, :button
  end

  def label_style
    text 'Hi there! Welcome to MotionKit'
    font UIFont.fontWithName('Comic Sans', size: 24)
    size_to_fit

    # note: there are better ways to set the center, see the frame helpers below
    center [CGRectGetMidX(superview.bounds), CGRectGetMidY(superview.bounds)]
    text_alignment NSTextAlignmentCenter
    text_color UIColor.whiteColor

    # if you prefer to use shorthands from another gem, you certainly can!
    background_color rmq.color.white  # from RMQ
    background_color :white.uicolor   # from SugarCube
  end

  def button_style
    # this will call 'setTitle(forState:)' via a UIButton helper
    title 'Press it!'
    size_to_fit

    # this shorthand is much better!  More about frame helpers below.
    center ['50%', '50% + 50']
  end

end

That's easy enough, right? In this next, more complicated layout, we'll create a login page with a 'Login' button and inputs for username and password. I will assign the frame in the layout method instead of in the _style methods. This is purely an aesthetic choice. Some people like to have their frame code in the layout method, others like to put it in the *_style methods.

class LoginLayout < MotionKit::Layout
  # we write our `_style` methods in a module
  include LoginStyles

  def layout
    # we know it's easy to add a subview, with a stylename...
    add UIImageView, :logo

    # inside a block you can set properties on that view
    add UIImageView, :logo do
      frame [[0, 0], [320, 568]]
    end

    # you can set the size to fill horizontally and keep the aspect ratio
    add UIImageView, :logo do
      frame [[0, 0], ['100%', :scale]]
    end

    # many other examples here
    add UIView, :button_container do

      # Like I said, the frame method is very powerful. It will try to
      # apply the correct autoresizingMask for you; the from_bottom method will
      # set the UIAutoresizingMask to "FlexibleTop", and using '100%' in the
      # width will ensure the frame stays the width of its parent.
      frame from_bottom(height: 50, width: '100%')
      frame from_bottom(h: 50, w: '100%')  # is fine, too

      # same as above; assumes full width
      frame from_bottom(height: 50)

      # views added inside a block are added to that
      # container.  You can reference the container with 'superview', but then
      # you're working on the object directly, so no method translation (foo_bar
      # => fooBar) will be done for you.
      add UIButton, :login_button do
        background_color superview.backgroundColor

        # 'parent' is not instance of a view; it's a special object that
        # acts like a placeholder for various values. If you want to assign
        # *any* superview property, use 'superview' instead.  'parent' is mostly
        # useful for setting the frame.
        frame [[ 10, 5 ], [ 50, parent.height - 10 ]]
      end
    end

    add UIView, :inputs do
      frame x: 0, y: 0, width: '100%', height: '100% - 50'

      # setting autoresizing_mask should handle rotation events
      autoresizing_mask :pin_to_top, :flexible_height, :flexible_width

      # we'll use 'sizeToFit' to calculate the height
      add UITextField, :username_input do
        frame [[10, 10], ['100% - 10', :auto]]
      end
      add UITextField, :password_input do
        frame below(:username_input, down: 8)
      end
    end
  end
end

Dynamically adding views

In MotionKit, it is easy to add views on the fly using the same API as used during layout.

def add_button style, button_title

  context get(:inputs) do #Two very useful methods for accessing/modifying previously added views
    add UIButton, :dynamic_button do 
      title button_title
      constraints do # if using autolayout
        ...
      end
    end
  end
end

During layout, z-order is determined by the sequence in which views are added to the hierarchy. You can control this dynamically by supplying :behind, :in_front_of, or :z_index options (:z_index not supported in OS X)

add UIImageView, :highlight_square, behind: get(:dynamic_button)
add UIImageView, :x_marks_the_spot, in_front_of: @selected_label 
add UILabel, :subterranian_marker, z_index: 4 #becomes the 4th view in the subview hierarchy

Styles are compiled, simple, and clean

In MotionKit, when you define a method that has the same name as a view stylename with the suffix "_style", that method is called and is expected to style that view.

class LoginLayout < MK::Layout

  def layout
    add UIImageView, :logo do
      # this can be moved into `logo_style` below:
      frame [[0, 0], ['100%', :scale]]
    end
    add UIView, :button_container
  end

  def logo_style
    frame [[0, 0], ['100%', :scale]]
    image UIImage.imageNamed('logo')
  end

  def button_container_style
    background_color UIColor.clearColor
  end

  # In case you're curious, the MK::Layout#initialize method takes no arguments.
  # Just be sure to call `super`
  def initialize
    super
    # ...
  end

end

So as an additional code-cleanup step, why not put those methods in a module, and include them in your layout! Sounds clean and organized to me! You can include multiple stylesheets this way, just be careful around name collisions.

# app/styles/login_styles.rb
module LoginStyles

  def login_button_style
    # this example uses SugarCube to create UIColor and CGColor objects.
    background_color '#51A8E7'.uicolor
    title 'Log In'
    # `layer` returns a CALayer, which in turn becomes the new context inside
    # this block
    layer do
      corner_radius 7.0
      shadow_color '#000000'.cgcolor
      shadow_opacity 0.9
      shadow_radius 2.0
      shadow_offset [0, 0]
    end
  end

end

# back in our LoginLayout class
class LoginLayout
  include LoginStyles

  def layout
    add UIButton, :login_button
    # ...
  end

end

Using child-layouts

If you have a very complicated layout that you want to break up into child layouts, that is supported as well:

class ParentLayout < MK::Layout

  def layout
    add ChildLayout, :child_id
  end

end

The id is (as always) optional, but allows you to fetch the layout using get(id).

layout.get(:child_id)  # => ChildLayout

Calling get(:child_id).view will return the view associated with that layout.

Setting a custom root view

If you need to use a custom root view, you can use the root method from within the layout method. When you create or assign the root view this way, you must assign subviews and styles inside a block that you pass to root.

def layout
  root(SomeOtherViewclass) do
    add UILabel
  end
end

You can also pass in a root view to your layout, like this:

def loadView
  @layout = MyLayout.new(root: self.view).build
end

Make sure to call .build; otherwise, the layout will be returned but the view not built.

In this case, if you want to style the root view, just refer to it in your layout:

def layout
  root :my_root_view do
    # ...
  end
end

def my_root_view_style
  background_color UIColor.grayColor
end

This is especially useful with collection views, table views, and table cells, where you can assign a root view explicitly:

return MyCellLayout.new(root: cell).build

Keep in mind that MotionKit will not retain a strong reference when you provide a root view, so retain it yourself to prevent it from being deallocated.

How do styles get applied?

If you've used RMQ's Stylers, you'll recognize a very similar pattern here. In RMQ the 'style' methods are handed a 'Styler' instance, which wraps access to the view. In MotionKit we make use of method_missing to call these methods indirectly. That takes care of most methods related to styling, but you might want to write some "helper" methods so that your styling code is more concise. Some examples are included in the MotionKit core, but the SweetKit gem has many more. If you are writing helpers for UIKit or AppKit, please consider adding them to SweetKit, so we can all share in the productivity boost! ๐Ÿ˜ƒ

  def login_label_style
    text 'Press me'  # this gets delegated to UILabel#text
  end

  # It's not hard to add extensions for common tasks, like setting the "normal"
  # title on a UIButton
  def login_button_style
    title 'Press me'
    # this gets delegated to UIButtonHelpers#title(title), which in turn calls
    # button.setTitle(title, forState: UIControlStateNormal)
    # See uibutton_helpers.rb for implementation.
  end

MotionKit offers shortcuts and mini-DSLs for frames, auto-layout, and miscellaneous helpers. But if a method is not defined, it is sent to the view after a little introspection. If you call a method like title_color value, MotionKit will try to call:

  • setTitle_color(value)
  • title_color=(value)
  • title_color(value)
  • (try again, converting to camelCase)
  • setTitleColor(value)
  • titleColor=(value)
  • titleColor(value)
  • (failure:) raise NoMethodError
  def login_button_style
    background_color UIColor.clearColor  # this gets converted to `self.target.backgroundColor = ...`
  end

Introspection and method_missing add a little overhead to your code, but in our benchmarking it is insignificant and undetectable. Let us know if you find any performance issues.

You can easily add your own helpers to MotionKit. They should all be named consistenly, e.g. MotionKit::UIViewHelpers, MotionKit::UILabelHelpers, etc. You just need to specify the "target class" that your helper class is meant to work with. Each class can only have one helper class.

module MotionKit
  # these helpers will only be applied to instances of UILabel and UILabel
  # subclasses
  class UILabelHelpers < UIViewHelpers
    targets UILabel

    # style methods can accept any number of arguments, and a block. The current
    # view should be referred to via the method `target`
    def color(color)
      target.textColor = color
    end

    # If a block is passed it is your responsibility to call `context(val, &block)`
    # if that is appropriate.  I'll use `UIView#layer` as an example,
    # but actually if you pass a block to a method that returns an object, that
    # block will be called with that object as the context.
    def layer(&block)
      context(target.layer, &block)
    end

    # Sure, you can add flow-control mechanisms if that's your thing!
    #
    # You can use the block to conditionally call code; on iOS there are
    # orientation helpers `portrait`, `landscape`, etc that apply styles based
    # on the current orientation.
    def sometimes(&block)
      if rand > 0.5
        yield
      end
    end

  end
end

Adding your own helper methods

For your own custom classes, or when you want to write helper methods for a built-in class, you will need to write a class that "targets" that class. This will be a subclass of MK::UIViewHelpers; it looks and feels like a MK::Layout subclass, but these classes are used to extend the MotionKit DSL, and should not be instantiated or used to build layouts.

Again, to be clear: you should be subclassing MK::Layout when you build your controller layouts, and you should write a subclass of MK::UIViewHelpers only when you are adding extensions to the MotionKit DSL.

# Be sure to extend an existing Helpers class, otherwise you'll lose a lot of
# functionality.  Often this will be `MK::UIViewHelpers` on iOS and
# `MK::NSViewHelpers` on OS X.
class CustomViewHelpers < MK::UIViewHelpers
  targets CustomView

  def fore_color(value)
    target.foregroundColor = value
  end

end

Even more information...

...is in the READMORE document. I re-explain some of these topics, go into some more detail, that kinda thing. Basically an overflow document for topics I don't want to stuff into the README.

MotionKit extensions

These are all built-in, unless otherwise specified.

Frames

There are lots of frame helpers for NSView and UIView subclasses. It's cool that you can set position and sizes as percents, but scroll down to see examples of setting frames based on any other view. These are super useful! Most of the ideas, method names, and some code came straight out of geomotion. It's not quite as powerful as geomotion, but it's close!

One advantage over geomotion is that many of these frame helpers accept a view or view name, so that you can place the view relative to that view.

# most direct way to set the frame, using pt values
frame [[0, 0], [320, 568]]

# using sizes relative to superview
frame [[5, 5], ['100% - 10pt', '100% - 10pt']]
# the 'pt' suffix is optional, and ignored.  in the future we could add support
# for other suffixes - would that even be useful?  probably not...

# other available methods:
origin [5, 5]
x 5  # aka left(..)
right 5  # right side of the view is 5px from the left side of the superview
bottom 5  # bottom of the view is 5px from the top of the superview
size ['100% - 10', '100% - 10']
width '100% - 10'  # aka w(...)
height '100% - 10'  # aka h(...)

size ['90%', '90%']
center ['50%', '50%']

########
# +--------------------------------------------------+
# |from_top_left       from_top        from_top_right|
# |                                                  |
# |from_left          from_center          from_right|
# |                                                  |
# |from_bottom_left   from_bottom   from_bottom_right|
# +--------------------------------------------------+

You can position the view relative to other views, either the superview or any other view. You must pass the return value to frame.

# If you don't specify a view to base off of, the view is positioned relative to
# the superview:
frame from_bottom_right(size: [100, 100])  # 100x100 in the BR corner
frame from_bottom(size: ['100%', 32])  # full width, 32pt height
frame from_top_right(left: 5)

# But if you pass a view or symbol as the first arg, the position will be
# relative to that view
from_top_right(:info_container, left: 5)


########
#          above
#          +---+
#  left_of |   | right_of
# (before) |   | (after)
#          +---+
#          below

# these methods *require* another view.
frame above(:foo, up: 8)

frame above(:foo, up: 8)
frame before(:foo, left: 8)
frame relative_to(:foo, down: 5, right: 5)

# it's not common, but you can also pass a view to any of these methods
foo = self.get(:foo)
frame from_bottom_left(foo, up: 5, left: 5)

Autoresizing mask

You can pass symbols like autoresizing_mask :flexible_width, or use symbols that have more intuitive meaning than the usual UIViewAutoresizingFlexible* constants. These work in iOS and OS X.

All of the :pin_to_ shorthands have a fixed size, whereas the :fill_ shorthands have flexible size.

# the :fill shorthands will get you a ton of mileage
autoresizing_mask :fill_top
# but if you want the size to stay constant, use :pin_to
autoresizing_mask :pin_to_bottom
# or, a list of flexible sides
autoresizing_mask :flexible_right, :flexible_bottom, :flexible_width
# or, combine them in some crazy fancy way
autoresizing_mask :pin_to_left, :rigid_top  # 'rigid' undoes a 'flexible' setting

flexible_left:       Sticks to the right side
flexible_width:      Width varies with parent
flexible_right:      Sticks to the left side
flexible_top:        Sticks to the bottom
flexible_height:     Height varies with parent
flexible_bottom:     Sticks to the top

rigid_left:          Left side stays constant (undoes :flexible_left)
rigid_width:         Width stays constant (undoes :flexible_width)
rigid_right:         Right side stays constant (undoes :flexible_right)
rigid_top:           Top stays constant (undoes :flexible_top)
rigid_height:        Height stays constant (undoes :flexible_height)
rigid_bottom:        Bottom stays constant (undoes :flexible_bottom)

fill:                The size increases with an increase in parent size
fill_top:            Width varies with parent and view sticks to the top
fill_bottom:         Width varies with parent and view sticks to the bottom
fill_left:           Height varies with parent and view sticks to the left
fill_right:          Height varies with parent and view sticks to the right

pin_to_top_left:     View stays in top-left corner, size does not change.
pin_to_top:          View stays in top-center, size does not change.
pin_to_top_right:    View stays in top-right corner, size does not change.
pin_to_left:         View stays centered on the left, size does not change.
pin_to_center:       View stays centered, size does not change.
pin_to_right:        View stays centered on the right, size does not change.
pin_to_bottom_left:  View stays in bottom-left corner, size does not change.
pin_to_bottom:       View stays in bottom-center, size does not change.
pin_to_bottom_right: View stays in bottom-right corner, size does not change.

Constraints / Auto Layout

Inside a constraints block you can use similar helpers as above, but you'll be using Cocoa's Auto Layout system instead. This is the recommended way to set your frames, now that Apple is introducing multiple display sizes. But beware, Auto Layout can be frustrating... :-/

Here are some examples to get started:

constraints do
  top_left x: 5, y: 10
  # the MotionKit::Constraint class has lots of aliases and "smart" methods,
  # so you can write very literate code:
  top_left.equals([5, 10])
  top_left.is([5, 10])
  top_left.is.equal_to(x: 5, y: 10)
  top_left.is == { x: 5, y: 10 }
  top_left.is >= { x: 5, y: 10 }
  top_left.is <= { x: 5, y: 10 }

  # this is all the same as setting these two constraints:
  x 5   # aka `left 5`
  y 10  # aka `top 10`

  # You can have multiple constraints on the same property, and if the
  # priorities are set appropriately you can easily have minimum margins,
  # minimum widths, that kind of thing:
  x.is.at_least(10).priority(:required)
  x.is(15).priority(:low)
  width.is.at_least(100).priority(:required)
  width.is(150).priority(:low)

  # using the `Constraint#is` method you can even use ==, <= and >=
  x.is >= 10
  x.is == 15

  # setting the priority:
  (x.is >= 10).priority(:required)
  (x.is == 15).priority(:low)
  # setting the identifier
  x.equals(15).identifier('foo')
end

But of course with AutoLayout you set up relationships between views. Using the element-id as a placeholder for a view works especially well here.

constraints do
  top_left.equals x: 5, y: 5     # this sets the origin relative to the superview
  top_left.equals(:superview).plus([5, 5])  # this will do the same thing!

  width.equals(:foo).minus(10)  # searches for a view named :foo
  height.equals(:foo).minus(10)
  # that's repetitive, so just set 'size'
  size.equals(:foo).minus(10)
  size.equals(:foo).minus([10, 15])  # 10pt thinner, 15pt shorter

  # if you are using a view that has a sensible intrinsic size, like an image,
  # you can use :scale to have the width or height adjusted according to the
  # other size
  width.equals(:superview)
  height(:scale)  # scale the height according to the width
end

Just like with frame helpers you can use the :element_id to refer to another view, but get this: the view need not be created yet! This is because when you setup a constraints block, it isn't resolved immediately; the symbols are resolved at the end. This feature uses the deferred method behind the scenes to accomplish this.

add UIView, :foo do
  constraints do
    width.equals(:bar).plus(10)  # :bar has not been added yet!
  end
end

add UIView, :bar do
  constraints do
    width.equals(:foo).minus(10)
    width.equals(100).minus(10)
    # believe it or not, this ^ code works!  AutoLayout is a strange beast; it's
    # not an "imperative" system, it solves a system of equations.  In this
    # case, :bar will have width 110, and :foo will have width 100, because
    # those values solve these equations:
    #     foo.width = 100
    #     foo.width = bar.width - 10
    #     foo.width = bar.width + 10
    # If you have constraints that conflict you'll get error messages or
    # nonsensical values.

    # There are helpers that act as placeholders for views, if you have multiple
    # views with the same name:
    #     first, last, nth
    width.equals(last(:foo))
    width.equals(first(:foo))
    width.equals(nth(:foo, 5))
  end
end

One common use case is to use a child layout to create many instances of the same layout that repeat, for instance a "row" of content. In this case you will probably have many views with the same id, and you will not know the index of the container view that you want to add constraints to. In this situation, use the nearest, prev or next method to find a container, sibling, or child view.

prev and next are easy; they just search for a sibling view. No superviews or subviews are searched.

nearest will search child views, siblings, and superviews, in that order. The "distance" is calculated as such:

  • the current view
  • subviews
  • siblings
  • superview
  • superview's siblings, or a child of the sibling (depth-first search)
  • continue up the tree

See the AutoLayout sample app for an example of this usage.

items.each do |item|
  add UIView, :row do
    add UIImageView, :avatar
    add UILabel, :title
  end
end

def title_style
  constraints do
    # center the view vertically
    center.equals(nearest(:row))
    # and place it to the right of the :avatar
    left.equals(nearest(:avatar), :right).plus(8)
    right.equals(nearest(:row)).minus(8)
  end
end

One pain point in working with constraints is determining when to add them to your views. We tried really hard to figure out a way to automatically add them, but it's just an untenable problem (Teacup suffers from a similar conundrum).

Essentially, the problem comes down to this: you will often want to set constraints that are related to the view controller's view, but those must be created/set after controller.view = @layout.view. Without doing some crazy method mangling on NS/UIView we just can't do this automatically

Long story short: If you need to create constraints that refer to the controller view, you need to use a separate method that is called after the view hierarchy is created.

class MainLayout < MK::Layout

  def layout
    add UILabel, :label do
      constraints do
        x 0
        width('100%')
      end
    end
  end

  # You should call this method from `UIViewController#updateViewConstraints`
  # and pass in your controller
  def add_constraints(controller)
    # guard against adding these constraints more than once
    unless @layout_constraints_added
      @layout_constraints_added = true
      constraints(:label) do
        top.equals(controller.topLayoutGuide)
      end
    end
  end

end

class MainController < UIViewController

  def loadView
    @layout = MainLayout.new
    self.view = @layout.view
  end

  # for the constraints to work reliably they should be added in this method:
  def updateViewConstraints
    @layout.add_constraints(self)
    super
  end

end

Animating and Changing constraints

It might feel natural to treat constraints as "frame setters", but they are persistent objects that are attached to your views. This means if you create new constraints, like during a screen rotation, your old constraints don't โ€œgo awayโ€. For example:

def label_style
  portrait do
    left 10
  end

  landscape do
    left 15  # adds *another* constraint on the left attribute - in addition to the `left 10` constraint!
  end
end

Instead, you should retain the constraint and make changes to it directly:

  constraints do
    @label_left_constraint = left 10
  end

  # reapply blocks are called via the Layout#reapply! method.
  reapply do
    portrait do
      @label_left_constraint.equals 10
    end

    landscape do
      @label_left_constraint.equals 15
    end
  end

If you want to animate a constraint change, you can use layoutIfNeeded from within a UIView animation block. The sample app "Chatty" does this to move a text field when the keyboard is displayed. kbd_height is the height of the keyboard.

@container_bottom.minus kbd_height  # set @container_bottom.constant = 0 when the keyboard disappears

UIView.animateWithDuration(duration, delay: 0, options: curve, animations: -> do
  self.view.layoutIfNeeded  # applies the constraint change
end, completion: nil)

You can also activate/deactivate constraints selectively, and animate the transitions between them.

class MyLayout < MK::Layout

  def layout
    add UIButton, :my_button do
      constraints do
        @top_constraint = top.equals(:superview, :bottom)
        @bottom_constraint = bottom.equals(:superview).deactivate
        left.equals(:superview)
        right.equals(:superview)
        height 48
      end
  end

  def show_button
    @top_constraint.deactivate
    @bottom_constraint.activate
    UIView.animateWithDuration(0.3, animations: -> do
      self.view.layoutIfNeeded
    end)
  end

  def hide_button
    @bottom_constraint.deactivate
    @top_constraint.activate
    UIView.animateWithDuration(0.3, animations: -> do
      self.view.layoutIfNeeded
    end)
  end

end

MotionKit::Events

gem install motion-kit-events

Adds on :event and trigger :event methods to MK::Layout objects. These can be used to send events from the Layout to your controller, further simplifying the controller code (and usually making it more testable). See the MotionKit::Events documentation for more information.

MotionKit::Templates

gem install motion-kit-templates

Adds project templates, for use with motion create.

motion create foo --template=mk-ios
motion create foo --template=mk-osx

Some handy tricks and Features

Orientation specific styles

These are available on iOS.

add UIView, :container do
  portrait do
    frame from_top(width: '100%', height: 100)
  end
  landscape do
    frame from_top_left(width: 300, height: 100)
  end
end

Update views via 'always', 'reapply', and 'deferred'

In your style methods, you can register blocks that get called during "restyling", which is usually triggered by a rotation change (though, if you're making good use of autoresizingMask or AutoLayout constraints, you should not have to do this, right?).

It's important to note that the style methods are not actually called again. The blocks are retained on the view, along with the "context", and calling reapply! calls all those blocks with the context set as you'd expect.

If you have code that you want to be called during initialization and during reapply, use the always helper:

def login_button_style
  # only once, when the layout is first being created
  title 'initial title'

  # only during reapply
  reapply do
    title 'something happened!'
  end

  # applied every time
  always do
    title 'You win!'
  end
end

Or, you might need to set a frame or other property based on a view that hasn't been created yet. In this case, you can use deferred to have a block of code run after the current layout is completed.

def login_button_style
  deferred do
    frame below(last(:label), height: 20)
  end
end

Apply styles via module

module AppStyles

  def rounded_button
    layer do
      corner_radius 7
      masks_to_bounds true
    end
  end

end

class LoginLayout < MotionKit::Layout
  include AppStyles

  def layout
    add button, :login_button
  end

  def login_button_style
    self.rounded_button
    title 'Login'
  end

end

Using SweetKit

The SweetKit gem combines MotionKit and SugarCube. The helpers it provides allow for even more expressiveness, for instance:

add UITextField do
  return_key_type :email
  text_alignment :right
end

The OS X helpers are really nice, because it tries to hide most of the annoying subtletees of the NSCell/NSControl dichotomy.

gem install sweet-kit

Gotchas

A Note on add and remove

When you use the add method to add a subview, that view will be retained by the Layout even if you remove it from the view hierarchy. If you want the Layout to forget all about the view, call remove(view) (which also calls removeFromSuperview) or forget(element_id) (which only removes it from the Layout) on the Layout.

Contributing

We welcome your contributions! Please be sure to run the specs before you do, and consider adding support for both iOS and OS X.

To run the specs for both platforms, you will need to run rake spec twice:

> rake spec  # runs iOS specs
> rake spec platform=osx  # OS X specs

Goodbye Teacup

If you've worked with XIB/NIB files, you might know that while they can be cumbersome to deal with, they have the great benefit of keeping your controllers free of layout and styling concerns. Teacup brought some of this benefit, in the form of stylesheets, but you still built the layout in the body of your controller file. This needed to be fixed.

Plus Teacup is a beast! Imported stylesheets, orientation change events, auto-layout support. It's got a ton of features, but with that comes a lot of complexity. This has led to an unfortunate situation - I'm the only person who understands the code base! This was never the intention of Teacup. It started out as, and was always meant to be, a community project, with contributions coming from all of its users.

When ProMotion and later RMQ were released, they both included their own styling mechanisms. Including Teacup as a dependency would have placed a huge burden on their users, and they would have had to ensure compatibility. Since Teacup does a lot of method swizzling on base classes, this is not a trivial undertaking.

If you use RMQ or ProMotion already, you'll find that MotionKit fits right in. We designed it to be something that can easily be brought into an existing project, too; it does not extend any base classes, so it's completely opt-in.

Unlike Teacup, you won't have your styles reapplied due to orientation changes, but it's really easy to set that up, as you'll see. Or, use AutoLayout (the DSL is better than Teacup's, I think) and you'll get orientation changes for free!

Big thanks to everyone who contributed on this project! I hope it serves you as well as Teacup, and for even longer into the future.

Sincerely,

Colin T.A. Gray Feb 13, 2014

motion-kit's People

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

motion-kit's Issues

ProMotion, MotionKit and child layouts

I have a PM::Screen with a Navigation Bar, and I am adding views to it using a MotionKit Layout.

Experimenting with child layouts, it appears that the when I do add ChildLayout it first creates a UIView the size of the iPhone screen and then adds whatever subviews I've defined in the child layout.

Is this how it is supposed to happen?

One problem is that this screen sized container view blocks whatever is underneath it, a TableView in my case. The other thing is that this container view doesn't take into account the height of the NavBar, and if something is positioned at the bottom of the child view, it will be off screen.

Here's some code I'm using:
main layout

 def layout
    root :main do
      #add UILabel, :logo_label
      add today_events_table, :today_events_table
      add LogoLayout, :logo_layout
    end
  end

child layout

  def layout
    add UIView, :logo_container do
      add UIImageView, :logo_image
    end
  end

And this is how Reveal sees the hierarchy:

captura de tela 2014-09-05 as 14 58 21

What am I doing wrong here?

Thanks!!

Styles inheritance and extensions

Hello,

I came from TeaCup and what I loved there is styles inheritance (like include and extend).
How this can be done in MotionKit?

Random reapply! crashes

Hi everyone :)

lately I get more and more crashes which seem to relate to the reapply! method.

Example Stacktrace:

2014-07-14 11:22:14.658 8actions Development[51083:70b] *** Terminating app due to uncaught exception 'ArgumentError', reason: 'uiview_layout.rb:33:in `reapply!:': wrong number of arguments (1 for 0) (ArgumentError)
    from search_layout.rb:65:in `block in refresher_style'
    from tree_layout.rb:174:in `reapply'
    from search_layout.rb:64:in `refresher_style'
    from tree_layout.rb:147:in `block in call_style_method:'
    from base_layout.rb:113:in `context:'
    from tree_layout.rb:146:in `call_style_method:'
    from tree_layout.rb:158:in `block in reapply!:'
    from cocoa_util.rb:34:in `find_all_views:'
    from cocoa_util.rb:39:in `block in find_all_views:'
    from cocoa_util.rb:38:in `find_all_views:'
    from tree_layout.rb:157:in `reapply!:'
    from uiview_layout.rb:33:in `reapply!:'
    from tree_layout.rb:174:in `reapply'
    from memo_list_container_layout.rb:26:in `container_style'
    from tree_layout.rb:147:in `block in call_style_method:'
    from base_layout.rb:113:in `context:'
    from tree_layout.rb:146:in `call_style_method:'
    from tree_layout.rb:158:in `block in reapply!:'
    from cocoa_util.rb:34:in `find_all_views:'
    from cocoa_util.rb:39:in `block in find_all_views:'
    from cocoa_util.rb:38:in `find_all_views:'
    from tree_layout.rb:157:in `reapply!:

By now I know my way around the motion kit code and it's clear (by simply grepping the code), that there is no reapply! definition in the code that does not take an argument.

That makes me a little suspicious, as it might possibly be a Rubymotion Bug rather than a MotionKit bug.

Ugly as it is, I never manage to create a consistent crash to build a test case for it.

Has anyone else ever encountered such behaviour?

Problem running AutoLayout example

I'm fairly new to rubymotion in general so I was exploring your samples and had some problems running the ios AutoLayout example. Here's the steps I took:

  1. Setup .ruby-version and .envrc in motion-kit/samples/ios/AutoLayout so my environment loads ruby 1.9.3.
  2. run bundle
  3. run rake

The following error pops up:

  Simulate ./build/iPhoneSimulator-8.0-Development/AutoLayout.app
2014-10-06 14:50:53.852 AutoLayout[76202:2454465] base_layout.rb:310:in `apply_with_target:': undefined method `setPrevious' for #<MotionKit::ConstraintsTarget:0xf427af0
...> (NoMethodError)
        from base_layout.rb:253:in `apply:'
        from base_layout.rb:176:in `method_missing:'
        from home_layout.rb:122:in `block in avatar_row_style'
        from base_layout.rb:116:in `context:'
        from base_layout.rb:95:in `context:'
        from uiview_constraints_helpers.rb:14:in `block in constraints:'
        from base_layout.rb:116:in `context:'
        from base_layout.rb:158:in `block in run_deferred:'
        from base_layout.rb:157:in `run_deferred:'
        from tree_layout.rb:455:in `build_view'
        from tree_layout.rb:71:in `view'
        from tree_layout.rb:76:in `build'
        from home_controller.rb:8:in `loadView'
2014-10-06 14:50:53.858 AutoLayout[76202:2454465] *** Terminating app due to uncaught exception 'NoMethodError', reason: 'base_layout.rb:310:in `apply_with_target:': unde
fined method `setPrevious' for #<MotionKit::ConstraintsTarget:0xf427af0 ...> (NoMethodError)

I've included the full log at the following gist: https://gist.github.com/lengarvey/6045a652a94a2503ab99 which also includes my .ruby-version and .envrc files to show you that they shouldn't be affecting the situation (as far as I know).

Thanks!

Thoughts on adding `_constraints` method dsl similar to `_style`

I find myself separating my constraints into their own functions a lot of times, just to keep things simpler and easier to read. And because, personally I have found that a lot of constraints aren't related to styles.

What do you think of adding the ability to do something like this:

class MyLayout < MK::Layout
  def layout
    root :main do
      add UIView, :background
    end
  end  

  def background_constraints
    left.equals(:superview)
    ....
  end

  def background_style
    background_color :orange.uicolor
  end
end

added elements not visible

Hey guys :)

I tried your gem and keep having the same issue.
I add a view in my layout and try to position it relative to superview.
As far as I understand the documentation, a root view should be created automatically and assigned as superview to any views I create.

but when I try to position my view relative to that root view, the app crashes as it can't get a frame from a nonexisting superview.

the same happens when I try to position a subview inside :test relative to something else that was defined on top level of the layout method.

https://gist.github.com/jhmegorei/2b0a6200b21542372601

Further info about my app:
pods:

  • FMDB
  • Reachability
  • FXBlurView
  • CAAnimation-EasingEquations
  • SwipeView

gems:

  • rake
  • motion-cocoapods
  • motion-testflight
  • dotenv
  • motion-require
  • bubble-wrap
  • motion_model (sql branch)
  • motion-kit

Sample app fails with "uninitialized constant MK"

AutoLayout ios sample app does not run correctly. It crashes with uninitialized constant MK.

I thought that the correct way to launch a sample app would be:

cd samples/ios/AutoLayout
rvm gemset use motion-kit-autolayout --create
bundle
rake

However, this doesn't seem to be true?

Idea: MK::PartialLayout

We often run into situations where our layouts get fairly large, especially when we're building views of variable count.

A very preliminary idea would be to allow partial layouts. Child layouts are okay, but forwarding events back to the screen gets pretty nasty, so partials would be better.

class MyLayout < MK::Layout
  def layout
    root :main do
      10.times do |i|
        partial MyPartial, { index: i, other_data: whatever }
      end
    end
  end
end

class MyPartial < MK::PartialLayout
  include MyPartialStyles # just a normal styles module

  attr_accessor :index
  attr_accessor :other_data

  def layout
    add UIControl, :some_control do
      add UIImageView, :some_image do
        image "some-image-#{index}".uiimage
      end
    end
  end
end

class MyViewController < UIViewController
  def viewDidLoad
    @layout = MyLayout.new(root: self.view).build
    @layout.get(:some_control).on :tap do
      # handle on_tap, even though it's originating in the partial
      # No complex forwarding
    end
  end
end

Thoughts?

setting a root element does not set @context

Hi Guys,

today I tried to set a custom root element (to insert a layout view as a subview in a non layouted view).
Doing that I always got the error

*** Terminating app due to uncaught exception 'MotionKit::NoContextError', reason: 'view_layout.rb:263:in `create_default_root_context': No top level view specified (missing outer 'create' method?) (MotionKit::NoContextError)

Digging through the code I found out that MotionKit::ViewLayout.root does not set the provided view as @context and thus the @context variable is empty when adding additional stuff via add.
I circumvented the bug in my code, setting the @context manually:

if parent_view
  Utilities.log("parent_view present: #{parent_view}")

  root(UIView, :root) do
    frame parent_view.frame
  end
  @context = self.get(:root)
end

This should be pretty easy to fix. I will try to create a pull request tomorrow, unless someone is faster ;)

Find out if view is already built

Moin Moin from Hamburg, Germany :)

Is there a way to ask a layout if its view has already been built?

Context: I use a layout where I define an MKMapView and when I deallocate the controller I have to deallocate the map manually (otherwise it will be retained and leaks). The Layout is rendered into a CollectionView Cell and when the cell containing the map has not yet been loaded, my code

@layout.get(:map)

crashes because the layout hasn't been built yet and lacks some initialization (setting a datasource for the pins to display).

So I want to deallocate the map only if its layout has been built.
I currently use @layout.instance_variable_get(:@view) to see if the view has been built, but I don't want my code to depend on an internal naming convention of rubymotion.

It should be quite easy to fix though:

def view?
  @view.present?
end

Readme font example

UIFont.fontWithName('Comic Sans', size: 24)

This returns nil for me, and so does not change the font. Perhaps the font name is incorrect?

:scale UIImage using constraints

Looks like I've run into something I can't get to work ...

For some background, I'm using constraints but can't seem to use :scale for a uiimage from within a constraints block

  def logo_style

    initial do
      add UILabel, :logo_label do
      end

      add UIImageView, :logo_image do
        @logo_image = image "logo.png".uiimage
        #clips_to_bounds false
      end

    end

    # frame constraints - make things a bit easier as we don't need dynamic positioning here
    constraints :logo_image do
      top_left.equals(:logo)
      width.equals(:logo)
      height :scale

    end


  end

  def logo_constraints(controller)
    constraints :logo do
      top_left.equals 20, 20
      width.equals(controller.view).minus(40)
      height.equals(controller.view).times(0.5)
    end
  end

With the height :scale set in the constraints, the image height is zero. Is there anyway to use constraints in this manner?

Setting variables and importing in motion-kit

Previously in Teacup, it was possible in a stylesheet to inherit attributes from another stylesheet. E.g.

Teacup::Stylesheet.new :app do
  @default_white = "#d0d0d0".uicolor
end

And in another stylesheet, you would be able to import that stylesheet and use the variables in them, or do extends in your styles.

Teacup::Stylesheet.new :another_stylesheet do
  import :app
  style :element,
    backgroundColor: @default_white
end

Is it possible to achieve the same thing using motion-kit?

Basic ProMotion Usage

This is likely just a question:

I'm trying to use MotionKit in conjunction with ProMotion and after some time of trial and error I seem to be unable to find a way to get it to work. It's probably just a confusion on my side:
All I want to do is add a layout to my PM::GroupedTableScreen. Since ProMotion is in charge of creating the root view I thought

def on_load
  @layout = MyLayout.new(root: self.view).build
end

would do the trick, but doing so raises

tree_layout.rb:84:in `root:': Already created the root view (MotionKit::ContextConflictError)

The layout class itself has nothing in it except an empty layout method.
If I name my root view as suggested in the docs:

def layout
  root :just_a_name do
    # nothing
  end
end

I get:

tree_layout.rb:269:in `create_default_root_context': No top level view specified (missing outer 'create' method?) (MotionKit::NoContextError)
    from base_layout.rb:42:in `target'
    from base_layout.rb:173:in `apply:'
    from base_layout.rb:156:in `method_missing:'
    from table_view_controller.rb:11:in `loadView'

base_layout.rb:175:in `apply:': undefined method `build' for #<MyLayout:0x9e8ca60>:MyLayout (NoMethodError)
    from base_layout.rb:156:in `method_missing:'
    from table_view_controller.rb:11:in `loadView'

What did I miss here? What didn't I grasp here? :-)

Thanks a lot,
Chris

Top corner radius?

Hello,

Is it possible to set corner radius on only for example the top corners?
Right now corner_radius, set the whole view.

Couple of questions

Hey there,

I was using teacup and now that I am refactoring my application I am trying to move my stuff over to motion-kit.
A couple of questions arise.

First of, when I install the gem, and try to compile the application, I get an error:

$ rake --trace
================================================================================
A new version of RubyMotion is available. Run `sudo motion update' to upgrade.
Your maintenance plan is about to expire in 15 days. Run `motion account' to renew it.
================================================================================

rake aborted!
ArgumentError: invalid byte sequence in US-ASCII
/Library/Ruby/Gems/2.0.0/gems/dbt-1.1.4/lib/dbt.rb:17:in `block (3 levels) in analyze'
/Library/Ruby/Gems/2.0.0/gems/dbt-1.1.4/lib/dbt.rb:14:in `each_line'
/Library/Ruby/Gems/2.0.0/gems/dbt-1.1.4/lib/dbt.rb:14:in `block (2 levels) in analyze'
/Library/Ruby/Gems/2.0.0/gems/dbt-1.1.4/lib/dbt.rb:13:in `open'
/Library/Ruby/Gems/2.0.0/gems/dbt-1.1.4/lib/dbt.rb:13:in `block in analyze'
/Library/Ruby/Gems/2.0.0/gems/dbt-1.1.4/lib/dbt.rb:12:in `each'
/Library/Ruby/Gems/2.0.0/gems/dbt-1.1.4/lib/dbt.rb:12:in `analyze'
/Library/Ruby/Gems/2.0.0/gems/motion-kit-0.10.0/lib/motion-kit.rb:33:in `block in <top (required)>'
/Library/Ruby/Gems/2.0.0/gems/bubble-wrap-1.6.0/lib/bubble-wrap/ext/motion_project_app.rb:12:in `call'
/Library/Ruby/Gems/2.0.0/gems/bubble-wrap-1.6.0/lib/bubble-wrap/ext/motion_project_app.rb:12:in `block in setup_with_bubblewrap'
/Library/RubyMotion/lib/motion/project/config.rb:113:in `call'
/Library/RubyMotion/lib/motion/project/config.rb:113:in `block in setup'
/Library/RubyMotion/lib/motion/project/config.rb:113:in `each'
/Library/RubyMotion/lib/motion/project/config.rb:113:in `setup'
/Library/RubyMotion/lib/motion/project/app.rb:66:in `config'
/Library/RubyMotion/lib/motion/project/app.rb:74:in `setup'
/Library/Ruby/Gems/2.0.0/gems/bubble-wrap-1.6.0/lib/bubble-wrap/ext/motion_project_app.rb:15:in `setup_with_bubblewrap'
/Users/koen/Projects/trapps/code/mobile/ios/test/Rakefile:12:in `<top (required)>'
/Library/Ruby/Gems/2.0.0/gems/rake-10.3.2/lib/rake/rake_module.rb:28:in `load'
/Library/Ruby/Gems/2.0.0/gems/rake-10.3.2/lib/rake/rake_module.rb:28:in `load_rakefile'
/Library/Ruby/Gems/2.0.0/gems/rake-10.3.2/lib/rake/application.rb:687:in `raw_load_rakefile'
/Library/Ruby/Gems/2.0.0/gems/rake-10.3.2/lib/rake/application.rb:94:in `block in load_rakefile'
/Library/Ruby/Gems/2.0.0/gems/rake-10.3.2/lib/rake/application.rb:176:in `standard_exception_handling'
/Library/Ruby/Gems/2.0.0/gems/rake-10.3.2/lib/rake/application.rb:93:in `load_rakefile'
/Library/Ruby/Gems/2.0.0/gems/rake-10.3.2/lib/rake/application.rb:77:in `block in run'
/Library/Ruby/Gems/2.0.0/gems/rake-10.3.2/lib/rake/application.rb:176:in `standard_exception_handling'
/Library/Ruby/Gems/2.0.0/gems/rake-10.3.2/lib/rake/application.rb:75:in `run'
/Library/Ruby/Gems/2.0.0/gems/rake-10.3.2/bin/rake:33:in `<top (required)>'
/usr/bin/rake:23:in `load'
/usr/bin/rake:23:in `<main>'

I guess this has to do with some file that dbt cannot recognise or something? how can I debug this further?

Another thing that is not quite clear to me is how I can set the delegate to the viewcontroller from my layout, an example for this would be nice =)

Thanks!

Unable to set contraints in custom view layouts

Hi,

I'm pulling my hairs on this one. I have a very simple project with a single controller and a single custom view with its associated layout.

controler layout:

motion_require 'timers_title_layout'

class TimersLayout < MK::Layout

  view :title
  #view :ticker
  #view :timers

  def layout
    add TimersTitleLayout, :title do
      constraints do
        top 0
        left 0
        height 60
        width.equals(self.view)
      end
    end
    #add DayTickerView, :ticker
    #add UITableView, :timers
  end

  def title_style
    background_color :green.uicolor
  end
end

custom layout:

motion_require '../views/timers_title_view'

class TimersTitleLayout < MK::UIViewLayout
  targets ::TimersTitleView

  view :label

  def layout
    add UILabel, :label do
      constraints do
        left 0
        top 20
        width.equals(self.view)
        height.equals(self.view).minus(20)
      end
    end
  end

  def label_style
    text 'Hello World'
    background_color :red.uicolor
  end
end

The contraints defined in the first layout are applied correctly but the constraints defined in the custom layout fail with the following error:

undefined method 'frame' for #<MotionKit::ConstraintsTarget:0xca7a160 ...> (NoMethodError)

I tried to pass self.view to contraints to no avail.

Any idea what I did wrong?

On a side note, width '100%' doesn't work for me, the generated constraint becomes

#<NSLayoutConstraint:#1084c150 firstItem=TimersTitleView(#117102e0, [[0.0, 0.0], [0.0, 60.0]]), child of UIView(#1170fba0) secondItem=nil priority=1000.0 formula="first.width == ">

MacBacon can't simulate tapping buttons added with motionkit's layout DSL

Here's a small proof-of-concept app that just adds 2 buttons to a view; one created and added to the view cocoa's plain old-fashioned way and one created and added to the view using motionkits layout DSL. If you rake and tap them, you see their output on the console, however if you rake spec, only the one added the old-fashioned way works (MacBacon's tap(motionkit_button) finds the button, but does not have the effect of tapping it). If you could take a look Colin, I'd really appreciate it. Thanks!

https://github.com/pachun/MotionKit-Button-with-MacBacon

RubyMotion 2.30 regression - NoMethodError - undefined method `setFromTopLeft:x:y::'

RubyMotion 2.30 seems to have introduced some regressions that MotionKit's test suite picks up on.

Reproducing steps

  1. Update to RubyMotion 2.30 sudo motion update
  2. Clone MK and run rake spec. 30-odd specs fail.
  3. Run sudo motion update --cache-version=2.29
  4. Change the top of the Rakefile to:
    $:.unshift("/Library/RubyMotion2.29/lib")
  5. Run rake spec. zero failures.

Error message:

[ERROR: NoMethodError - undefined method `setFromTopLeft:x:y::' for #<UIView:0x95f2e20>] - should support setting the frame via `from_top_left(view)`
BACKTRACE: NoMethodError: undefined method `setFromTopLeft:x:y::' for #<UIView:0x95f2e20>
    base_layout.rb:260:in `apply_with_target:': Frame helpers - should support setting the frame via `from_top_left(view)`
    base_layout.rb:197:in `apply:'
    base_layout.rb:258:in `apply_with_target:'
    base_layout.rb:197:in `apply:'
    base_layout.rb:157:in `method_missing:'
    base_layout.rb:97:in `context:'
    spec.rb:316:in `block in run_spec_block'
    spec.rb:440:in `execute_block'
    spec.rb:316:in `run_spec_block'
    spec.rb:331:in `run'

Not much we can do right now but I will report this to HipByte. In the meantime, a workaround is to downgrade to 2.29.

NoContextError when calling root in layout of nested subclass

I have a menu layout like this:

class MenuLayout < MK::Layout
  include MenuStyles

  def layout
    root :main do
      add UIView, :container do
        add MenuControl.new(title: "My Account"), :my_account do
          on :touch { trigger :swap_center, AccountScreen }
        end
        add MenuControl.new(title: "Sign Out"), :sign_out do
          on :touch { trigger :sign_out }
        end
      end
    end
  end
end

MenuControl a subclass of UIControl has it's own layout like this:

class MenuControlLayout < MK::Layout
  attr_accessor :title

  def layout
    root :menu_control do
      add UIView, :line
      add UIView, :knob
      add UILabel, :title_label
    end
  end
end

This causes a NoContextError which can be resolved by removing the call to root from the child layout. Is this expected behavior or is it a bug?

Warnings after update

I updated the gem and now i have this warnings:

2014-06-11 17:50:24.602 MyApp[89527:70b] Warning! The method MotionKit::TreeLayout#root has already been defined on MotionKit::BaseLayout or one of its ancestors.
2014-06-11 17:50:24.608 MyApp[89527:70b] Warning! The method MotionKit::ConstraintsLayout#right has already been defined on MotionKit::BaseLayout or one of its ancestors.
2014-06-11 17:50:24.610 MyApp[89527:70b] Warning! The method MotionKit::ConstraintsLayout#size has already been defined on MotionKit::BaseLayout or one of its ancestors.
2014-06-11 17:50:24.611 MyApp[89527:70b] Warning! The method MotionKit::ConstraintsLayout#center has already been defined on MotionKit::BaseLayout or one of its ancestors.

Everything works fine, anw.

NavigationController Layouts

I'm not sure if I'm on the right road here, but I'm trying to move my navigation_bar styles to a motion-kit layout. For some reason I'm getting errors when I try to do something like this:

I'd really like to move even navigation controller styles to motion-kit if possible

module Layout
  class NavBar < MotionKit::Layout

    def layout
      root :nav do

      end
    end

    def nav_style
      barStyle UIBarStyleBlack
    end
  end
end

class RootScreen < PM::Screen

  def on_load

    @navbar_layout = Layout::NavBar.new(nav: self.navigationController.navigationBar).build
  end
end

Travis build failing -- dbt issue

rake aborted!
invalid byte sequence in US-ASCII
/Users/travis/.rvm/gems/ruby-2.0.0-p353/gems/dbt-1.1.4/lib/dbt.rb:17:in `block (3 levels) in analyze'
/Users/travis/.rvm/gems/ruby-2.0.0-p353/gems/dbt-1.1.4/lib/dbt.rb:14:in `each_line'
/Users/travis/.rvm/gems/ruby-2.0.0-p353/gems/dbt-1.1.4/lib/dbt.rb:14:in `block (2 levels) in analyze'
/Users/travis/.rvm/gems/ruby-2.0.0-p353/gems/dbt-1.1.4/lib/dbt.rb:13:in `open'
/Users/travis/.rvm/gems/ruby-2.0.0-p353/gems/dbt-1.1.4/lib/dbt.rb:13:in `block in analyze'
/Users/travis/.rvm/gems/ruby-2.0.0-p353/gems/dbt-1.1.4/lib/dbt.rb:12:in `each'
/Users/travis/.rvm/gems/ruby-2.0.0-p353/gems/dbt-1.1.4/lib/dbt.rb:12:in `analyze'
/Users/travis/build/rubymotion/motion-kit/lib/motion-kit.rb:33:in `block in <top (required)>'
/Library/RubyMotion/lib/motion/project/config.rb:110:in `call'
/Library/RubyMotion/lib/motion/project/config.rb:110:in `block in setup'
/Library/RubyMotion/lib/motion/project/config.rb:110:in `each'
/Library/RubyMotion/lib/motion/project/config.rb:110:in `setup'
/Library/RubyMotion/lib/motion/project/app.rb:66:in `config'
/Library/RubyMotion/lib/motion/project/app.rb:74:in `setup'
/Users/travis/build/rubymotion/motion-kit/Rakefile:18:in `<top (required)>'
/Users/travis/.rvm/gems/ruby-2.0.0-p353/bin/ruby_executable_hooks:15:in `eval'
/Users/travis/.rvm/gems/ruby-2.0.0-p353/bin/ruby_executable_hooks:15:in `<main>'
(See full trace by running task with --trace)

https://travis-ci.org/rubymotion/motion-kit/builds/25831005

Any thoughts, @colinta?

Center frame of main window

Hi

I suppose there is a much more elegant way to center the main window than this ?

class WindowLayout < MK::WindowLayout
  def layout
    myframe=NSScreen.mainScreen.frame;
    largeur=400
    hauteur=200
    x=myframe.origin.x+myframe.size.width/2-largeur/2
    y=myframe.origin.y+myframe.size.height/2-hauteur/2
    myframe.size.width=largeur
    myframe.size.height=hauteur
    myframe.origin.x=x
    myframe.origin.y=y
   frame myframe

(The code given is the examples result in a frame of main window that is not centered)

    frame [[335, 390], [402, 114]]

Sorry if it's obvious

UITableViewCell unselectable

Hi,
I'm currently building a feed interface based on an UITableView.
The problem is that the whole view seems to go under my rootViewController declared in the app_delegate, I can't select it but I scroll it, strange.

Here is how I declared my layout:

  def layout
    add UITableView, :feed_view
  end

  def feed_view_style
    initWithFrame(UIScreen.mainScreen.bounds, style:UITableViewStyleGrouped)
    frame UIScreen.mainScreen.bounds #Crash when not precised, even with initWithFrame
    self.base_style

    separatorStyle UITableViewCellSeparatorStyleNone
    registerClass(UITableViewCell, forCellReuseIdentifier: CELL_ID)
  end

And here's my delegate:

  def tableView(table_view, cellForRowAtIndexPath: index_path) #this method should return the correct cell
    entry = @debug_data[index_path.section]
    cell = table_view.dequeueReusableCellWithIdentifier(CELL_ID)
      #background_color entry
    cell.backgroundView = initLabel(entry, :blue.uicolor)
    cell
  end #OK for cell generation

  def tableView(table_view, numberOfRowsInSection: section)
    1 # Comes here
  end

  def numberOfSectionsInTableView(table_view)
    3 # Comes here
  end

  def tableView(table_view, shouldHighlightRowAtIndexPath: index_path)
    LOGGER.debug("should highlight #{index_path.inspect} ?") #Never comes here
    true
  end

  def tableView(table_view, willSelectRowAtIndexPath: index_path)
    LOGGER.debug('Cell selected.') #Never comes here
  end

Thank you

Memory Leaks

Hiho everyone,

While using MotionKit Layouts heavily, we noticed, that the layouts seem to leak a lot.
To see this, use the sample project Chatty from the MotionKit Repo and deploy it on a device. Using Instruments' Leak Tool, we can see that every time we use a table entry to enter a chatroom and then return to the previous screen via navigation controller, the count of TableViews increases but never decreases.

I noticed the same in a small sample of my own. Just two controllers on the nav stack, the bottom one without layout and the top one with. Popping the controller with layout leaves the contents of the layout alive, while still deallocating the controller -> leak.

I am unfortunately not quite sure where to start on this issue, as I come from the rails world, where I rarely have to care about memory leaking. I will provide sample projects if necessary.

Dynamic Stuff

Hey,

I'm struggling to build some dynamic views. I have a button that I want to change as an IAP is in in process. Is there an easy way to either apply a new style to the view/button, or remove and then add a new button from the controller?

Crash after updating RubyMotion to 2.32

My layout has the following to style a UIButton:

  def start_button_style
    title 'Start'
    size_to_fit
    title_color "37A8A4".to_color, state: UIControlStateNormal
    title_color "37ffA4".to_color, state: UIControlStateHighlighted    
    font UIFont.fontWithName('Helvetica Neue', size: 25.0)
  end

I was working fine before updating RM to the latest version, but now I'm getting some errors that trace back to the base_layout.rb file from MotionKit:

2014-09-02 15:43:03.622 inLabor[83815:70b] base_layout.rb:179:in objc_version:': undefined method values' for 1:Fixnum (NoMethodError)

After some tinkering, I traced it back to the title_color lines in the code above. I can use either one by itself, but not the two lines at the same time.

Am I doing something wrong, or is this some kind of bug?

Thanks!

RubyMotion v2.30 causing problem?

I updated RubyMotion this morning and now I'm getting a motion-kit error I wasn't before...

2014-07-04 12:05:11.994] No top level view specified (missing outer 'create' method?) (MotionKit::NoContextError)
2014-07-04 12:05:12.033] undefined method `name' for #<MatchesLayout:0x14ffbd00>:MatchesLayout (NoMethodError)

My view controller has worked fine before updating and I'm stumped as to how to fix this error, as the error message doesn't give much in the way of remedying the issue. I've performed a rake clean and recompiled everything.

My view controller looks like:

class MatchesController < UIViewController

  def loadView
    @layout     = MatchesLayout.new
    self.view   = @layout.view

    @table_view = @layout.get(:table_view)
  end

  ...

And the layout controller looks like:

class MatchesLayout < MK::Layout
  view :table_view

  def layout
    @table_view = add UITableView, :table_view
  end

  ...

Any clue as to what could be going on? I've gone through my Git history to make sure I haven't made any changes that would cause this and this controller & layout have not been touched for a bit.

Any help would be appreciated! Thanks!

`title_label do` doesn't set properties on UIButton#titleLabel

I was expecting to be able to use the underscored version of button.titleLabel to work for setting styles on the button's label.

This doesn't work:

def button_style
  # ...    
  title_label do
    font rmq.font.button
  end
end

This works:

def button_style
  # ...    
  titleLabel do
    font rmq.font.button
  end
end

relative positioning brings relative sizing

While using relative positioning a lot, I encountered a (at least for me^^) quite counterintuitive behavior.

When I position an element with relative dimensions in relation to another (e.g. below), not only the origin of the frame is set in relation to the other element, but also the size.

Example:

class ExampleLayout < MotionKit::Layout
  def layout
    add UIView, :test1 do
      frame from_top down: 10, w: "100%", h: "50%"
      background_color UIColor.redColor
    end

    add UIView, :test2 do
      frame below :test1, w: "100%", h: "50%"
      background_color UIColor.blueColor
    end
  end
end

What I expected was that test1 and test2 have the same dimensions, with test2 being located directly below test1. Which in its essence means that the position of test2 is relative to test1 but the size of test1 is relative to the superview of both (rootview).

screen shot 2014-05-09 at 16 58 28

To get the result I want, I have to set the height of test2 to "100%", because it will set it to 100% of test1's height.

I probably brought this behavior in, as this happens exactly at the place I edited when fixing a crash with relational positioning.

But this time, it is not as easy to fix, because the question is, if the behavior I described above, is actually what was intended or not.

Perhaps it could be circumvented by allowing a separation of size.
Another solution would be, to define the size of the element in relation to the superview when relative width/height are used. But that might be counterintuitive for some people as well.

I hope the authors and other users of this gem will participate in finding a good solution for this problem :)

accessibility_label broken

Reproduced in minimal app here:

https://github.com/jamonholmgren/mk_issue_accessibility_label/blob/master/app/app_delegate.rb#L8

base_layout.rb:187:in `apply:': wrong number of arguments (1 for 0) (ArgumentError)
    from base_layout.rb:254:in `apply_with_target:'
    from base_layout.rb:193:in `apply:'
    from base_layout.rb:153:in `method_missing:'
    from app_delegate.rb:8:in `main_style'
    from tree_layout.rb:139:in `block in call_style_method:'
    from base_layout.rb:94:in `context:'
    from tree_layout.rb:138:in `call_style_method:'
    from tree_layout.rb:125:in `create:'
    from tree_layout.rb:111:in `root:'
    from app_delegate.rb:3:in `layout'
    from tree_layout.rb:300:in `build_view'
    from tree_layout.rb:56:in `view'
    from app_delegate.rb:17:in `loadView'
    from app_del:in `application:didFinishLaunchingWithOptions:'

HelloWorld crashes

$ rake debug=1
     Build ./build/iPhoneSimulator-7.1-Development
    Create ./build/iPhoneSimulator-7.1-Development/HelloWorld.app/Info.plist
  Simulate ./build/iPhoneSimulator-7.1-Development/HelloWorld.app
Executing commands in '/var/folders/9l/c4vhcf217lxcr_kvcgtl38940000gn/T/_simgdbcmds_ios'.
(lldb)  process attach -p 18405
Process 18405 stopped
Executable module set to "/usr/lib/dyld".
Architecture set to: i486-apple-macosx.
(lldb)  command script import /Library/RubyMotion/lldb/lldb.py
(lldb)  breakpoint set --name rb_exc_raise
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
(lldb)  breakpoint set --name malloc_error_break
Breakpoint 2: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
(lldb)  continue
Process 18405 resuming
Process 18405 stopped
1 location added to breakpoint 1
1 location added to breakpoint 2
(lldb) c
Process 18405 resuming
Process 18405 stopped
* thread #1: tid = 0x1d84e6, 0x002a149c HelloWorld`rb_vm_dispatch + 12, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0xbf7ff55c)
    frame #0: 0x002a149c HelloWorld`rb_vm_dispatch + 12
HelloWorld`rb_vm_dispatch + 12:
-> 0x2a149c:  calll  0x2a14a1                  ; rb_vm_dispatch + 17
   0x2a14a1:  popl   %ebx
   0x2a14a2:  movl   0x1c(%ebp), %edi
   0x2a14a5:  movl   0x18(%ebp), %edx

AutoLayout errors when selecting relative to others

... or I'm doing something wrong. Trying to position one button relative to another button:

class SigninLayout < MotionKit::WindowLayout

    def layout
      frame [[0, 0], [500, 400]]

      add NSButton, :cancel_button
      add NSButton, :ok_button
    end

    def cancel_button_style
      title 'Cancel'
      sizeToFit
      cell do
        bezelStyle NSRoundedBezelStyle
        alignment NSCenterTextAlignment
      end

      constraints do
        bottom_right.equals(:ok_button).minus(5)
      end
    end
  end
end

I'm aware that this would probably give a non-sensical position, but it throws the following error:

(main)> 2014-06-16 13:39:08.869 TimeAgent[16942:303] nswindow_layout.rb:41:in `get:': undefined method `contentView' for #<NSButton:0x7fb54a886380> (NoMethodError)
    from tree_layout.rb:202:in `get:'
    from constraint.rb:309:in `view_lookup:'
    from constraint.rb:735:in `resolve_all:'
    from constraints_target.rb:17:in `block in apply_all_constraints:'
    from constraints_target.rb:16:in `apply_all_constraints:'
    from nsview_layout_constraints.rb:15:in `block in constraints:'
    from base_layout.rb:94:in `context:'
    from base_layout.rb:135:in `block in run_deferred:'
    from base_layout.rb:134:in `run_deferred:'
    from tree_layout.rb:322:in `run_deferred:'
    from base_layout.rb:97:in `context:'
    from tree_layout.rb:138:in `call_style_method:'
    from tree_layout.rb:150:in `block in reapply!:'
    from cocoa_util.rb:34:in `find_all_views:'
    from cocoa_util.rb:39:in `block in find_all_views:'
    from cocoa_util.rb:38:in `find_all_views:'
    from tree_layout.rb:149:in `reapply!:'
    from nswindow_layout.rb:34:in `reapply!:'
    from signin_controller.rb:32:in `show:'
    from preferences_controller.rb:16:in `click_signin_button:'

How to implement controls that take parameters in their initializers?

For example, UISegmentedControl

    add(UISegmentedControl, :segmented_control)
    ...
    def segmented_control
      size [100,100]
      origin [50,50]
    end

This will add a segmented control on the view stack, but the frame is not correctly set (wrong initializer method called?), and I don't see any obvious way to pass in the items array.

Any tips?

Problem getting view to occupy entire screen height

Hi there, I'm probably doing something really dumb here, but I'm porting my app to use MotionKit and something that was working fine with Teacup, is presenting me with an infuriating UI glitch in MotionKit.

The layout is pretty simple. It's has a background image, a logo at the bottom of the panel, and a table view which is used for menu items. Here's the layout:

  class SidebarLayout < MK::Layout

    VPADDING     = 10
    HPADDING     = 10
    TMARGIN      = 30
    BMARGIN      = 8
    FIELD_HEIGHT = 35

    attr_accessor :menu_items_view

    def layout
      add UIView, :menu_items_container do
        background_color '#000000'.uicolor
        frame [[0,0], ['100%','100%']]
        add @menu_items_view, :menu_items_view do
          background_view UIImageView.alloc.initWithImage(UIImage.imageNamed('sidebar/nav_background'))
          # backgroundView.backgroundColor = UIColor.clearColor
          background_color UIColor.clearColor
          set_separator_inset UIEdgeInsetsZero
          set_separator_style UITableViewCellSeparatorStyleNone
          constraints do
            size.equals(:superview)
          end
        end
      end
      add UIImageView, :footer_image do
        image UIImage.imageNamed('sidebar/sidebar-logo')
        content_mode UIViewContentModeScaleAspectFit
        constraints do
          bottom.equals(:superview)
          left.equals(:menu_items_view).plus(HPADDING)
          height.equals(80)
          width.equals(150)
        end
      end
    end

  end # class SidebarLayour

The problem is that even though the :menu_items_container is set with a frame of [[0,0],['100%','100%']], it starts form about 20px down from the absolute top of the screen. When I look in Sugarcube's tree command, all of the frames are [0,0] so whatever is going on here must have something to do with another setting outside the layout of this particular view.

Also for the record, I'm using JASidePanel to coordinate a hamburger menu. The front panel (which is a just a table in this test) works fine. It's the back one that is not working properly.

I've attached a screenshot so you can see what's going on.

screen shot 2014-06-04 at 3 55 34 pm

Migration from TeaCup

Recently I've decided to move from TeaCup to MotionKit, but README is missing some kind of migration guide.

What I want to do is to dynamically manipulate views, add subviews, change styles and so on.

With TeaCup I could do anytime:

  • subview(UIView, :email_contacts_view) - to add any subview (even nested)
  • layout(@done_button, :done_button_disabled) - to change style

and more complex solutions.

How to achieve such things in MotionKit?

Retain Layout Class of Child Layouts

Hi everyone,

I am currently implementing a layout which contains several reusable parts (for example a button grid), which are Layouts under the hood.

an example from my code:

class ItemLayout < MotionKitLayout
...
def layout
    add A3ParallaxScrollView, :parallax_view do
      add UIImageView, :background
      add UIView, :foreground do
        add MetadataOverviewLayout.alloc.initWithItem(item), :metadata_overview
        add ActionButtonGridLayout.alloc.initWithItem(item), :action_buttons
        add MemoTitleLayout.alloc.initWithItem(item), :memo_title
        add DeletionLayout.alloc.initWithItem(item), :delete_button
      end
    end
  end

My problem is, that the views generated by this layout go into a swipe view, which, like a collection view, reuses their views.
So I want to set a new item on my ItemLayout and then call reapply, which should either should cascade the reapply down to the childlayouts or I could do it manually in the reapply block of my metadata_overview_style methods.

Looking through the code, it shouldn't be too hard to implement.
initialize_element would need the element_id as second parameter and then could save the layout to a new instance variable:

def initialize_element(elem, elem_id)
      if elem.is_a?(Class) && elem < TreeLayout
        layout_id = "#{elem_id.to_s}_layout".to_sym if elem_id.present?
        layout = elem.new_child(@layout, nil, self)
        @child_layouts[layout_id] = layout if elem_id.present?
        layout.view
      elsif elem.is_a?(Class)
        elem = elem.new
      elsif elem.is_a?(TreeLayout)
        layout_id = "#{elem_id.to_s}_layout".to_sym if elem_id.present?
        @child_layouts[layout_id] = elem if elem_id.present?
        elem = elem.view
      end
      return elem
end

(Disclaimer: just typed the code from the top of my head, so no guarantees ^^)

Would this feature be attractive to the motionkit community?

Trouble with height(:scale) constraint

# if you are using a view that has a sensible intrinsic size, like an image,
# you can use :scale to have the width or height adjusted according to the
# other size
width.equals(:superview)
height(:scale)  # scale the height according to the width

Crashing when I add this height(:scale) constraint. Getting the following when I run rake.

*** Assertion failure in -[NSLayoutConstraint _setMultiplier:], /SourceCache/Foundation_Sim/Foundation-1140.11/Layout.subproj/NSLayoutConstraint.m:151
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Multiplier is not finite! That's illegal. multiplier:nan'

I can post more if it helps.

I have tried both:

width.equals(:superview)
height(:scale)

and

width.equals(:superview)
height :scale

^^^ This one is from Adds :scale option to width and height constraints #73

Any ideas? I am running the most recent versions of both RM and MK.

Linear/Grid Layout

I wanted to continue the discussion of laying out subviews according to columns, tables, grids, or "typewriter" layout.

I am especially interested in grid or typewriter layouts, because I am hoping to support both iPhone and iPad from the same codebase, and the iPad will often use grids. Also, I'm trying to avoid the complexity of UITableView and UICollectionView when cell-reuse isn't needed.

I tried the proposed example for a vertical layout:

module MotionKit
  class UIViewLayout

  # inside this block, calls to 'row' will add a row to a container.  After the
  # block is called, constraints are added to the rows so that they stack
  # vertically.
  def vertical_layout(&block)
    # because MotionKit relies on delegation, it's important to store "state"
    # in the motion_kit_meta object
    rows_was = target.motion_kit_meta[:motionkit_rows]
    target.motion_kit_meta[:motionkit_rows] = []

    container = add(UIView, &block)
    rows = target.motion_kit_meta[:motionkit_rows]
    prev_row = nil

    rows.each do |row|
      context(row) do
        constraints do
          if prev_row
            top.equals(prev_row, :bottom)
          else
            top.equals(container, :top)
          end
        end
      end
      prev_row = row
    end
    target.motion_kit_meta[:motionkit_rows] = rows_was

    rows
  end

  # a shortcoming of this method, I couldn't think of any other way to set an
  # explicit height.  If you're using auto-layout *everywhere* we might be able
  # to use intrinsic sizes, but that gets complicated...
  def row(height, &block)
    row = add(UIView, &block)
    context(row) do
      constraints do
        width.is == superview.frame.size.width
        height.is == height
      end
    end
    target.motion_kit_meta[:motionkit_rows] << row

    row
  end

end

This throws an exception because in row, target is a different object than it is in vertical_layout, so target.motion_kit_meta[:motionkit_rows] isn't set.

Thanks!

MotionKit + ProMotion

I'm trying to create custom cell with ProMotion, but if I using simple MK::Layout and I want to add constraints to cell it tells me I have no superview and _style notation also don't work(only if i add styling through block).

class MessageCell < PM::TableViewCell
  def setup(data_cell, table_screen)
    super
    @layout = MessageCellLayout.new
    self.contentView.addSubview @layout.view
    @layout.label.text = data_cell[:arguments][:message].body
    self
  end
end

I tried to create custom layout, but it create MessageCell inside MessageCell. Also with that i need to rethink how to calculate dynamic height.

class MessageCell < PM::TableViewCell; end # without this code can't find constant
class CustomCellLayout < MK::Layout
  targets MessageCell
end

class MessageCellLayout < CustomCellLayout
    view :label

    def layout
      backgroundColor UIColor.grayColor
      @label = add(UILabel, :label) do
        backgroundColor UIColor.blackColor
        numberOfLines 0
        font UIFont.boldSystemFontOfSize(20)
        textColor UIColor.blackColor
        shadowColor UIColor.whiteColor
        textAlignment UITextAlignmentLeft
        frame [[0, 0], [320, '100%']]
      end
    end
end

Sorry if that's question doesn't apply to motion-kit, I'm new to autolayout and rubymotion.

UIButton color

I've just started my first project using this gem and noticed that if I create a button without setting a text color, the button's text is white. Shouldn't it have the typical blue UIButton color if I don't set anything?

Could this happen when no action is set for the button? By the way, whats the correct MotionKit way to set a button action? I couldn't find it in the readme. Thanks!

question: setting images on buttons for multiple states

Can you explain how one can set the image for a button for multiple states given the following example?

non working example

 def layout
    root :item do
      add UIButton, :favorite_button
      add UIImageView, :profile_image
      add UILabel, :item_name
    end
  end

  def favorite_button_style
    title 'fav'
    image '726-star', UIControlStateNormal
    image '726-star-selected', UIControlStateSelected
    background_color UIColor.redColor
    constraints do

      above(:item_name)
      right_of(:item_name).minus(10)
      width 40
      height 30
    end
  end

Cannot declare `right` in the `frame below()` style method.

I have a style that works correctly if it's declared like this

    frame below(:header, down: 15)
    right '100%-20'

but if I try to combine them into

    frame below(:header, down: 15, right: '100%-20')

it crashes with

*** Terminating app due to uncaught exception 'TypeError', reason: 'calculate.rb:121:in `calculate_origin:': String can't be coerced into Float (TypeError)
    from calculate.rb:214:in `calculate_frame:'
    from calculate.rb:13:in `calculate:'
    from uiview_layout_frame.rb:110:in `_calculate_frame:from:relative_to:'
    from uiview_layout_frame.rb:282:in `below:'
    from base_layout.rb:185:in `apply:'
    from base_layout.rb:151:in `method_missing:'

NSPanel - can't use "frame"

I'm building an OSX app using MotionKit, and I would like it to layout an NSPanel just the way it does an NSWindow.

This code gives me an error:

  class SigninLayout < MotionKit::WindowLayout

    def layout
      root(NSPanel) do
        frame [[335, 390], [340, 139]]
      end
    end

  end

Error:

(main)> 2014-06-16 11:52:47.547 TimeAgent[3537:303] base_layout.rb:247:in `apply_with_target:': wrong number of arguments (1 for 0) (ArgumentError)
    from base_layout.rb:273:in `block in delegate_method_fix:'
    from base_layout.rb:187:in `apply:'
    from base_layout.rb:277:in `block in delegate_method_fix:'
    from signin_layout.rb:6:in `block in layout'
    from base_layout.rb:94:in `context:'
    from tree_layout.rb:129:in `create:'
    from tree_layout.rb:108:in `block in root:'
    from base_layout.rb:94:in `context:'
    from tree_layout.rb:105:in `root:'
    from signin_layout.rb:5:in `layout'
    from tree_layout.rb:300:in `build_view'
    from tree_layout.rb:56:in `view'
    from nswindow_layout.rb:9:in `window'
    from signin_controller.rb:6:in `block in init'
    from signin_controller.rb:4:in `init'
    from preferences_controller.rb:25:in `signin_sheet'
    from preferences_controller.rb:17:in `click_signin_button:'

I've also tried extending MK::WindowLayout with adding targets NSPanel, and using that instead of MK::WindowLayout, but it gives me the same error.

Am I doing something wrong, or is there a bug?

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.