tgios 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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