Class: MotionPrime::Section

Inherits:
Object
  • Object
show all
Includes:
DelegateMixin, DrawSectionMixin, HasAuthorization, HasClassFactory, HasNormalizer, MotionSupport::Callbacks
Defined in:
motion-prime/sections/base_section.rb

Constant Summary collapse

KEYBOARD_HEIGHT_PORTRAIT =

Basic Sample

class MySection < MotionPrime::Section

element :title, text: "Hello World"
element :avatar, type: :image, image: 'defaults/avatar.jpg'

end

216
KEYBOARD_HEIGHT_LANDSCAPE =
162
DEFAULT_CONTENT_HEIGHT =
65

Instance Attribute Summary collapse

Attributes included from DrawSectionMixin

#cached_draw_image, #container_element, #container_gesture_recognizers

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DelegateMixin

#clear_delegated, #delegated_by

Methods included from DrawSectionMixin

#before_element_render, #bind_gesture_on_container_for, #clear_gesture_for_receiver, #draw_in, #prerender_elements_for_state, #prerender_enabled?

Methods included from SectionWithContainerMixin

#container_view, #init_container_element, #load_container_with_elements

Methods included from FrameCalculatorMixin

#calculate_frame_for

Methods included from HasStyles

#prepare_gradient

Methods included from HasClassFactory

#camelize_factory, #class_factory, #low_camelize_factory, #underscore_factory

Methods included from HasNormalizer

#debug_info, #element?, #normalize_object, #normalize_options, #normalize_value

Methods included from HasAuthorization

#api_client, #current_user, #reset_current_user, #user_signed_in?

Constructor Details

#initialize(options = {}) ⇒ Section

Returns a new instance of Section.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'motion-prime/sections/base_section.rb', line 28

def initialize(options = {})
  @options = options

  run_callbacks :initialize do
    @options[:screen] = @options[:screen].try(:weak_ref)
    self.screen = options[:screen]
    @model = options[:model]
    @name = options[:name] ||= default_name
    @options_block = options[:block]
  end

  if Prime.env.development?
    @_section_info = "#{@name} #{screen.try(:class)}"
    @@_allocated_sections ||= []
    @@_allocated_sections << @_section_info
  end
end

Instance Attribute Details

#elementsObject

Returns the value of attribute elements.



24
25
26
# File 'motion-prime/sections/base_section.rb', line 24

def elements
  @elements
end

#modelObject

Returns the value of attribute model.



24
25
26
# File 'motion-prime/sections/base_section.rb', line 24

def model
  @model
end

#nameObject

Returns the value of attribute name.



24
25
26
# File 'motion-prime/sections/base_section.rb', line 24

def name
  @name
end

#optionsObject

Returns the value of attribute options.



24
25
26
# File 'motion-prime/sections/base_section.rb', line 24

def options
  @options
end

#screenObject

Returns the value of attribute screen.



24
25
26
# File 'motion-prime/sections/base_section.rb', line 24

def screen
  @screen
end

#section_stylesObject

Returns the value of attribute section_styles.



24
25
26
# File 'motion-prime/sections/base_section.rb', line 24

def section_styles
  @section_styles
end

Class Method Details

.after_element_render(element_name, method, options = {}) ⇒ Object



422
423
424
425
426
427
# File 'motion-prime/sections/base_section.rb', line 422

def after_element_render(element_name, method, options = {})
  options.merge!(method: method)
  self.elements_callbacks ||= {}
  self.elements_callbacks[element_name] ||= []
  self.elements_callbacks[element_name] << options
end

.after_initialize(*method_names, &block) ⇒ Object



419
420
421
# File 'motion-prime/sections/base_section.rb', line 419

def after_initialize(*method_names, &block)
  set_callback :initialize, :after, *method_names, &block
end

.after_render(*method_names, &block) ⇒ Object



413
414
415
# File 'motion-prime/sections/base_section.rb', line 413

def after_render(*method_names, &block)
  set_callback :render, :after, *method_names, &block
end

.before_initialize(*method_names, &block) ⇒ Object



416
417
418
# File 'motion-prime/sections/base_section.rb', line 416

def before_initialize(*method_names, &block)
  set_callback :initialize, :before, *method_names, &block
end

.before_render(*method_names, &block) ⇒ Object



410
411
412
# File 'motion-prime/sections/base_section.rb', line 410

def before_render(*method_names, &block)
  set_callback :render, :before, *method_names, &block
end

.bind_keyboard_close(options) ⇒ Object



428
429
430
# File 'motion-prime/sections/base_section.rb', line 428

def bind_keyboard_close(options)
  self.keyboard_close_bindings = options
end

.container(options) ⇒ Object



407
408
409
# File 'motion-prime/sections/base_section.rb', line 407

def container(options)
  self.container_options = options
end

.element(name, options = {}, &block) ⇒ Object



399
400
401
402
403
404
405
406
# File 'motion-prime/sections/base_section.rb', line 399

def element(name, options = {}, &block)
  options[:name] ||= name
  options[:type] ||= :label
  options[:block] = block
  self.elements_options ||= {}
  self.elements_options[name] = options
  self.elements_options[name]
