Class: MotionPrime::TableSection

Inherits:
Section
  • Object
show all
Includes:
HasSearchBar, HasStyleChainBuilder, TableSectionRefreshMixin
Defined in:
motion-prime/sections/table.rb

Direct Known Subclasses

FormSection

Constant Summary

Constants inherited from Section

Section::DEFAULT_CONTENT_HEIGHT, Section::KEYBOARD_HEIGHT_LANDSCAPE, Section::KEYBOARD_HEIGHT_PORTRAIT

Instance Attribute Summary collapse

Attributes inherited from Section

#elements, #model, #name, #options, #screen, #section_styles

Attributes included from DrawSectionMixin

#cached_draw_image, #container_element, #container_gesture_recognizers

Class Method Summary collapse

Instance Method Summary collapse

Methods included from HasSearchBar

#add_search_bar, #create_search_bar, #searchBar, #searchBarSearchButtonClicked

Methods included from HasStyleChainBuilder

#build_styles_chain

Methods included from TableSectionRefreshMixin

#add_pull_to_refresh, #finish_pull_to_refresh

Methods inherited from Section

#add_element, after_initialize, after_render, before_initialize, before_render, bind_keyboard_close, #bind_keyboard_events, #cell, container, #container_bounds, #container_height, #container_options, #create_elements, #current_input_view_height, #default_name, element, #element, #elements_options, #elements_to_draw, #elements_to_render, #has_container_bounds?, #hide_keyboard, #initialize, #keyboard_will_hide, #keyboard_will_show, #on_keyboard_hide, #on_keyboard_show, #reload_section, #render, #render!, #render_container, #render_element?, #screen?, #strong_references, #view

Methods included from DelegateMixin

#clear_delegated, #delegated_by

Methods included from DrawSectionMixin

#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_frome_for

Methods included from HasStyles

#prepare_gradient

Methods included from HasClassFactory

#camelize_factory, #class_factory, #low_camelize_factory

Methods included from HasNormalizer

#normalize_object, #normalize_options

Methods included from HasAuthorization

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

Constructor Details

This class inherits a constructor from MotionPrime::Section

Instance Attribute Details

#deceleratingObject (readonly)

Returns the value of attribute decelerating.



13
14
15
# File 'motion-prime/sections/table.rb', line 13

def decelerating
  @decelerating
end

#did_appearObject

Returns the value of attribute did_appear.



12
13
14
# File 'motion-prime/sections/table.rb', line 12

def did_appear
  @did_appear
end

#group_header_optionsObject

Returns the value of attribute group_header_options.



12
13
14
# File 'motion-prime/sections/table.rb', line 12

def group_header_options
  @group_header_options
end

#group_header_sectionsObject

Returns the value of attribute group_header_sections.



12
13
14
# File 'motion-prime/sections/table.rb', line 12

def group_header_sections
  @group_header_sections
end

#table_elementObject

Returns the value of attribute table_element.



12
13
14
# File 'motion-prime/sections/table.rb', line 12

def table_element
  @table_element
end

Class Method Details

.async_table_data(options = {}) ⇒ Object



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

def async_table_data(options = {})
  self.send :include, Prime::AsyncTableMixin
  self.set_async_data_options options
end

.group_header(name, options) ⇒ Object



433
434
435
436
437
438
# File 'motion-prime/sections/table.rb', line 433

def group_header(name, options)
  options[:name] = name
  self.group_header_options ||= []
  section = options.delete(:id)
  self.group_header_options[section] = options
end

.inherited(subclass) ⇒ Object



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

def inherited(subclass)
  super
  subclass.group_header_options = self.group_header_options.try(:clone)
end

.pull_to_refresh(&block) ⇒ Object



440
441
442
# File 'motion-prime/sections/table.rb', line 440

def pull_to_refresh(&block)
  self.pull_to_refresh_block = block
end

Instance Method Details

#add_cell_sections(sections) ⇒ Boolean

Add cells to table view and reload table view.

Parameters:

  • cell

    sections [Prime::Section, Array<Prime::Section>] cells which will be added to table view.

Returns:

  • (Boolean)

    true



96
97
98
99
100
101
# File 'motion-prime/sections/table.rb', line 96

def add_cell_sections(sections)
  prepare_table_cell_sections(sections)
  @data ||= []
  @data += sections
  reload_table_data
end

#cell_for_index(table, index) ⇒ Object



301
302
303
304
305
306
307
308
# File 'motion-prime/sections/table.rb', line 301

