Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
M

motion-kit

> 编程语言
Open source

The RubyMotion layout and styling gem. Follow @MotionKit on twitter for updates and commit notifications

315 stars0 likes0 views
WebsiteGitHub

About

The RubyMotion layout and styling gem. Follow @MotionKit on twitter for updates and commit notifications

MotionKit

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:
    • UIKit
    • AppKit
    • AutoLayout
    • Frame geometry
    • CoreAnimation
    • NSMenu/NSMenuItem
  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.

…

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  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! :smiley:

  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  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 = { x: 5, y: 10 }
  top_left.is =
  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.

…

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.

…

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  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  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
    fram

Issues· 14 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Ruby

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 18, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言