end

.inherited(subclass) ⇒ Object



392
393
394
395
396
397
# File 'motion-prime/sections/base_section.rb', line 392

def inherited(subclass)
  subclass.elements_callbacks = self.elements_callbacks.try(:clone)
  subclass.elements_options = self.elements_options.try(:clone)
  subclass.container_options = self.container_options.try(:clone)
  subclass.keyboard_close_bindings = self.keyboard_close_bindings.try(:clone)
end

Instance Method Details

#add_element(key, options = {}) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'motion-prime/sections/base_section.rb', line 180

def add_element(key, options = {})
  return unless render_element?(key)
  opts = options.clone
  index = opts.delete(:at)
  options = build_options_for_element(opts)
  options[:name] ||= key
  element = build_element(options)
  if index
    new_elements_array = elements.to_a.insert(index, [key, element])
    self.elements = Hash[new_elements_array]
  else
    self.elements[key] = element
  end
  element
end

#after_element_render(element) ⇒ Object



216
217
218
219
220
221
222
# File 'motion-prime/sections/base_section.rb', line 216

def after_element_render(element)
  super
  return unless callbacks = elements_callbacks.try(:[], element.name)
  callbacks.each do |options|
    options[:method].to_proc.call(options[:target] || self)
  end
end

#bind_keyboard_eventsObject



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'motion-prime/sections/base_section.rb', line 270

def bind_keyboard_events
  NSNotificationCenter.defaultCenter.addObserver self,
                                     selector: :on_keyboard_show,
                                         name: UIKeyboardDidShowNotification,
                                       object: nil
  NSNotificationCenter.defaultCenter.addObserver self,
                                     selector: :on_keyboard_hide,
                                         name: UIKeyboardDidHideNotification,
                                       object: nil
  NSNotificationCenter.defaultCenter.addObserver self,
                                     selector: :keyboard_will_show,
                                         name: UIKeyboardWillShowNotification,
                                       object: nil
  NSNotificationCenter.defaultCenter.addObserver self,
                                     selector: :keyboard_will_hide,
                                         name: UIKeyboardWillHideNotification,
                                       object: nil
end

#bring_to_frontObject

Bring all views of section to front. It will bring to front all base elements and container of draw elements. FIXME: container_view manipulation should be in draw mixin.



256
257
258
259
260
261
262
263
# File 'motion-prime/sections/base_section.rb', line 256

def bring_to_front
  if container_view
    container_view.superview.bringSubviewToFront container_view
  end
  elements_to_render.values.each do |element|
    element.view.superview.bringSubviewToFront element.view
  end
end

#container_boundsObject



60
61
62
# File 'motion-prime/sections/base_section.rb', line 60

def container_bounds
  options[:container_bounds] or raise "You must pass `container bounds` option to prerender base section"
end

#container_heightObject

Get computed container height

Examples:

class MySection < Prime::Section
  container height: proc { element(:title).content_outer_height }
  element :title, text: 'Hello world'
end
section = MySection.new
section.container_height # => 46


87
88
89
# File 'motion-prime/sections/base_section.rb', line 87

def container_height
  container_options[:height] || DEFAULT_CONTENT_HEIGHT
end

#container_optionsObject

Get computed container options



71
72
73
74
# File 'motion-prime/sections/base_section.rb', line 71

def container_options
  compute_container_options! unless @container_options
  @container_options
end

#create_elementsObject

Create elements if they are not created yet. This will not cause rendering elements, they will be rendered immediately after that or rendered async later, based on type of section.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'motion-prime/sections/base_section.rb', line 122

def create_elements
  return if @section_loaded
  if @section_loading
    sleep 0.1
    return @section_loaded ? false : create_elements
  end
  @section_loading = true

  self.elements = {}
  elements_options.each do |key, opts|
    add_element(key, opts)
  end
  elements_eval(&@options_block) if @options_block.is_a?(Proc)

  @section_loading = false
  return @section_loaded = true
end

#current_input_view_heightObject



307
308
309
# File 'motion-prime/sections/base_section.rb', line 307

def current_input_view_height
  App.shared.windows.last.subviews.first.try(:height) || KEYBOARD_HEIGHT_PORTRAIT
end

#deallocObject



46
47
48
49
50
51
52
53
54
# File 'motion-prime/sections/base_section.rb', line 46

def dealloc
  if Prime.env.development?
    index = @@_allocated_sections.index(@_section_info)
    @@_allocated_sections.delete_at(index)
  end
  Prime.logger.dealloc_message :section, self, self.name
  NSNotificationCenter.defaultCenter.removeObserver self # unbinding events created in bind_keyboard_events
  super
end

#default_nameObject

Get section default name, based on class name

Examples:

class ProfileSection < Prime::Section
end

section = ProfileSection.new
section.default_name # => 'profile'
section.name         # => 'profile'

another_section = ProfileSection.new(name: 'another')
another_section.default_name # => 'profile'
another_section.name         # => 'another'


106
107
108
# File 'motion-prime/sections/base_section.rb', line 106

def default_name
  underscore_factory(self.class_name_without_kvo.demodulize).gsub(/\_section$/, '')