def cell_for_index(table, index)
  cell = cached_cell(index, table) || render_cell(index, table)
  # run table view is appeared callback if needed
  if !@did_appear && index.row == cell_sections_for_group(index.section).size - 1
    on_appear
  end
  cell.is_a?(UIView) ? cell : cell.view
end

#cell_name(table, index) ⇒ Object



289
290
291
292
# File 'motion-prime/sections/table.rb', line 289

def cell_name(table, index)
  record = cell_section_by_index(index)
  "cell_#{record.object_id}_#{@data_stamp[record.object_id]}"
end

#cell_section_by_index(index) ⇒ Object



285
286
287
# File 'motion-prime/sections/table.rb', line 285

def cell_section_by_index(index)
  cell_sections_for_group(index.section)[index.row]
end

#cell_section_styles(section) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'motion-prime/sections/table.rb', line 172

def cell_section_styles(section)
  # type = [`cell`, `header`, `field`]

  # UserFormSection example: field :email, type: :string
  # form_name = `user`
  # type = `field`
  # field_name = `email`
  # field_type = `string_field`

  # CategoriesTableSection example: table is a `CategoryTableSection`, cell is a `CategoryTitleSection`, element :icon, type: :image
  # table_name = `categories`
  # type = `cell` (always true)
  # table_cell_section_name = `title`
  type = section.respond_to?(:cell_type) ? section.cell_type : 'cell'
  suffixes = [type]
  if section.is_a?(BaseFieldSection)
    suffixes << section.default_name
  end

  styles = {}
  # table: base_table_<type>
  # form: base_form_<type>, base_form_<field_type>
  styles[:common] = build_styles_chain(table_styles[:common], suffixes)
  if section.is_a?(BaseFieldSection)
    # form cell: _<type>_<field_name> = `_field_email`
    suffixes << :"#{type}_#{section.name}" if section.name
  elsif section.respond_to?(:cell_section_name) # cell section came from table
    # table cell: _<table_cell_section_name> = `_title`
    suffixes << section.cell_section_name
  end
  # table: <table_name>_table_<type>, <table_name>_table_<table_cell_section_name> = `categories_table_cell`, `categories_table_title`
  # form: <form_name>_form_<type>, <form_name>_form_<field_type>, user_form_<type>_email = `user_form_field`, `user_form_string_field`, `user_form_field_email`
  styles[:specific] = build_styles_chain(table_styles[:specific], suffixes)

  container_options_styles = section.container_options[:styles]
  if container_options_styles.present?
    styles[:specific] += Array.wrap(container_options_styles)
  end

  styles
end

#cell_sections_for_group(section) ⇒ Object



281
282
283
# File 'motion-prime/sections/table.rb', line 281

def cell_sections_for_group(section)
  flat_data? ? data : data[section]
end

#dataArray<Prime::Section>

Returns cached version of table data

Returns:

  • (Array<Prime::Section>)

    cached array of sections



30
31
32
# File 'motion-prime/sections/table.rb', line 30

def data
  @data || set_table_data
end

#deallocObject



41
42
43
44
45
46
# File 'motion-prime/sections/table.rb', line 41

def dealloc
  Prime.logger.dealloc_message :table, self, self.table_element.try(:view).to_s
  table_delegate.clear_delegated
  table_view.setDataSource nil
  super
end

#delete_cell_sections(sections) ⇒ Array<NSIndexPath>

Delete cells from table data and remove them from table view with animation.

Parameters:

  • cell

    sections [Prime::Section, Array<Prime::Section>] cells which will be removed from table view.

Returns:

  • (Array<NSIndexPath>)

    index paths of removed cells.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'motion-prime/sections/table.rb', line 107

def delete_cell_sections(sections)
  paths = []
  Array.wrap(sections).each do |section|
    index = index_for_cell_section(section)
    next Prime.logger.debug("Delete cell section: `#{section.name}` is not in the list") unless index
    paths << index
    delete_from_data(index)
  end
  if paths.any?
    table_view.beginUpdates
    table_view.deleteRowsAtIndexPaths(paths, withRowAnimation: UITableViewRowAnimationLeft)
    table_view.endUpdates
  end
  paths
end

#delete_from_data(index) ⇒ Object

Delete section from data at index

Parameters:

  • index (NSIndexPath)

    index of cell which will be removed from table data.



126
127
128
129
130
131
132
# File 'motion-prime/sections/table.rb', line 126

def delete_from_data(index)
  if flat_data?
    delete_from_flat_data(index)
  else
    delete_from_groped_data(index)
  end
