Button_on_click(self.nit_button);
}
@end
+
+// Proxy for both delegates of UITableView relaying all callbacks to `nit_list_layout`
+@interface UITableViewAndDataSource: NSObject <UITableViewDelegate, UITableViewDataSource>
+
+ // Nit object receiving the callbacks
+ @property ListLayout nit_list_layout;
+
+ // List of native views added to this list view from the Nit side
+ @property NSMutableArray *views;
+@end
+
+@implementation UITableViewAndDataSource
+
+ - (id)init
+ {
+ self = [super init];
+ self.views = [[NSMutableArray alloc] initWithCapacity:8];
+ return self;
+ }
+
+ - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+ return ListLayout_number_of_sections_in_table_view(self.nit_list_layout, tableView);
+ }
+
+ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return ListLayout_number_of_rows_in_section(self.nit_list_layout, tableView, section);
+ }
+
+ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
+ return ListLayout_title_for_header_in_section(self.nit_list_layout, tableView, section);
+ }
+
+ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ return ListLayout_cell_for_row_at_index_path(self.nit_list_layout, tableView, indexPath);
+ }
+@end
`}
redef class App
forControlEvents:UIControlEventTouchUpInside];
`}
end
+
+redef class ListLayout
+
+ redef type NATIVE: UITableView
+ redef var native = new UITableView(new UITableViewStyle.plain)
+
+ init
+ do
+ native.autoresizing_mask
+ native.assign_delegate_and_data_source self
+ end
+
+ redef fun add(item)
+ do
+ # Adding a view to a UITableView is a bit tricky.
+ #
+ # Items are added to the Objective-C view only by callbacks.
+ # We must store the sub views in local lists while waiting
+ # for the callbacks.
+ #
+ # As usual, we keep the Nity object in `items`.
+ # But we also keep their native counterparts in a list of
+ # the `UITableViewAndDataSource` set as `native.delegate`.
+ # Otherwise the native views could be freed by the Objective-C GC.
+
+ # TODO use an adapter for the app.nit ListLayout closer to what exists
+ # on both iOS and Android, to support large data sets.
+
+ if item isa View then
+ add_view_to_native_list(native, item.native)
+ end
+
+ super
+
+ # Force redraw and trigger callbacks
+ native.reload_data
+ end
+
+ private fun add_view_to_native_list(native: UITableView, item: UIView) in "ObjC" `{
+ [((UITableViewAndDataSource*)native.delegate).views addObject:item];
+ `}
+
+ private fun get_view_from_native_list(native: UITableView, index: Int): UIView in "ObjC" `{
+ return [((UITableViewAndDataSource*)native.delegate).views objectAtIndex:index];
+ `}
+
+ # Number of sections in this view
+ #
+ # By default, we assume that all `items` are in a single section,
+ # so there is only one section.
+ #
+ # iOS callback: `numberOfSectionsInTableView`
+ protected fun number_of_sections_in_table_view(view: UITableView): Int
+ do return 1
+
+ # Number of entries in `section`
+ #
+ # By default, we assume that all `items` are in a single section,
+ # so no matter the section, this returns `items.length`.
+ #
+ # iOS callback: `numberOfRowsInSection`
+ protected fun number_of_rows_in_section(view: UITableView, section: Int): Int
+ do return items.length
+
+ # Title for `section`, return `new NSString.nil` for no title
+ #
+ # By default, this returns no title.
+ #
+ # iOS callback: `titleForHeaderInSection`
+ protected fun title_for_header_in_section(view: UITableView, section: Int): NSString
+ do return new NSString.nil
+
+ # Return a `UITableViewCell` for the item at `index_path`
+ #
+ # By default, we assume that all `items` are in a single section.
+ # So no matter the depth of the `index_path`, this returns a cell with
+ # the view at index part of `index_path`.
+ #
+ # iOS callback: `cellForRowAtIndexPath`
+ protected fun cell_for_row_at_index_path(table_view: UITableView, index_path: NSIndexPath): UITableViewCell
+ do
+ var reuse_id = "NitCell".to_nsstring
+ var cell = new UITableViewCell(reuse_id)
+
+ # TODO if there is performance issues, reuse cells with
+ # the following code, but clear the cell before use.
+
+ #var cell = table_view.dequeue_reusable_cell_with_identifier(reuse_id)
+ #if cell.address_is_null then cell = new UITableViewCell(reuse_id)
+
+ var index = index_path.index_at_position(1)
+ var view_native = get_view_from_native_list(table_view, index)
+ var cv = cell.content_view
+ cv.add_subview view_native
+
+ return cell
+ end
+end
+
+redef class UITableView
+
+ # Assign `list_view` as `delegate` and `dataSource`, and pin all references in both GCs
+ private fun assign_delegate_and_data_source(list_view: ListLayout)
+ import ListLayout.number_of_sections_in_table_view,
+ ListLayout.number_of_rows_in_section,
+ ListLayout.title_for_header_in_section,
+ ListLayout.cell_for_row_at_index_path in "ObjC" `{
+
+ UITableViewAndDataSource *objc_delegate = [[UITableViewAndDataSource alloc] init];
+ objc_delegate = (__bridge UITableViewAndDataSource*)CFBridgingRetain(objc_delegate);
+
+ objc_delegate.nit_list_layout = list_view;
+ ListLayout_incr_ref(list_view);
+
+ // Set our
+ self.delegate = objc_delegate;
+ self.dataSource = objc_delegate;
+ `}
+end
new bottom in "ObjC" `{ return UIStackViewAlignmentBottom; `}
new last_baseline in "ObjC" `{ return UIStackViewAlignmentLastBaseline; `}
end
+
+# View to display and edit hierarchical lists of information
+extern class UITableView in "ObjC" `{ UITableView * `}
+ super UIView
+
+ # Wraps: `[self initWithFrame:(CGRect)frame style:(UITableViewStyle)style]`
+ new (style: UITableViewStyle) in "ObjC" `{
+ return [[UITableView alloc] initWithFrame: [[UIScreen mainScreen] bounds] style:style];
+ `}
+
+ # Wraps: `[self reloadData]`
+ fun reload_data in "ObjC" `{ [self reloadData]; `}
+
+ # Wraps: `self.autoresizingMask =`
+ fun autoresizing_mask in "ObjC" `{
+ self.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
+ `}
+
+ # Wraps: `self.delegate =`
+ fun delegate=(delegate: UITableViewDelegate) in "ObjC" `{ self.delegate = delegate; `}
+
+ # Wraps: `self.dataSource =`
+ fun data_source=(source: UITableViewDataSource) in "ObjC" `{ self.dataSource = source; `}
+
+ # Wraps: `[self dequeueReusableCellWithIdentifier]`
+ fun dequeue_reusable_cell_with_identifier(identifier: NSString): UITableViewCell in "ObjC" `{
+ return [self dequeueReusableCellWithIdentifier:identifier];
+ `}
+end
+
+# Delegate for a `UITableView` to configure selection, sections, cells and more
+extern class UITableViewDelegate in "ObjC" `{ id<UITableViewDelegate> `}
+ super NSObject
+end
+
+# Mediator the data model for a `UITableView`
+extern class UITableViewDataSource in "ObjC" `{ id<UITableViewDataSource> `}
+ super NSObject
+end
+
+# Cell of a `UITableViewCell`
+extern class UITableViewCell in "ObjC" `{ UITableViewCell * `}
+ super NSObject
+
+ new (identifier: NSString) in "ObjC" `{
+ return [[UITableViewCell alloc]
+ initWithStyle:UITableViewCellStyleDefault
+ reuseIdentifier:identifier];
+ `}
+
+ # Wraps: `[self textLabel]`
+ fun text_label: UILabel in "ObjC" `{ return [self textLabel]; `}
+
+ # Wraps: `[self contentView]`
+ fun content_view: UIView in "ObjC" `{ return [self contentView]; `}
+end
+
+# Style of a `UITableView`
+extern class UITableViewStyle in "ObjC" `{ UITableViewStyle* `}
+ super NSObject
+
+ new plain in "ObjC" `{ return UITableViewStylePlain; `}
+ new grouped in "ObjC" `{ return UITableViewStyleGrouped; `}
+end