end

#element(name) ⇒ Object



224
225
226
227
# File 'motion-prime/sections/base_section.rb', line 224

def element(name)
  self.elements ||= {}
  self.elements[name.to_sym]
end

#elements_optionsObject

Get section elements options, where the key is element name.



113
114
115
# File 'motion-prime/sections/base_section.rb', line 113

def elements_options
  self.class.elements_options || {}
end

#elements_to_drawObject



299
300
301
# File 'motion-prime/sections/base_section.rb', line 299

def elements_to_draw
  self.elements.select { |key, element| element.is_a?(DrawElement) }
end

#elements_to_renderObject



303
304
305
# File 'motion-prime/sections/base_section.rb', line 303

def elements_to_render
  self.elements.except(*elements_to_draw.keys)
end

#hard_reload_sectionBoolean

Force reload section, will also re-render elements. For table view cells will also reload it’s table data. Useful on some cases, but in common case please use #reload.

Returns:

  • (Boolean)

    true



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'motion-prime/sections/base_section.rb', line 145

def hard_reload_section
  # reload Base Elements
  self.elements_to_render.values.map(&:view).flatten.each do |view|
    view.removeFromSuperview if view
  end
  render({}, true)
  # reload Draw Elements
  elements_to_draw.values.each(&:update)

  if @collection_section && !self.is_a?(BaseFieldSection)
    cell.setNeedsDisplay
    @collection_section.reload_collection_data
  end
  true
end

#has_container_bounds?Boolean

Returns:

  • (Boolean)


64
65
66
# File 'motion-prime/sections/base_section.rb', line 64

def has_container_bounds?
  options[:container_bounds].present?
end

#hideObject

Hide all elements of section. It will hide all base elements and container of draw elements. FIXME: container_view manipulation should be in draw mixin.



236
237
238
239
240
241
# File 'motion-prime/sections/base_section.rb', line 236

def hide
  if container_view
    container_view.hidden = true
  end
  elements_to_render.values.each(&:hide)
end

#hide_keyboardObject



289
290
291
292
293
294
295
296
297
# File 'motion-prime/sections/base_section.rb', line 289

def hide_keyboard
  elements = Array.wrap(keyboard_close_bindings_options[:elements])
  views = Array.wrap(keyboard_close_bindings_options[:views])

  elements.each do |el|
    views << el.view if el.try(:view) && %w[text_field text_view].include?(el.view_name)
  end
  views.compact.each(&:resignFirstResponder)
end

#keyboard_will_hideObject



268
# File 'motion-prime/sections/base_section.rb', line 268

def keyboard_will_hide; end

#keyboard_will_showObject



267
# File 'motion-prime/sections/base_section.rb', line 267

def keyboard_will_show; end

#on_keyboard_hideObject



266
# File 'motion-prime/sections/base_section.rb', line 266

def on_keyboard_hide; end

#on_keyboard_showObject



265
# File 'motion-prime/sections/base_section.rb', line 265

def on_keyboard_show; end

#reloadBoolean

Reload section, will re-render elements.

Returns:

  • (Boolean)

    true



164
165
166
167
# File 'motion-prime/sections/base_section.rb', line 164

def reload
  elements.values.each(&:update)
  true
end

#render(container_options = {}, force = false) ⇒ Object



200
201
202
203
204
205
206
# File 'motion-prime/sections/base_section.rb', line 200

def render(container_options = {}, force = false)
  force ? create_elements! : create_elements
  self.container_options.deep_merge!(container_options)
  run_callbacks :render do
    render!
  end
end

#render!Object



208
209
210
211
212
213
214
# File 'motion-prime/sections/base_section.rb', line 208

def render!
  render_container(container_options) do
    elements_to_render.each do |key, element|
      element.render
    end
  end
end

#render_container(options = {}, &block) ⇒ Object



169
170
171
172
173
174
175
176
177
178
# File 'motion-prime/sections/base_section.rb', line 169

def render_container(options = {}, &block)
  if should_render_container? && !self.container_element.try(:view)
    element = self.init_container_element(options)
    element.render do
      block.call
    end
  else
    block.call
  end
end

#render_element?(element_name) ⇒ Boolean

Returns:

  • (Boolean)


196
197
198
# File 'motion-prime/sections/base_section.rb', line 196

def render_element?(element_name)
  true
end

#screen?Boolean

Returns:

  • (Boolean)


311
312
313
# File 'motion-prime/sections/base_section.rb', line 311

def screen?
  screen.weakref_alive?
end

#showObject

Show all elements of section. It will show all base elements and container of draw elements. FIXME: container_view manipulation should be in draw mixin.



246
247
248
249
250
251
# File 'motion-prime/sections/base_section.rb', line 246

def show
  if container_view
    container_view.hidden = false
  end
  elements_to_render.values.each(&:show)
end

#strong_referencesObject



56
57
58
# File 'motion-prime/sections/base_section.rb', line 56

def strong_references
  [screen, screen.try(:main_controller)].compact
end

#view(name) ⇒ Object



229
230
231
# File 'motion-prime/sections/base_section.rb', line 229

def view(name)
  element(name).view
end