Class: MotionKit::BaseLayout
- Inherits:
-
Object
show all
- Extended by:
- BaseLayoutClassMethods
- Defined in:
- lib/motion-kit/layouts/base_layout.rb,
lib/motion-kit-ios/layouts/layout_device.rb,
lib/motion-kit-cocoa/layouts/sugarcube_compat.rb
Overview
Abstract base class, responsible for "registration" of layout classes with
the class targets method.
Very few methods are defined on BaseLayout, and any unknown methods are
delegated to the 'apply' method, which accepts a method name, arguments, and
an optional block to set the new context.
The ViewLayout subclass defines methods that are appropriate for adding and
removing views to a view hierarchy.
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
-
#add_deferred_block(layout, &block) ⇒ Object
Only intended for private use.
-
#apply(method_name, *args, &block) ⇒ Object
Tries to call the setter (foo 'value' => view.setFoo('value')), or assignment method (foo 'value' => view.foo = 'value'), or if a block is given, then the object returned by 'method_name' is assigned as the new context, and the block is executed.
-
#apply_with_context(method_name, *args, &block) ⇒ Object
-
#apply_with_target(method_name, *args) ⇒ Object
-
#center(*values) ⇒ Object
-
#context(target, &block) ⇒ Object
Runs a block of code with a new object as the 'context'.
-
#deferred(&block) ⇒ Object
Blocks passed to deferred are run at the end of a "session", usually after a call to Layout#layout.
-
#deferred_blocks ⇒ Object
Only intended for private use.
-
#down(*values) ⇒ Object
-
#frame(*values) ⇒ Object
-
#initialize ⇒ BaseLayout
constructor
A new instance of BaseLayout.
-
#ipad? ⇒ Boolean
-
#iphone35? ⇒ Boolean
-
#iphone4? ⇒ Boolean
-
#iphone? ⇒ Boolean
-
#left(*values) ⇒ Object
-
#method_missing(method_name, *args, &block) ⇒ Object
Methods that style the view start out as missing methods.
-
#origin(*values) ⇒ Object
-
#retina? ⇒ Boolean
-
#right(*values) ⇒ Object
-
#run_deferred(top_level_context) ⇒ Object
Only intended for private use.
-
#set_layout(layout) ⇒ Object
-
#size(*values) ⇒ Object
-
#target ⇒ Object
-
#up(*values) ⇒ Object
-
#v ⇒ Object
layout_for, memoize, new_child, target_klasses, targets
Constructor Details
Returns a new instance of BaseLayout.
19
20
21
22
23
24
25
26
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 19
def initialize
@layout = self
@layout_delegate = nil
@layout_state = :initial
end
|
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &block) ⇒ Object
Methods that style the view start out as missing methods. This just calls
'apply', which searches for the method in the delegate
(@layout_delegate) or using inspection (respond_to?(:setFoo)).
150
151
152
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 150
def method_missing(method_name, *args, &block)
self.apply(method_name, *args, &block)
end
|
Instance Attribute Details
Returns the value of attribute parent.
17
18
19
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 17
def parent
@parent
end
|
Class Method Details
.method_added(method_name) ⇒ Object
this last little "catch-all" method is helpful to warn against methods
that are defined already. Since magic methods are so important, this
warning can come in handy.
282
283
284
285
286
287
288
289
290
291
292
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 282
def method_added(method_name)
return if @allow_all_override
if self < BaseLayout && BaseLayout.method_defined?(method_name)
if overridden_methods.include?(method_name)
overridden_methods.delete(method_name)
else
NSLog("Warning! The method #{self.name}##{method_name} has already been defined on MotionKit::BaseLayout or one of its ancestors.")
end
end
end
|
.overridden_methods ⇒ Object
275
276
277
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 275
def overridden_methods
@overridden_methods ||= []
end
|
.override_start ⇒ Object
263
264
265
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 263
def override_start
@allow_all_override = true
end
|
.override_stop ⇒ Object
267
268
269
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 267
def override_stop
@allow_all_override = false
end
|
.overrides(method_name) ⇒ Object
271
272
273
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 271
def overrides(method_name)
overridden_methods << method_name
end
|
Instance Method Details
#add_deferred_block(layout, &block) ⇒ Object
Only intended for private use
113
114
115
116
117
118
119
120
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 113
def add_deferred_block(layout, &block)
raise InvalidDeferredError.new('deferred must be run inside of a context') if @is_top_level.nil?
raise ArgumentError.new('Block required') unless block
self.deferred_blocks << [@context, block]
self
end
|
#apply(method_name, *args, &block) ⇒ Object
Tries to call the setter (foo 'value' => view.setFoo('value')), or
assignment method (foo 'value' => view.foo = 'value'), or if a block
is given, then the object returned by 'method_name' is assigned as the new
context, and the block is executed.
You can call this method directly, but usually it is called via
method_missing.
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 161
def apply(method_name, *args, &block)
method_name = method_name.to_s
raise ApplyError.new("Cannot apply #{method_name.inspect} to instance of #{target.class.name}") if method_name.length == 0
begin
target = self.target
rescue NoContextError => e
raise NoMethodError.new(method_name)
end
if args.length == 2 && args[1].is_a?(Hash) && !args[1].empty?
long_method_name = "#{method_name}:#{args[1].keys.join(':')}:"
long_method_args = [args[0]].concat args[1].values
else
long_method_name = nil
long_method_args = nil
end
@layout_delegate ||= Layout.layout_for(@layout, target.class)
if long_method_name && @layout_delegate.respond_to?(long_method_name)
return @layout_delegate.send(long_method_name, *long_method_args, &block)
elsif @layout_delegate.respond_to?(method_name)
return @layout_delegate.send(method_name, *args, &block)
end
if block
apply_with_context(method_name, *args, &block)
else
apply_with_target(method_name, *args)
end
end
|
#apply_with_context(method_name, *args, &block) ⇒ Object
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 195
def apply_with_context(method_name, *args, &block)
if args.length == 2 && args[1].is_a?(Hash) && !args[1].empty?
long_method_name = "#{method_name}:#{args[1].keys.join(':')}:"
long_method_args = [args[0]].concat args[1].values
else
long_method_name = nil
long_method_args = nil
end
if long_method_name && target.respond_to?(long_method_name)
new_context = target.send(long_method_name, *long_method_args)
self.context(new_context, &block)
elsif target.respond_to?(method_name)
new_context = target.send(method_name, *args)
self.context(new_context, &block)
elsif method_name.include?('_')
objc_name = MotionKit.objective_c_method_name(method_name)
self.apply(objc_name, *args, &block)
else
raise ApplyError.new("Cannot apply #{method_name.inspect} to instance of #{target.class.name}")
end
end
|
#apply_with_target(method_name, *args) ⇒ Object
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 218
def apply_with_target(method_name, *args)
setter = MotionKit.setter(method_name)
assign = "#{method_name}="
if args.length == 2 && args[1].is_a?(Hash) && !args[1].empty?
long_method_name = "#{method_name}:#{args[1].keys.join(':')}:"
long_method_args = [args[0]].concat args[1].values
else
long_method_name = nil
long_method_args = nil
end
if long_method_name && target.respond_to?(long_method_name)
target.send(long_method_name, *long_method_args)
elsif args.empty? && target.respond_to?(method_name)
target.send(method_name, *args)
elsif target.respond_to?(setter)
target.send(setter, *args)
elsif target.respond_to?(assign)
target.send(assign, *args)
elsif target.respond_to?(method_name)
target.send(method_name, *args)
elsif target.is_a?(MotionKit.appearance_class)
target.send(setter, *args)
elsif method_name.include?('_')
objc_name = MotionKit.objective_c_method_name(method_name)
self.apply(objc_name, *args)
else
target.send(setter, *args)
end
end
|
#center(*values) ⇒ Object
33
34
35
|
# File 'lib/motion-kit-cocoa/layouts/sugarcube_compat.rb', line 33
def center(*values)
apply(:center, *values)
end
|
#context(target, &block) ⇒ Object
Runs a block of code with a new object as the 'context'. Methods from the
Layout classes are applied to this target object, and missing methods are
delegated to a new Layout instance that is created based on the new
context.
This method is part of the public API, you can pass in any object to have
it become the 'context'.
Example:
def table_view_style
content = target.contentView
if content
context(content) do
background_color UIColor.clearColor
end
end
# usually you use 'context' automatically via method_missing, by
# passing a block to a method that returns an object. That object becomes
# the new context.
layer do
# self is now a CALayerLayout instance
corner_radius 5
end
end
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 70
def context(target, &block)
return target unless block
return @layout.context(target, &block) if @layout != self
if target.is_a?(Symbol)
target = self.get(target)
end
context_was, parent_was, delegate_was = @context, @parent, @layout_delegate
was_top_level = @is_top_level
if @is_top_level.nil?
@is_top_level = true
else
@is_top_level = false
end
@parent = MK::Parent.new(context_was)
@context = target
@context.motion_kit_meta[:delegate] ||= Layout.layout_for(@layout, @context.class)
@layout_delegate = @context.motion_kit_meta[:delegate]
yield
@layout_delegate, @context, @parent = delegate_was, context_was, parent_was
if @is_top_level
run_deferred(target)
end
@is_top_level = was_top_level
target
end
|
#deferred(&block) ⇒ Object
Blocks passed to deferred are run at the end of a "session", usually
after a call to Layout#layout.
104
105
106
107
108
109
110
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 104
def deferred(&block)
if @layout != self
return @layout.add_deferred_block(self, &block)
else
return self.add_deferred_block(self, &block)
end
end
|
#deferred_blocks ⇒ Object
Only intended for private use
123
124
125
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 123
def deferred_blocks
@deferred_blocks ||= []
end
|
#down(*values) ⇒ Object
21
22
23
|
# File 'lib/motion-kit-cocoa/layouts/sugarcube_compat.rb', line 21
def down(*values)
apply(:down, *values)
end
|
#frame(*values) ⇒ Object
5
6
7
|
# File 'lib/motion-kit-cocoa/layouts/sugarcube_compat.rb', line 5
def frame(*values)
apply(:frame, *values)
end
|
#ipad? ⇒ Boolean
17
18
19
|
# File 'lib/motion-kit-ios/layouts/layout_device.rb', line 17
def ipad?
UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad
end
|
#iphone35? ⇒ Boolean
13
14
15
|
# File 'lib/motion-kit-ios/layouts/layout_device.rb', line 13
def iphone35?
iphone? && UIScreen.mainScreen.bounds.size.width == 320 && UIScreen.mainScreen.bounds.size.height == 480
end
|
#iphone4? ⇒ Boolean
9
10
11
|
# File 'lib/motion-kit-ios/layouts/layout_device.rb', line 9
def iphone4?
iphone? && UIScreen.mainScreen.bounds.size.width == 320 && UIScreen.mainScreen.bounds.size.height == 568
end
|
#iphone? ⇒ Boolean
5
6
7
|
# File 'lib/motion-kit-ios/layouts/layout_device.rb', line 5
def iphone?
UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone
end
|
#left(*values) ⇒ Object
9
10
11
|
# File 'lib/motion-kit-cocoa/layouts/sugarcube_compat.rb', line 9
def left(*values)
apply(:left, *values)
end
|
#origin(*values) ⇒ Object
25
26
27
|
# File 'lib/motion-kit-cocoa/layouts/sugarcube_compat.rb', line 25
def origin(*values)
apply(:origin, *values)
end
|
#retina? ⇒ Boolean
21
22
23
|
# File 'lib/motion-kit-ios/layouts/layout_device.rb', line 21
def retina?
UIScreen.mainScreen.respond_to?(:scale) && UIScreen.mainScreen.scale == 2
end
|
#right(*values) ⇒ Object
13
14
15
|
# File 'lib/motion-kit-cocoa/layouts/sugarcube_compat.rb', line 13
def right(*values)
apply(:right, *values)
end
|
#run_deferred(top_level_context) ⇒ Object
Only intended for private use
128
129
130
131
132
133
134
135
136
137
138
139
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 128
def run_deferred(top_level_context)
deferred_blocks = self.deferred_blocks
@deferred_blocks = nil
deferred_blocks.each do |target, block|
context(target, &block)
end
if @deferred_blocks
run_deferred(top_level_context)
end
end
|
#set_layout(layout) ⇒ Object
28
29
30
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 28
def set_layout(layout)
@layout = layout && WeakRef.new(layout)
end
|
#size(*values) ⇒ Object
29
30
31
|
# File 'lib/motion-kit-cocoa/layouts/sugarcube_compat.rb', line 29
def size(*values)
apply(:size, *values)
end
|
32
33
34
35
36
37
38
39
40
41
42
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 32
def target
if @layout.nil? || @layout == self
@context ||= create_default_root_context
else
@layout.target
end
end
|
#up(*values) ⇒ Object
17
18
19
|
# File 'lib/motion-kit-cocoa/layouts/sugarcube_compat.rb', line 17
def up(*values)
apply(:up, *values)
end
|
43
|
# File 'lib/motion-kit/layouts/base_layout.rb', line 43
def v ; target ; end
|