end

#delete_from_flat_data(index) ⇒ Object



134
135
136
137
# File 'motion-prime/sections/table.rb', line 134

def delete_from_flat_data(index)
  @data[index.row] = nil
  @data.delete_at(index.row)
end

#delete_from_groped_data(index) ⇒ Object



139
140
141
142
# File 'motion-prime/sections/table.rb', line 139

def delete_from_groped_data(index)
  @data[index.section][index.row] = nil
  @data[index.section].delete_at(index.row)
end

#fixed_table_dataObject

IMPORTANT: when you use #map in table_data, then #dealloc of Prime::Section will not be called to section created on that #map. We did not find yet why this happening, for now just using hack.



37
38
39
# File 'motion-prime/sections/table.rb', line 37

def fixed_table_data
  table_data.to_enum.to_a
end

#flat_data?Boolean

Returns:

  • (Boolean)


277
278
279
# File 'motion-prime/sections/table.rb', line 277

def flat_data?
  !has_many_sections?
end

#has_many_sections?Boolean

Returns:

  • (Boolean)


273
274
275
# File 'motion-prime/sections/table.rb', line 273

def has_many_sections?
  group_header_options.present? || data.try(:first).is_a?(Array)
end

#header_cell_in_group(table, group) ⇒ Object



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'motion-prime/sections/table.rb', line 316

def header_cell_in_group(table, group)
  return unless header = header_section_for_group(group)

  reuse_identifier = "header_#{group}_#{@header_stamp}"
  cached = table.dequeueReusableHeaderFooterViewWithIdentifier(reuse_identifier)
  return cached if cached.present?

  styles = cell_section_styles(header).values.flatten
  wrapper = MotionPrime::BaseElement.factory(:table_header, 
    screen: screen, 
    styles: styles, 
    parent_view: table_view, 
    reuse_identifier: reuse_identifier,
    section: header
  )
  wrapper.render do |container_view, container_element|
    header.container_element = container_element
    header.render
  end
end

#header_section_for_group(group) ⇒ Object



264
265
266
267
# File 'motion-prime/sections/table.rb', line 264

def header_section_for_group(group)
  self.group_header_sections ||= []
  self.group_header_sections[group] || render_header(group)
end

#height_for_header_in_group(table, group) ⇒ Object



337
338
339
# File 'motion-prime/sections/table.rb', line 337

def height_for_header_in_group(table, group)
  header_section_for_group(group).try(:container_height) || 0
end

#height_for_index(table, index) ⇒ Object



310
311
312
313
314
# File 'motion-prime/sections/table.rb', line 310

def height_for_index(table, index)
  section = cell_section_by_index(index)
  section.create_elements
  section.container_height
end

#hideObject



236
237
238
# File 'motion-prime/sections/table.rb', line 236

def hide
  table_view.try(:hide)
end

#index_for_cell_section(section) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
# File 'motion-prime/sections/table.rb', line 144

def index_for_cell_section(section)
  if flat_data?
    row = @data.try(:index, section)
    NSIndexPath.indexPathForRow(row, inSection: 0)
  else
    (@data || []).each_with_index do |cell_sections, group|
      row = cell_sections.index(section)
      return NSIndexPath.indexPathForRow(row, inSection: group) if row
    end
  end
end

#number_of_groups(table = nil) ⇒ Object

Table View Delegate




297
298
299
# File 'motion-prime/sections/table.rb', line 297

def number_of_groups(table = nil)
  has_many_sections? ? data.count : 1
end

#on_appearObject



270
# File 'motion-prime/sections/table.rb', line 270

def on_appear; end

#on_cell_render(cell, index) ⇒ Object



269
# File 'motion-prime/sections/table.rb', line 269

def on_cell_render(cell, index); end

#on_click(table, index) ⇒ Object



271
# File 'motion-prime/sections/table.rb', line 271

def on_click(table, index); end

#refresh_if_neededBoolean

Reload table view if data was empty before.

Returns:

  • (Boolean)

    true if reload was happened



74
75
76
# File 'motion-prime/sections/table.rb', line 74

def refresh_if_needed
  @data.nil? ? reload_table_data : false
end

#reloadBoolean

Alias for reload_data

Returns:

  • (Boolean)

    true



59
60
61
# File 'motion-prime/sections/table.rb', line 59

def reload
  reload_data
end

#reload_cell_section(section) ⇒ Object



156
157
158
159
160
# File 'motion-prime/sections/table.rb', line 156

