tgios 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,77 @@
1
+ module Tgios
2
+ class SearchController < ExtendedUIViewController
3
+ Events=[:record_selected, :search, :after_layout]
4
+ attr_accessor :table_list_binding_options, :pop_after_selected, :field_name
5
+
6
+ def on(event_name, &block)
7
+ raise ArgumentError.new("Event not found, valid events are: [#{Events.join(', ')}]") unless Events.include?(event_name)
8
+ @events[event_name]=block
9
+ self
10
+ end
11
+
12
+ def result=(value)
13
+ @result=value
14
+ @search_result_table_binding.reload(@result) unless @search_result_table_binding.nil?
15
+ end
16
+
17
+ def select_record(record)
18
+ @events[:record_selected].call(record)
19
+ self.navigationController.popViewControllerAnimated(true) if @pop_after_selected
20
+ end
21
+
22
+ def viewDidLoad
23
+ super
24
+ @search_result_table = UITableView.new
25
+ @search_result_table_binding=UITableViewListBinding.new.bind(@search_result_table, @result, @field_name, @table_list_binding_options)
26
+ @search_result_table_binding.on(:touch_row) do |record, event|
27
+ select_record(record)
28
+ end
29
+ Motion::Layout.new do |l|
30
+ l.view self.view
31
+ l.subviews 'table' => @search_result_table
32
+ l.vertical '|[table]|'
33
+ l.horizontal '|[table]|'
34
+ end
35
+ @search_bar=UISearchBar.alloc.init
36
+ @search_bar.frame=[[0,0],['100',0]]
37
+ @search_bar.sizeToFit
38
+ @search_bar.delegate=self
39
+ @search_result_table.tableHeaderView=@search_bar
40
+ @search_bar.becomeFirstResponder
41
+ @events[:after_layout].call(@search_bar, @search_result_table) unless @events[:after_layout].nil?
42
+
43
+ end
44
+
45
+ def searchBarSearchButtonClicked(searchBar)
46
+ searchBar.resignFirstResponder
47
+
48
+ unless @events.nil? || @events[:search].nil?
49
+ @events[:search].call(searchBar.text) do |success, result|
50
+ @result=result
51
+ if @result.count == 1
52
+ select_record(@result.first)
53
+ else
54
+ @search_result_table_binding.reload(@result) unless @search_result_table_binding.nil?
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def init
61
+ super
62
+ @events={}
63
+ @result=[]
64
+ @pop_after_selected=true
65
+ self
66
+ end
67
+
68
+ def onPrepareForRelease
69
+ @events=nil
70
+ @search_result_table_binding.prepareForRelease
71
+ @search_result_table_binding=nil
72
+ self.navigationItem.rightBarButtonItem = nil
73
+ end
74
+
75
+
76
+ end
77
+ end
@@ -0,0 +1,36 @@
1
+ module Tgios
2
+ class UIButtonBinding < BindingBase
3
+ def initialize
4
+ @events={}
5
+ end
6
+
7
+ def on(event_name, &block)
8
+ @events[event_name]= block
9
+ self
10
+ end
11
+
12
+ def bind(button)
13
+ @button=WeakRef.new(button)
14
+ @button.addTarget(self, action: 'button_tapped', forControlEvents: UIControlEventTouchUpInside)
15
+ self
16
+ end
17
+
18
+ def button_tapped
19
+ ap "button_tapped"
20
+ @events[:tapped].call unless @events[:tapped].nil?
21
+ end
22
+
23
+ def onPrepareForRelease
24
+ @events=nil
25
+ end
26
+
27
+ def self.unbind(button)
28
+ button.removeTarget(nil, action: 'button_tapped', forControlEvents: UIControlEventTouchUpInside)
29
+ end
30
+
31
+ def dealloc
32
+ @button.removeTarget(self, action: 'button_tapped', forControlEvents: UIControlEventTouchUpInside)
33
+ super
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,53 @@
1
+ module Tgios
2
+ class UIPickerViewListBinding < BindingBase
3
+ def initialize
4
+ @events={}
5
+ end
6
+
7
+ def on(event_name, &block)
8
+ @events[event_name]=block
9
+ end
10
+
11
+ def bind(picker_view, list: list, display_field: display_field)
12
+ @picker_view=WeakRef.new(picker_view)
13
+ @list=WeakRef.new(list)
14
+ @display_field=display_field
15
+ @picker_view.dataSource=self
16
+ @picker_view.delegate=self
17
+ end
18
+
19
+
20
+ def numberOfComponentsInPickerView(pickerView)
21
+ 1
22
+ end
23
+
24
+ def pickerView(picker_view, numberOfRowsInComponent:section)
25
+ @list.length
26
+ end
27
+
28
+ def pickerView(pickerView, titleForRow: row, forComponent: component)
29
+ @list[row][@display_field]
30
+ end
31
+
32
+ def pickerView(pickerView, didSelectRow:row, inComponent:component)
33
+ @events[:row_selected].call(row, selected_record) unless @events[:row_selected].nil?
34
+ end
35
+
36
+ def selected_record
37
+ @list[@picker_view.selectedRowInComponent(0)]
38
+ end
39
+
40
+ def select_record(record)
41
+ idx = (@list.find_index(record) || 0)
42
+ @picker_view.selectRow(idx, inComponent:0, animated: false)
43
+ end
44
+
45
+ def onPrepareForRelease
46
+ @events=nil
47
+ @picker_view.dataSource=nil
48
+ @picker_view.delegate=nil
49
+ @list=nil
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,70 @@
1
+ module Tgios
2
+ class UITableViewListBinding < BindingBase
3
+ def bind(tableView, list, display_field, options={})
4
+ @tableView=WeakRef.new(tableView)
5
+ @tableView.dataSource=self
6
+ @tableView.delegate=self
7
+ @display_field=display_field
8
+ @events={}
9
+ @list=WeakRef.new(list)
10
+ @options=(options || {})
11
+ return self
12
+ end
13
+
14
+ def on(event_name, &block)
15
+ @events[event_name]=block
16
+ self
17
+ end
18
+
19
+ def reload(list)
20
+ @list=list
21
+ @tableView.reloadData()
22
+ end
23
+
24
+ def tableView(tableView, cellForRowAtIndexPath: index_path)
25
+ record = @list[index_path.row]
26
+ cell_identifier = "CELL_IDENTIFIER"
27
+ cell=tableView.dequeueReusableCellWithIdentifier(cell_identifier)
28
+ if cell.nil?
29
+ cell=UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier: cell_identifier)
30
+ cell.textLabel.adjustsFontSizeToFitWidth = true
31
+ if @options[:lines] && @options[:lines] != 1
32
+ cell.textLabel.numberOfLines = 0
33
+ end
34
+ end
35
+ cell.textLabel.text=record[@display_field]
36
+ cell
37
+
38
+ end
39
+
40
+ def tableView(tableView, didSelectRowAtIndexPath:index_path)
41
+ @events[:touch_row].call(@list[index_path.row], event: {tableView: tableView, didSelectRowAtIndexPath:index_path}) if @events.has_key?(:touch_row)
42
+ end
43
+
44
+ def tableView(tableView, numberOfRowsInSection: section)
45
+ @list.length
46
+ end
47
+
48
+ def tableView(tableView, heightForRowAtIndexPath: index_path)
49
+ if @options[:lines]
50
+ 20 + 20 * (@options[:lines] || 2)
51
+ else
52
+ 45
53
+ end
54
+ end
55
+
56
+ def onPrepareForRelease
57
+ @events=nil
58
+ @list=nil
59
+ @display_field=nil
60
+ @tableView.delegate=nil
61
+ @tableView.dataSource=nil
62
+ @tableView=nil
63
+
64
+ end
65
+
66
+
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,373 @@
1
+ module Tgios
2
+ class UITableViewModelBinding < BindingBase
3
+
4
+ def initialize
5
+ super
6
+ PlasticCup::Base.add_style_sheet(:ui_field_default_styles, {
7
+ clearButtonMode: UITextFieldViewModeWhileEditing,
8
+ tag: 99
9
+ }) unless Base.get_style_sheet(:ui_field_default_styles)
10
+ PlasticCup::Base.add_style_sheet(:ui_field_without_label, {
11
+ extends: :ui_field_default_styles,
12
+ frame: CGRectMake(16, 9, 292, 25)
13
+ }, :ios7) unless Base.get_style_sheet(:ui_field_without_label)
14
+ PlasticCup::Base.add_style_sheet(:ui_field_without_label, {
15
+ extends: :ui_field_default_styles,
16
+ frame: CGRectMake(16, 9, 282, 25)
17
+ }) unless Base.get_style_sheet(:ui_field_without_label)
18
+ PlasticCup::Base.add_style_sheet(:ui_field_with_label, {
19
+ extends: :ui_field_default_styles,
20
+ frame: CGRectMake(114, 9, 194, 25)
21
+ }, :ios7) unless Base.get_style_sheet(:ui_field_with_label)
22
+ PlasticCup::Base.add_style_sheet(:ui_field_with_label, {
23
+ extends: :ui_field_default_styles,
24
+ frame: CGRectMake(84, 9, 214, 25)
25
+ }) unless Base.get_style_sheet(:ui_field_with_label)
26
+ PlasticCup::Base.add_style_sheet(:ui_view_default_styles, {
27
+ font: lambda {UIFont.systemFontOfSize(15)},
28
+ backgroundColor: :clear.uicolor,
29
+ tag: 88
30
+ }) unless Base.get_style_sheet(:ui_view_default_styles)
31
+
32
+ @events={}
33
+ @events[:build_cell]=->(cell_identifier, type) { build_cell(cell_identifier, type) }
34
+ @events[:update_cell]=->(field_set, cell, index_path) { create_or_update_field(field_set, cell, index_path)}
35
+ @events[:update_accessory]=->(field_set, cell, index_path, ui_field) { update_accessory_type_or_view(field_set, cell, index_path, ui_field)}
36
+ end
37
+
38
+
39
+
40
+ def bind(tableView, model, fields)
41
+ @tableView=WeakRef.new(tableView)
42
+ @fields=fields
43
+ bindings_prepare_release
44
+ @bindings={}
45
+ @tv_bindings={}
46
+ self.model=model
47
+ @tableView.dataSource=self
48
+ @tableView.delegate=self
49
+ self
50
+ end
51
+
52
+ def model=(value)
53
+ @model=WeakRef.new(value)
54
+ @tableView.reloadData()
55
+ end
56
+
57
+ def reload
58
+ @tableView.reloadData
59
+ end
60
+
61
+ def on(event_name, &block)
62
+ @events[event_name]=block
63
+ self
64
+ end
65
+
66
+ def tableView(tableView, cellForRowAtIndexPath: index_path)
67
+ field_set = field_set_at_index_path(index_path)
68
+ type = field_set[:child_index].nil? ? field_set[:type] : field_set[:child_field][:type]
69
+ cell_identifier = "CELL_IDENTIFIER_#{type.to_s}"
70
+ cell=tableView.dequeueReusableCellWithIdentifier(cell_identifier)
71
+ isReusedCell=!cell.nil?
72
+
73
+ cell=@events[:build_cell].call(cell_identifier, type) unless isReusedCell
74
+ weak_cell=WeakRef.new(cell)
75
+
76
+ ui_field=@events[:update_cell].call(field_set, weak_cell, index_path) unless @events[:update_cell].nil?
77
+ @events[:update_accessory].call(field_set, weak_cell, index_path, ui_field) unless @events[:update_accessory].nil?
78
+ cell
79
+
80
+ end
81
+
82
+ def build_cell(cell_identifier, type)
83
+ if type == :checkbox
84
+ cell = UITableViewCell.default(cell_identifier)
85
+ cell.textLabel.numberOfLines = 0
86
+ else
87
+ cell = UITableViewCell.value2(cell_identifier)
88
+ cell.textLabel.numberOfLines = 2
89
+ end
90
+ cell
91
+ end
92
+
93
+ def create_or_update_field(field_set, cell, index_path)
94
+ ui_field=nil
95
+ #ap field_set
96
+ cell.textLabel.text= field_set[:show_label] ? field_set[:label] : ''
97
+
98
+ cell.selectionStyle=UITableViewCellSelectionStyleNone
99
+ case field_set[:type]
100
+ when :text, :password, :label
101
+ ui_field=cell.contentView.viewWithTag(99)
102
+
103
+ if ui_field.nil?
104
+ ui_field = UITextField.new
105
+ cell.contentView.addSubview(ui_field)
106
+ end
107
+
108
+ if field_set[:show_label]
109
+ PlasticCup::Base.style(ui_field, :ui_field_with_label)
110
+ else
111
+ PlasticCup::Base.style(ui_field, :ui_field_without_label)
112
+ end
113
+ ui_field.placeholder= field_set[:show_label] ? field_set[:placeholder] : field_set[:label]
114
+
115
+ text_field_binding=@bindings[field_set[:name]]
116
+ if text_field_binding.nil?
117
+ text_field_binding=UITextFieldBinding.new(@model, ui_field, field_set[:name], field_set)
118
+ text_field_binding.on(:begin_edit) do |model, field_name, event|
119
+ @events[:text_begin_edit].call(model, field_name, event) unless @events[:text_begin_edit].nil?
120
+ performSelector('will_scroll_to_index_path:', withObject: index_path, afterDelay:0.01)
121
+ end
122
+ text_field_binding.on(:return_tapped) do |model, field_name, event|
123
+ @events[:text_return_tapped].call(model, field_name, event) unless @events[:text_return_tapped].nil?
124
+ end
125
+ @bindings[field_set[:name]]=text_field_binding
126
+ end
127
+ text_field_binding.update(ui_field, @model)
128
+ ui_field.becomeFirstResponder if field_set[:first_responder] # TODO: not work when cell is not visible, buggy
129
+ when :big_label
130
+ cell.detailTextLabel.numberOfLines = 0
131
+ cell.detailTextLabel.backgroundColor = :clear.uicolor
132
+ cell.detailTextLabel.text = @model.send(field_set[:name])
133
+
134
+ when :array
135
+ if field_set[:child_index].nil?
136
+ cell.detailTextLabel.text = field_set[:label]
137
+ else
138
+ child_field = field_set[:child_field]
139
+ child = @model.send(field_set[:name])[field_set[:child_index]]
140
+ if child_field[:type] == :checkbox
141
+ cell.textLabel.text = child[child_field[:display_field]]
142
+ cell.imageView.image = child[:selected] ? 'tick_select.png'.uiimage : 'tick_deselect.png'.uiimage
143
+ else
144
+ cell.textLabel.text = nil
145
+ cell.detailTextLabel.numberOfLines = 0
146
+ cell.detailTextLabel.text = child[child_field[:display_field]]
147
+ end
148
+ end
149
+
150
+ when :checkbox
151
+ cell.textLabel.text = field_set[:label]
152
+ cell.imageView.image = @model.send(field_set[:name])== true ? 'tick_select.png'.uiimage : 'tick_deselect.png'.uiimage
153
+
154
+ when :label_only
155
+ cell.detailTextLabel.text = field_set[:label]
156
+
157
+ when :text_view
158
+
159
+ ui_field=cell.contentView.viewWithTag(88)
160
+ ui_field_default_frame = Rect([[16, 9], [282, 90]])
161
+
162
+ if ui_field.nil?
163
+ ui_field = PlasticCup::Base.style(UITextView.new, :ui_view_default_styles)
164
+ cell.contentView.addSubview(ui_field)
165
+ end
166
+
167
+ ui_field.frame = ui_field_default_frame
168
+
169
+ text_field_binding=@tv_bindings[field_set[:name]]
170
+ if text_field_binding.nil?
171
+ text_field_binding=UITextViewBinding.new(@model, ui_field, field_set[:name], field_set)
172
+ text_field_binding.on(:begin_edit) do |model, field_name, event|
173
+ @events[:text_begin_edit].call(model, field_name, event) unless @events[:text_begin_edit].nil?
174
+ performSelector('will_scroll_to_index_path:', withObject: index_path, afterDelay:0.01)
175
+ end
176
+ text_field_binding.on(:return_tapped) do |model, field_name, event|
177
+ @events[:text_return_tapped].call(model, field_name, event) unless @events[:text_return_tapped].nil?
178
+ end
179
+ @tv_bindings[field_set[:name]]=text_field_binding
180
+ end
181
+ text_field_binding.update(ui_field, @model)
182
+ ui_field.becomeFirstResponder if field_set[:first_responder] # TODO: not work when cell is not visible, buggy
183
+
184
+
185
+
186
+ end
187
+
188
+ ui_field
189
+ end
190
+
191
+ def update_accessory_type_or_view(field_set, cell, index_path, ui_field)
192
+ cell.accessoryType = (field_set[:accessory] || :none).uitablecellaccessory
193
+ cell.accessoryView = nil
194
+ end
195
+
196
+ def tableView(tableView, didSelectRowAtIndexPath:index_path)
197
+ @selected_field_set=field_set_at_index_path(index_path)
198
+ @events[:touch_row].call(@selected_field_set, {tableView: tableView, didSelectRowAtIndexPath:index_path}) if @events.has_key?(:touch_row)
199
+ if @selected_field_set[:scroll]
200
+ performSelector('will_scroll_to_index_path:', withObject: index_path, afterDelay:0.01)
201
+ end
202
+
203
+ field_set = @selected_field_set
204
+ unless field_set[:child_index].nil?
205
+ field_set = field_set[:child_field]
206
+ if field_set[:type] == :checkbox
207
+ child = @model.send(@selected_field_set[:name])[@selected_field_set[:child_index]]
208
+ child[:selected] = !(child[:selected] || false)
209
+ reload
210
+ end
211
+ end
212
+ end
213
+
214
+ def tableView(tableView, numberOfRowsInSection: section)
215
+ count = @fields.length
216
+ @fields.each do |fld|
217
+ count += @model.send(fld[:name]).length if fld[:type] == :array
218
+ end
219
+ count
220
+ end
221
+
222
+ def tableView(tableView, heightForRowAtIndexPath: index_path)
223
+ field_set = field_set_at_index_path(index_path)
224
+ field_set = field_set[:child_field] unless field_set[:child_index].nil?
225
+ if field_set[:type] == :big_label || field_set[:type] == :checkbox
226
+ 20 + 20 * (field_set[:lines] || 2)
227
+ elsif field_set[:type] == :text_view
228
+ 110
229
+ else
230
+ 45
231
+ end
232
+ end
233
+
234
+ def field_set_at_index_path(index_path)
235
+ row = index_path.row
236
+ array_indices = @fields.each_index.select{|i| @fields[i][:type] == :array}
237
+ return @fields[row] if array_indices.empty? || array_indices.first >= row
238
+ array_count_sum = 0
239
+ array_indices.each do |a_idx|
240
+ array_count = @model.send(@fields[a_idx][:name]).length
241
+ if row <= a_idx + array_count_sum + array_count
242
+ sub_idx = row - a_idx - array_count_sum - 1
243
+ return sub_idx < 0 ? @fields[a_idx + sub_idx + 1] : @fields[a_idx].merge(child_index: sub_idx)
244
+ end
245
+ array_count_sum += array_count
246
+ end
247
+ @fields[row - array_count_sum]
248
+ end
249
+
250
+ def listen_to_keyboard
251
+ @scroll_when_editing = true
252
+ NSNotificationCenter.defaultCenter.addObserver(self, selector: 'keyboardWillShow:', name: UIKeyboardWillShowNotification, object: nil)
253
+ NSNotificationCenter.defaultCenter.addObserver(self, selector: 'keyboardWillHide:', name: UIKeyboardWillHideNotification, object: nil)
254
+ end
255
+
256
+ def stop_listen_to_keyboard
257
+ @scroll_when_editing = false
258
+ NSNotificationCenter.defaultCenter.removeObserver(self)
259
+ end
260
+
261
+ def keyboardWillShow(note)
262
+ shrink_table_view(note)
263
+ end
264
+
265
+ def keyboardWillHide(note)
266
+ expand_table_view(note)
267
+ end
268
+
269
+ def expand_table_view(note)
270
+ @expanding = true
271
+ offset_y = @tableView.contentOffset.y
272
+ frame_height = @tableView.frame.size.height
273
+ content_height = @tableView.contentSize.height
274
+ new_offset_height = nil
275
+ if frame_height > content_height
276
+ if offset_y != 0
277
+ new_offset_height = 0
278
+ end
279
+ else
280
+ bottom_offset = frame_height - content_height + offset_y
281
+ if bottom_offset > 0
282
+ new_offset_height = offset_y - bottom_offset
283
+ end
284
+ end
285
+
286
+ if new_offset_height.nil?
287
+ reset_content_inset_bottom
288
+ else
289
+ curve = note[UIKeyboardAnimationCurveUserInfoKey]
290
+ duration = note[UIKeyboardAnimationDurationUserInfoKey]
291
+ @animation_proc = -> {
292
+ @tableView.setContentOffset([0, new_offset_height])
293
+ }
294
+ @completion_proc = lambda { |finished|
295
+ reset_content_inset_bottom
296
+ }
297
+ UIView.animateWithDuration(duration-0.01, delay: 0, options: curve,
298
+ animations: @animation_proc,
299
+ completion: @completion_proc)
300
+ end
301
+ end
302
+
303
+ def shrink_table_view(note)
304
+ # TODO: don't shrink when table frame bottom is above the keyboard
305
+ @shrinking = true
306
+ rect = note[UIKeyboardFrameEndUserInfoKey].CGRectValue
307
+ if @expanding
308
+ @shrink_height = rect.size.height
309
+ else
310
+ set_content_inset_bottom(rect.size.height)
311
+ end
312
+ end
313
+
314
+ def reset_content_inset_bottom
315
+ @tableView.contentInset = UIEdgeInsetsZero
316
+ @tableView.scrollIndicatorInsets = UIEdgeInsetsZero
317
+ @expanding = false
318
+ if @shrink_height
319
+ set_content_inset_bottom(@shrink_height)
320
+ @shrink_height = nil
321
+ end
322
+ end
323
+
324
+ def set_content_inset_bottom(height)
325
+ @tableView.contentInset = UIEdgeInsetsMake(0,0,height,0)
326
+ @tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0,0,height,0)
327
+ @shrinking = false
328
+ if @index_path_to_scroll
329
+ scroll_to_index_path(@index_path_to_scroll)
330
+ @index_path_to_scroll = nil
331
+ end
332
+ end
333
+
334
+ def will_scroll_to_index_path(index_path)
335
+ if @scroll_when_editing
336
+ if @shrinking
337
+ @index_path_to_scroll = index_path
338
+ else
339
+ scroll_to_index_path(index_path)
340
+ end
341
+ end
342
+ end
343
+
344
+ def scroll_to_index_path(index_path)
345
+ @tableView.scrollToRowAtIndexPath(index_path, atScrollPosition: UITableViewScrollPositionBottom, animated: true)
346
+ @index_path_to_scroll = nil
347
+ end
348
+
349
+ def bindings_prepare_release
350
+ @bindings.values.each do |binding|
351
+ binding.prepareForRelease
352
+ end if @binding.is_a?(Hash)
353
+ @tv_bindings.values.each do |binding|
354
+ binding.prepareForRelease
355
+ end if @tv_bindings.is_a?(Hash)
356
+ end
357
+
358
+ def onPrepareForRelease
359
+ bindings_prepare_release
360
+ self.stop_listen_to_keyboard
361
+ @animation_proc=nil
362
+ @completion_proc=nil
363
+ @bindings=nil
364
+ @tv_bindings=nil
365
+ @events=nil
366
+ @tableView.dataSource=nil
367
+ @tableView.delegate=nil
368
+ @tableView=nil
369
+ @model=nil
370
+ end
371
+
372
+ end
373
+ end