motion-layouts 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ .rake_tasks~
2
+ build/
3
+ *.gem
4
+ *.rbc
5
+ .bundle
6
+ .config
7
+ .yardoc
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
20
+ .vim_librarian
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in motion-layouts.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ LICENCE
2
+
3
+ MIT: http://robmalko.mit-license.org
4
+
5
+ ------------------------------------------------
6
+
7
+ The MIT License (MIT)
8
+ Copyright © 2012 Rob Malko <rob.malko@gmail.com>
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the “Software”), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in
18
+ all copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ THE SOFTWARE.
@@ -0,0 +1,153 @@
1
+ # Layouts for RubyMotion
2
+
3
+ A DSL for creating layouts easily in RubyMotion. Also comes bundled with a
4
+ set of categories to make life easier. I'm using the word category from
5
+ objective-c land which is basically the same as re-opening classes in ruby :D.
6
+
7
+ ## Getting started
8
+
9
+ Add motion-layouts as a git submodule of your RubyMotion project:
10
+
11
+ git submodule add https://github.com/malkomalko/motion-layouts.git vendor/motion-layouts
12
+
13
+ Add the motion-layouts lib path to your project 'Rakefile'
14
+
15
+ ```ruby
16
+ Motion::Project::App.setup do |app|
17
+ app.name = 'myapp'
18
+ app.files.unshift(Dir.glob(File.join(app.project_dir, 'vendor/motion-layouts/lib/**/*.rb')))
19
+ end
20
+ ```
21
+
22
+ Now, you can use motion-layouts to start making some layouts.
23
+
24
+ I put all my layouts by convention into app/layouts but feel free to do
25
+ whatever you want.
26
+
27
+ ## Define a layout
28
+
29
+ ```ruby
30
+ class NameEditorLayout
31
+ include Layouts::Base
32
+
33
+ def self.template
34
+ UIToolbar {
35
+ anchor 'top'
36
+ height 50
37
+ resize :top, :right, :left, :width
38
+ items [
39
+ ['Cancel', 'cancel'],
40
+ [:flexible_space],
41
+ ['Done', 'done']
42
+ ]
43
+ }
44
+ UITextField {
45
+ id 'nameTextField'
46
+ delegate @controller
47
+ top 90
48
+ width 85.percent
49
+ align 'center'
50
+ text_color '222222'
51
+ background_color 'FFFFFF'
52
+ border_style 'rounded'
53
+ resize :top, :right, :left, :width
54
+ placeholder 'Enter the photo album name'
55
+ }
56
+ end
57
+ end
58
+ ```
59
+
60
+ You start by including Layouts::Base and defining a self.template method.
61
+
62
+ ## Instantiate your view (from controller)
63
+
64
+ ```ruby
65
+ def viewWillAppear(animated)
66
+ super
67
+ view.fromLayout(NameEditorLayout, self)
68
+ end
69
+ ```
70
+
71
+ boom.. that's it, you should see a toolbar and a text field in your view.
72
+
73
+ ## How it works
74
+
75
+ The project includes a mixture of categories and nodes.
76
+
77
+ Nodes are the entry point inside self.template in your layout:
78
+
79
+ ```ruby
80
+ class NameEditorLayout
81
+ include Layouts::Base
82
+
83
+ def self.template
84
+ UIToolbar {
85
+ ...
86
+ }
87
+ UITextField {
88
+ ...
89
+ }
90
+ end
91
+ end
92
+ ```
93
+
94
+ Every node inherits from LayoutBase which sets up a lot of shared functionality
95
+ and handles proper instantiation.
96
+
97
+ You have access to a few instance variables inside each node:
98
+
99
+ ```
100
+ @parent - the parent view
101
+ @view - the current view
102
+ @controller - the controller who instantiated the view via view.fromLayout
103
+ ```
104
+
105
+ Every node can also set a defaults hash.
106
+
107
+ Let's take a look at the UITextField node:
108
+
109
+ ```ruby
110
+ module Layouts
111
+ class UITextField < LayoutBase
112
+ def self.defaults
113
+ {
114
+ width: @parent.bounds.size.width * 0.90,
115
+ height: 30
116
+ }
117
+ end
118
+
119
+ def border_style(style)
120
+ @view.borderStyle = ::UITextField::BORDER_STYLES.fetchWithDefault(style)
121
+ end
122
+
123
+ def placeholder(text)
124
+ @view.placeholder = text
125
+ end
126
+ end
127
+ end
128
+ ```
129
+
130
+ This is where the categories come in. To make defining these nodes as easy
131
+ as possible, I'm creating a collection of categories to make the process
132
+ as smooth as can be.
133
+
134
+ Take a look inside the lib/layouts/categories folder to see some of the
135
+ helpers I've defined for you.
136
+
137
+ ## Todo
138
+
139
+ Tests Tests Tests. This was mostly thrown together very quickly as a POC,
140
+ but there is nothing complex going on here.
141
+
142
+ Filling out a complete set of nodes. I'm throwing this out now in hopes that
143
+ people can create wrapper nodes for all the missing standard UI classes.
144
+
145
+ ## Thanks
146
+
147
+ Quick thanks to https://github.com/mattetti/BubbleWrap for letting me gut
148
+ their README.md and for suggesting a rather nice convention for installing
149
+ custom libs into the vendor directory until something else better comes
150
+ along.
151
+
152
+ Also, thanks to Laurent and the whole RubyMotion community for making iOS
153
+ programming fun to learn.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
File without changes
@@ -0,0 +1,48 @@
1
+ module Layouts
2
+ module Base
3
+ def initialize(view)
4
+ @view = view
5
+ end
6
+
7
+ def view
8
+ @view
9
+ end
10
+
11
+ def self.included(base)
12
+ base.extend(ClassMethods)
13
+ end
14
+
15
+ module ClassMethods
16
+ def layout(view, controller)
17
+ @instance = self.new(view)
18
+ @controller = controller
19
+ self.template
20
+ @views
21
+ end
22
+
23
+ def controller
24
+ @controller
25
+ end
26
+
27
+ def instance
28
+ @instance
29
+ end
30
+
31
+ def method_missing(klass, *args, &block)
32
+ build_layout_for(klass, *args, &block)
33
+ end
34
+
35
+ def build_layout_for(klass, *args, &block)
36
+ begin
37
+ layout = Layouts.const_get(klass)
38
+ @views ||= []
39
+ current_view = layout.alloc(self, klass, controller)
40
+ @views.push(current_view)
41
+ rescue NameError
42
+ return
43
+ end
44
+ layout.new(self, current_view, controller, &block)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,11 @@
1
+ class ExpectingHashParameter < StandardError
2
+ def initialize(msg='First parameter must be a hash')
3
+ super
4
+ end
5
+ end
6
+
7
+ class MissingViewException < StandardError
8
+ def initialize(msg='Please include a :view class')
9
+ super
10
+ end
11
+ end
@@ -0,0 +1,110 @@
1
+ module Layouts
2
+ class LayoutBase
3
+ def self.method_missing(klass, *args, &block);end
4
+
5
+ def self.alloc(template, klass, controller)
6
+ klass = Module.const_get(klass)
7
+ @parent = template.instance.view
8
+ @controller = controller
9
+ _defaults = base_defaults.merge(defaults)
10
+ origin = [0, 0]
11
+ size = [_defaults[:width], _defaults[:height]]
12
+
13
+ klass.alloc.initWithFrame [origin, size]
14
+ end
15
+
16
+ def self.base_defaults
17
+ {
18
+ width: @parent.bounds.size.width,
19
+ height: 50
20
+ }
21
+ end
22
+
23
+ def self.defaults
24
+ {}
25
+ end
26
+
27
+ def initialize(template, view, controller, &block)
28
+ @parent = template.instance.view
29
+ @view = view
30
+ @controller = controller
31
+ @args = self.class.base_defaults
32
+ instance_eval &block
33
+ end
34
+
35
+ def id(id)
36
+ @controller.class.send(:attr_accessor, id)
37
+ @controller.instance_variable_set "@#{id}", @view
38
+ end
39
+
40
+ def height(height=50)
41
+ height = height.is_a?(Float) ?
42
+ @parent.bounds.size.height * height : height
43
+ @args[:height] = height
44
+ @view.frame = update_dimensions h: height
45
+ anchor('bottom') if @args[:anchor] == 'bottom'
46
+ end
47
+
48
+ def width(width=50)
49
+ width = width.is_a?(Float) ?
50
+ @parent.bounds.size.width * width : width
51
+ @args[:width] = width
52
+ @view.frame = update_dimensions w: width
53
+ end
54
+
55
+ def left(left=0)
56
+ left = left.is_a?(Float) ?
57
+ @parent.bounds.size.width * left : left
58
+ @args[:left] = left
59
+ @view.frame = update_dimensions x: left
60
+ end
61
+
62
+ def top(top=0)
63
+ top = top.is_a?(Float) ?
64
+ @parent.bounds.size.height * top : top
65
+ @args[:top] = top
66
+ @view.frame = update_dimensions y: top
67
+ end
68
+
69
+ def align(align='left')
70
+ width_left = @parent.bounds.size.width - @view.frame.size.width
71
+
72
+ case align
73
+ when 'left'
74
+ @view.frame = update_dimensions x: 0
75
+ when 'center'
76
+ @view.frame = update_dimensions x: width_left / 2
77
+ when 'right'
78
+ @view.frame = update_dimensions x: width_left
79
+ end
80
+ end
81
+
82
+ def background_color(color='FFFFFF')
83
+ @view.backgroundColor = Color.from_hex(color)
84
+ end
85
+
86
+ def text_color(color='FFFFFF')
87
+ if @view.respond_to?(:textColor)
88
+ @view.textColor = Color.from_hex(color)
89
+ end
90
+ end
91
+
92
+ def delegate(instance)
93
+ if @view.respond_to?(:delegate) && !instance.nil?
94
+ @view.delegate = instance
95
+ end
96
+ end
97
+
98
+ def resize(*masks)
99
+ @view.resizeMask(*masks)
100
+ end
101
+
102
+ def update_dimensions(opts={})
103
+ x = opts.fetch(:x, @view.frame.origin.x)
104
+ y = opts.fetch(:y, @view.frame.origin.y)
105
+ w = opts.fetch(:w, @view.frame.size.width)
106
+ h = opts.fetch(:h, @view.frame.size.height)
107
+ [[x, y], [w, h]]
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,56 @@
1
+ class UIBarButtonItem
2
+ BUTTON_TYPES = {
3
+ default: UIBarButtonSystemItemDone,
4
+ done: UIBarButtonSystemItemDone,
5
+ cancel: UIBarButtonSystemItemCancel,
6
+ edit: UIBarButtonSystemItemEdit,
7
+ save: UIBarButtonSystemItemSave,
8
+ add: UIBarButtonSystemItemAdd,
9
+ flexible_space: UIBarButtonSystemItemFlexibleSpace,
10
+ fixed_space: UIBarButtonSystemItemFixedSpace,
11
+ compose: UIBarButtonSystemItemCompose,
12
+ reply: UIBarButtonSystemItemReply,
13
+ action: UIBarButtonSystemItemAction,
14
+ organize: UIBarButtonSystemItemOrganize,
15
+ bookmarks: UIBarButtonSystemItemBookmarks,
16
+ search: UIBarButtonSystemItemSearch,
17
+ refresh: UIBarButtonSystemItemRefresh,
18
+ stop: UIBarButtonSystemItemStop,
19
+ camera: UIBarButtonSystemItemCamera,
20
+ trash: UIBarButtonSystemItemTrash,
21
+ play: UIBarButtonSystemItemPlay,
22
+ pause: UIBarButtonSystemItemPause,
23
+ rewind: UIBarButtonSystemItemRewind,
24
+ fast_forward: UIBarButtonSystemItemFastForward,
25
+ undo: UIBarButtonSystemItemUndo,
26
+ redo: UIBarButtonSystemItemRedo,
27
+ page_curl: UIBarButtonSystemItemPageCurl
28
+ }
29
+
30
+ BUTTON_STYLES = {
31
+ default: UIBarButtonItemStyleBordered,
32
+ border: UIBarButtonItemStyleBordered,
33
+ plain: UIBarButtonItemStylePlain,
34
+ done: UIBarButtonItemStyleDone
35
+ }
36
+
37
+ def self.createTitle(title, target, action, style=:default)
38
+ style = BUTTON_STYLES.fetchWithDefault(style)
39
+ self.alloc.initWithTitle(title, style:style, target:target, action:action)
40
+ end
41
+
42
+ def self.createType(button, target, action)
43
+ button = BUTTON_TYPES.fetchWithDefault(button)
44
+ self.alloc.initWithBarButtonSystemItem(button, target:target, action:action)
45
+ end
46
+
47
+ def self.fixedSpace(width=20)
48
+ space = self.createType(:fixed_space, nil, nil)
49
+ space.width = width
50
+ space
51
+ end
52
+
53
+ def self.flexibleSpace
54
+ self.createType(:flexible_space, nil, nil)
55
+ end
56
+ end
@@ -0,0 +1,7 @@
1
+ class Color
2
+ def self.from_hex(color='FFFFFF')
3
+ color = color.length == 6 ? color : 'FFFFFF'
4
+ r, g, b = [color[0..1].to_i(16), color[2..3].to_i(16), color[4..5].to_i(16)]
5
+ UIColor.colorWithRed(r/255.0,green:g/255.0,blue:b/255.0,alpha:1)
6
+ end
7
+ end
@@ -0,0 +1,37 @@
1
+ class UIViewController
2
+ PRESENTATION_STYLES = {
3
+ default: UIModalPresentationFormSheet,
4
+ full: UIModalPresentationFullScreen,
5
+ page: UIModalPresentationPageSheet,
6
+ form: UIModalPresentationFormSheet,
7
+ current: UIModalPresentationCurrentContext
8
+ }
9
+
10
+ TRANSITION_STYLES = {
11
+ default: UIModalTransitionStyleCoverVertical,
12
+ vertical: UIModalTransitionStyleCoverVertical,
13
+ flip: UIModalTransitionStyleFlipHorizontal,
14
+ dissolve: UIModalTransitionStyleCrossDissolve,
15
+ curl: UIModalTransitionStylePartialCurl
16
+ }
17
+
18
+ def createModal(opts={})
19
+ raise ExpectingHashParameter unless opts.is_a?(Hash)
20
+ klass = opts.fetch(:view, nil)
21
+ raise MissingViewException if klass.nil?
22
+
23
+ presentation = PRESENTATION_STYLES.fetchWithDefault(opts[:style])
24
+ transition = TRANSITION_STYLES.fetchWithDefault(opts[:transition])
25
+
26
+ modal = klass.alloc.init
27
+ modal.delegate = self
28
+ modal.modalPresentationStyle = presentation
29
+ modal.modalTransitionStyle = transition
30
+
31
+ def modal.show
32
+ self.delegate.presentModalViewController(self, animated:true)
33
+ end
34
+
35
+ modal
36
+ end
37
+ end
@@ -0,0 +1,39 @@
1
+ module Kernel
2
+ def delegateTo(selector)
3
+ if self.respond_to?(:delegate) && !self.delegate.nil?
4
+ self.delegate.send(selector) if self.delegate.respond_to?(selector)
5
+ end
6
+ end
7
+ end
8
+
9
+ class Hash
10
+ def fetchWithDefault(key)
11
+ self.fetch(key, self[:default])
12
+ end
13
+ end
14
+
15
+ class Numeric
16
+ def percent
17
+ self.to_f / 100.0
18
+ end
19
+ end
20
+
21
+ class NSOrderedSet
22
+ def push(val)
23
+ self.addObject(val)
24
+ end
25
+
26
+ def size
27
+ self.count
28
+ end
29
+
30
+ def [](index)
31
+ self.objectAtIndex(index)
32
+ end
33
+ end
34
+
35
+ class NSMutableOrderedSet
36
+ def delete_at(index)
37
+ self.removeObjectAtIndex(index)
38
+ end
39
+ end
@@ -0,0 +1,46 @@
1
+ class UITextField
2
+ BORDER_STYLES = {
3
+ default: UITextBorderStyleRoundedRect,
4
+ none: UITextBorderStyleNone,
5
+ line: UITextBorderStyleLine,
6
+ bezel: UITextBorderStyleBezel,
7
+ rounded: UITextBorderStyleRoundedRect
8
+ }
9
+ end
10
+
11
+ class UITextInputTraits
12
+ KEYBOARD_TYPES = {
13
+ default: UIKeyboardTypeDefault,
14
+ ascii: UIKeyboardTypeASCIICapable,
15
+ numbers: UIKeyboardTypeNumbersAndPunctuation,
16
+ url: UIKeyboardTypeURL,
17
+ number_pad: UIKeyboardTypeNumberPad,
18
+ phone_pad: UIKeyboardTypePhonePad,
19
+ name_phone_pad: UIKeyboardTypeNamePhonePad,
20
+ email: UIKeyboardTypeEmailAddress,
21
+ decimal_pad: UIKeyboardTypeDecimalPad,
22
+ twitter: UIKeyboardTypeTwitter
23
+ }
24
+
25
+ RETURN_KEY_TYPES = {
26
+ default: UIReturnKeyDefault,
27
+ go: UIReturnKeyGo,
28
+ google: UIReturnKeyGoogle,
29
+ join: UIReturnKeyJoin,
30
+ next: UIReturnKeyNext,
31
+ route: UIReturnKeyRoute,
32
+ search: UIReturnKeySearch,
33
+ send: UIReturnKeySend,
34
+ yahoo: UIReturnKeyYahoo,
35
+ done: UIReturnKeyDone,
36
+ emergency: UIReturnKeyEmergencyCall
37
+ }
38
+
39
+ CAPITALIZATION_STYLES = {
40
+ default: UITextAutocapitalizationTypeNone,
41
+ none: UITextAutocapitalizationTypeNone,
42
+ words: UITextAutocapitalizationTypeWords,
43
+ sentences: UITextAutocapitalizationTypeSentences,
44
+ all: UITextAutocapitalizationTypeAllCharacters
45
+ }
46
+ end
@@ -0,0 +1,53 @@
1
+ class UITableView
2
+ def deleteRows(rows)
3
+ self.deleteRowsAtIndexPaths(rows,
4
+ withRowAnimation:UITableViewRowAnimationFade)
5
+ end
6
+
7
+ def selectRow(row)
8
+ self.selectRowAtIndexPath(
9
+ NSIndexPath.indexPathForRow(row, inSection:row),
10
+ animated:false,
11
+ scrollPosition:UITableViewScrollPositionMiddle)
12
+ end
13
+ end
14
+
15
+ class UITableViewCell
16
+ CID = 'CELL'
17
+
18
+ STYLES = {
19
+ default: UITableViewCellStyleDefault,
20
+ subtitle: UITableViewCellStyleSubtitle,
21
+ value1: UITableViewCellStyleValue1,
22
+ value2: UITableViewCellStyleValue2
23
+ }
24
+
25
+ ACCESSORIES = {
26
+ default: UITableViewCellAccessoryNone,
27
+ disclosure: UITableViewCellAccessoryDisclosureIndicator,
28
+ detail: UITableViewCellAccessoryDetailDisclosureButton,
29
+ checkmark: UITableViewCellAccessoryCheckmark
30
+ }
31
+
32
+ SELECT_STYLES = {
33
+ default: UITableViewCellSelectionStyleBlue,
34
+ blue: UITableViewCellSelectionStyleBlue,
35
+ gray: UITableViewCellSelectionStyleGray,
36
+ none: UITableViewCellSelectionStyleNone
37
+ }
38
+
39
+ def self.display(tableView, opts={})
40
+ style = STYLES.fetchWithDefault(opts[:style])
41
+ accessoryType = ACCESSORIES.fetchWithDefault(opts[:accessory])
42
+ editAccessory = ACCESSORIES.fetchWithDefault(opts[:edit_accessory])
43
+ selectionStyle = SELECT_STYLES.fetchWithDefault(opts[:selection])
44
+
45
+ tableView.dequeueReusableCellWithIdentifier(CID) || begin
46
+ cell = self.alloc.initWithStyle(style, reuseIdentifier:CID)
47
+ cell.accessoryType = accessoryType
48
+ cell.editingAccessoryType = editAccessory
49
+ cell.selectionStyle = selectionStyle
50
+ cell
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,11 @@
1
+ class UIToolbar
2
+ def self.createAtTop(view, height=50)
3
+ self.alloc.initWithFrame([[0,0], [view.bounds.size.width,height]])
4
+ end
5
+
6
+ def self.createAtBottom(view, height=50)
7
+ size = view.bounds.size
8
+ bottom = size.height - height
9
+ self.alloc.initWithFrame([[0,bottom], [size.width,height]])
10
+ end
11
+ end
@@ -0,0 +1,91 @@
1
+ class UIView
2
+ RESIZE_MASKS = {
3
+ none: UIViewAutoresizingNone,
4
+ top: UIViewAutoresizingFlexibleBottomMargin,
5
+ right: UIViewAutoresizingFlexibleLeftMargin,
6
+ bottom: UIViewAutoresizingFlexibleTopMargin,
7
+ left: UIViewAutoresizingFlexibleRightMargin,
8
+ width: UIViewAutoresizingFlexibleWidth,
9
+ height: UIViewAutoresizingFlexibleHeight
10
+ }
11
+
12
+ def resizeMask(*masks)
13
+ self.autoresizingMask = RESIZE_MASKS[:none] and return self if masks.include?(:none)
14
+ self.autoresizingMask = masks.reduce(0) {|memo,mask| memo | RESIZE_MASKS.fetch(mask, 0)}
15
+ self
16
+ end
17
+
18
+ def resizeAll
19
+ self.resizeMask :top, :right, :bottom, :left, :width, :height
20
+ end
21
+
22
+ def fromLayout(klass, controller)
23
+ _views_ = klass.layout(self, controller)
24
+ _views_.each {|_view_| self.addSubview _view_}
25
+ end
26
+
27
+ def initWithParent(parent)
28
+ @parent = parent
29
+ self.initWithFrame [[0,0],[0,0]]
30
+ end
31
+ end
32
+
33
+ module LayoutHelper
34
+ def height(height=50)
35
+ height = height.is_a?(Float) ?
36
+ @parent.bounds.size.height * height : height
37
+ self.frame = update_dimensions h: height
38
+ end
39
+
40
+ def width(width=50)
41
+ width = width.is_a?(Float) ?
42
+ @parent.bounds.size.width * width : width
43
+ self.frame = update_dimensions w: width
44
+ end
45
+
46
+ def left(left=0)
47
+ left = left.is_a?(Float) ?
48
+ @parent.bounds.size.width * left : left
49
+ self.frame = update_dimensions x: left
50
+ end
51
+
52
+ def top(top=0)
53
+ top = top.is_a?(Float) ?
54
+ @parent.bounds.size.height * top : top
55
+ self.frame = update_dimensions y: top
56
+ end
57
+
58
+ def align(align='left')
59
+ width_left = @parent.bounds.size.width - self.frame.size.width
60
+
61
+ case align
62
+ when 'left'
63
+ self.frame = self.update_dimensions x: 0
64
+ when 'center'
65
+ self.frame = self.update_dimensions x: width_left / 2
66
+ when 'right'
67
+ self.frame = self.update_dimensions x: width_left
68
+ end
69
+ end
70
+
71
+ def verticalAlign(align='top')
72
+ height_left = @parent.bounds.size.height - self.frame.size.height
73
+
74
+ case align
75
+ when 'top'
76
+ self.frame = self.update_dimensions y: 0
77
+ when 'center'
78
+ self.frame = self.update_dimensions y: height_left / 2
79
+ when 'bottom'
80
+ self.frame = self.update_dimensions y: height_left
81
+ end
82
+ end
83
+
84
+ def update_dimensions(opts={})
85
+ x = opts.fetch(:x, self.frame.origin.x)
86
+ y = opts.fetch(:y, self.frame.origin.y)
87
+ w = opts.fetch(:w, self.frame.size.width)
88
+ h = opts.fetch(:h, self.frame.size.height)
89
+ [[x, y], [w, h]]
90
+ end
91
+ end
@@ -0,0 +1,34 @@
1
+ module Layouts
2
+ class UITextField < LayoutBase
3
+ def self.defaults
4
+ {
5
+ width: @parent.bounds.size.width * 0.90,
6
+ height: 30
7
+ }
8
+ end
9
+
10
+ def border_style(style)
11
+ @view.borderStyle = ::UITextField::BORDER_STYLES.fetchWithDefault(style)
12
+ end
13
+
14
+ def placeholder(text)
15
+ @view.placeholder = text
16
+ end
17
+
18
+ def keyboard_type(type)
19
+ @view.keyboardType = ::UITextInputTraits::KEYBOARD_TYPES.fetchWithDefault(type)
20
+ end
21
+
22
+ def return_key_type(type)
23
+ @view.returnKeyType = ::UITextInputTraits::RETURN_KEY_TYPES.fetchWithDefault(type)
24
+ end
25
+
26
+ def secure(bool)
27
+ @view.setSecureTextEntry bool
28
+ end
29
+
30
+ def capitalize(style)
31
+ @view.autocapitalizationType = ::UITextInputTraits::CAPITALIZATION_STYLES.fetchWithDefault(style)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ module Layouts
2
+ class UIToolbar < LayoutBase
3
+ def self.defaults
4
+ {
5
+ height: 50
6
+ }
7
+ end
8
+
9
+ def anchor(type)
10
+ type ||= 'top'
11
+ @args[:anchor] = type
12
+ bottom = @parent.bounds.size.height - @args[:height]
13
+ @view.frame = update_dimensions y: type == 'bottom' ? bottom : 0
14
+ end
15
+
16
+ def items(toolbar_items)
17
+ items = toolbar_items.map do |item|
18
+ if item[0].is_a?(String)
19
+ ::UIBarButtonItem.createTitle(item[0], @controller, item[1])
20
+ elsif item[0].is_a?(Symbol)
21
+ ::UIBarButtonItem.createType(item[0], @controller, item[1])
22
+ end
23
+ end.compact
24
+ @view.items = items
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "#{__FILE__} must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+ Motion::Project::App.setup do |app|
6
+ root = File.expand_path('../..', __FILE__)
7
+ files = Dir.glob(File.join(root, 'lib/**/*.rb')).reject{ |f| f == __FILE__ }
8
+ files.each do |file|
9
+ app.files.unshift(file)
10
+ end
11
+
12
+ app.files_dependencies File.join(root, 'lib/layouts/nodes/ui_text_field.rb') => File.join(root, 'lib/layout_base.rb')
13
+ app.files_dependencies File.join(root, 'lib/layouts/nodes/ui_tool_bar.rb') => File.join(root, 'lib/layout_base.rb')
14
+ end
@@ -0,0 +1,5 @@
1
+ module Motion
2
+ module Layouts
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/motion-layouts/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["malkomalko (Robert Malko)"]
6
+ gem.email = ["robmalko@gmail.com"]
7
+ gem.description = %q{A DSL for creating layouts easily in RubyMotion. Also comes bundled with a set of categories to make life easier. I'm using the word category from objective-c land which is basically the same as re-opening classes in ruby :D.}
8
+ gem.summary = %q{A DSL for creating layouts easily in RubyMotion}
9
+ gem.homepage = "https://github.com/malkomalko/motion-layouts"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "motion-layouts"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Motion::Layouts::VERSION
17
+ end
File without changes
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: motion-layouts
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - malkomalko (Robert Malko)
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-09-17 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: A DSL for creating layouts easily in RubyMotion. Also comes bundled with a set of categories to make life easier. I'm using the word category from objective-c land which is basically the same as re-opening classes in ruby :D.
18
+ email:
19
+ - robmalko@gmail.com
20
+ executables: []
21
+
22
+ extensions: []
23
+
24
+ extra_rdoc_files: []
25
+
26
+ files:
27
+ - .gitignore
28
+ - Gemfile
29
+ - LICENSE
30
+ - README.md
31
+ - Rakefile
32
+ - lib/.gitignore
33
+ - lib/base.rb
34
+ - lib/errors.rb
35
+ - lib/layout_base.rb
36
+ - lib/layouts/categories/buttons.rb
37
+ - lib/layouts/categories/colors.rb
38
+ - lib/layouts/categories/controllers.rb
39
+ - lib/layouts/categories/core.rb
40
+ - lib/layouts/categories/forms.rb
41
+ - lib/layouts/categories/table_views.rb
42
+ - lib/layouts/categories/tool_bars.rb
43
+ - lib/layouts/categories/views.rb
44
+ - lib/layouts/nodes/ui_text_field.rb
45
+ - lib/layouts/nodes/ui_tool_bar.rb
46
+ - lib/motion-layouts.rb
47
+ - lib/motion-layouts/version.rb
48
+ - motion-layouts.gemspec
49
+ - spec/.gitignore
50
+ has_rdoc: true
51
+ homepage: https://github.com/malkomalko/motion-layouts
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.6.2
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: A DSL for creating layouts easily in RubyMotion
78
+ test_files:
79
+ - spec/.gitignore