def reload_cell_section(section)
  section.elements.values.each(&:compute_options!)
  section.cached_draw_image = nil
  # TODO: reset date stamps, reload row
end

#reload_dataBoolean

Reset all table data and reload table view

Returns:

  • (Boolean)

    true



51
52
53
54
# File 'motion-prime/sections/table.rb', line 51

def reload_data
  reset_data
  reload_table_data
end

#reload_table_dataBoolean

Reload table view

Returns:

  • (Boolean)

    true



66
67
68
69
# File 'motion-prime/sections/table.rb', line 66

def reload_table_data
  table_view.reloadData
  true
end

#render_cell(index, table = nil) ⇒ Object



244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'motion-prime/sections/table.rb', line 244

def render_cell(index, table = nil)
  table ||= table_view
  section = cell_sections_for_group(index.section)[index.row]
  element = section.container_element || section.init_container_element(container_element_options_for(index))

  view = element.render do
    section.render
  end

  @reusable_cells ||= {}
  @reusable_cells[section.object_id] = view
  on_cell_render(view, index)
  view
end

#render_header(group) ⇒ Object



259
260
261
262
# File 'motion-prime/sections/table.rb', line 259

def render_header(group)
  return unless options = self.group_header_options.try(:[], group)
  self.group_header_sections[group] ||= FormHeaderSection.new(options.merge(screen: screen, table: self.weak_ref))
end

#render_tableObject



228
229
230
# File 'motion-prime/sections/table.rb', line 228

def render_table
  self.table_element = screen.table_view(table_element_options)
end

#reset_dataBoolean

Reset all table data.

Returns:

  • (Boolean)

    true



81
82
83
84
85
86
87
88
89
90
# File 'motion-prime/sections/table.rb', line 81

def reset_data
  @did_appear = false
  @data = nil
  @data_stamp = nil
  @reusable_cells.each do |object_id, cell|
    cell.reuseIdentifier = nil
  end if @reusable_cells
  @reusable_cells = nil
  true
end

#scroll_view_did_end_decelerating(scroll) ⇒ Object



345
346
347
348
# File 'motion-prime/sections/table.rb', line 345

def scroll_view_did_end_decelerating(scroll)
  @decelerating = false
  display_pending_cells
end

#scroll_view_did_end_dragging(scroll, willDecelerate: will_decelerate) ⇒ Object



353
354
355
# File 'motion-prime/sections/table.rb', line 353

def scroll_view_did_end_dragging(scroll, willDecelerate: will_decelerate)
  display_pending_cells unless @decelerating = will_decelerate
end

#scroll_view_did_scroll(scroll) ⇒ Object



350
351
# File 'motion-prime/sections/table.rb', line 350

def scroll_view_did_scroll(scroll)
end

#scroll_view_will_begin_dragging(scroll) ⇒ Object



341
342
343
# File 'motion-prime/sections/table.rb', line 341

def scroll_view_will_begin_dragging(scroll)
  @decelerating = true
end

#showObject



240
241
242
# File 'motion-prime/sections/table.rb', line 240

def show
  table_view.try(:show)
end

#table_dataArray<Prime::Section>

Return sections which will be used to render as table cells.

This method should be redefined in your table section and must return array.

Returns:

  • (Array<Prime::Section>)

    array of sections



23
24
25
# File 'motion-prime/sections/table.rb', line 23

def table_data
  @model || []
end

#table_delegateObject



214
215
216
# File 'motion-prime/sections/table.rb', line 214

def table_delegate
  @table_delegate ||= TableDelegate.new(section: self)
end

#table_element_optionsObject



218
219
220
221
222
223
224
225
226
# File 'motion-prime/sections/table.rb', line 218

def table_element_options
  {
    section: self.weak_ref,
    styles: table_styles.values.flatten,
    delegate: table_delegate,
    data_source: table_delegate,
    style: (UITableViewStyleGrouped unless flat_data?)
  }
end

#table_stylesObject



162
163
164
165
166
167
168
169
170
# File 'motion-prime/sections/table.rb', line 162

def table_styles
  type = self.is_a?(FormSection) ? :base_form : :base_table

  base_styles = [type]
  base_styles << :"#{type}_with_sections" unless flat_data?
  item_styles = [name.to_sym]
  item_styles << @styles if @styles.present?
  {common: base_styles, specific: item_styles}
end

#table_viewObject



232
233
234
# File 'motion-prime/sections/table.rb', line 232

def table_view
  table_element.view
end