Sharing code between a UITableView and a UICollectionView - objective-c

My iPad and iPhone interfaces use a UICollectionView and a UITableView respectively. In each case, there is a lot of commonality:
each has the same number of sections (hard-coded)
each section has the same number of respective rows/items (derived from the same data source)
each has identical cell content (these are custom views built using auto layout so are suitable for both cases)
each has identical section headers and footers (again, custom content)
each can respond to certain notifications in the same way (e.g. when new data is received, refresh the data source) but with custom parts also (reload the UITableView vs reload the UICollectionView)
each will present the same controllers via cell selection, though the UITableView will push the new controller and the UICollectionView will use a popover.
I am using a shared parent class to cater to some of this. This approach seems especially well suited to the data requirements - the parent class builds the data and is responsible for maintaining it. I use two subclasses to present the data - one with a UITableView and the other UICollectionView.
The presentation side is a little less clean. To take the simplest example, when the table/collection view needs to know the number of sections, in each case I am relying on a customized method in the parent:
return [super sectionsCount]
This allows me to set many values only once and have both views updated.
Then comes the part that is working poorest. Again, to simplify, consider the header view for the first section. In both cases, this should be identical. I have a custom UIView subclass that I want to use, with a couple of properties that will be set the same. The problem here is that the re-usable header for a table view section expects a UITableViewHeaderFooterView and the counterpart for a collection view expects a UICollectionReusableView. So to accommodate this, I'm having to create subclasses of these simply to hold the header view I want in both. So in summary:
What I want: UITableView and UICollectionView should use the same UIView subclass as the header for section. The custom properties of that view should be set identically in each case.
What I am having to do:
Build the required UIView subclass for the header.
Build a UITableViewHeaderFooterView subclass that holds one such header. It's init does nothing more than add a header view.
Build a UICollectionReusableView subclass that equally does nothing more than add a header view.
When the collection view needs a header, create an instance of the UITableViewHeaderFooterView subclass. Set its properties.
When the table view needs a header, create an instance of the UITableViewHeaderFooterView subclass. Set its properties.
Once I have to do this for footers, and especially cells, things are getting kinda icky. I have three times the classes I should need, and I'm repeating all of my code for setting custom properties of the view.
How can I best re-use this logic between the UICollectionView and UITableView?

You can just create stock UICollectionReusableViews, UITableViewHeaderFooterViews, etc. and add your custom view as a subview. If you need to access the custom view later, you can set its tag property and use [view viewWithTag:].

Related

Get UITableView headers/footers

Ok, so I have this issue where I need to get access to the headers/footers displayed in a UITableView. Moreover, I need to do this from a subclass of UITableView (so I can't simply assign tags to the view from the UITableView Delegate). UITableView does keep an array of visible headers and footers but it provides no access to those arrays even to the subclass, which I personally think is asinine.
I need this so that I can provide a custom drag-n'-drop insertion/move user interface. I'm trying to get it to work almost exactly like Apple's own rearranging interface, but of course, with my own custom implementation (like the ability to drag from another table). Everything works perfectly except for the headers/footers.
At the moment I'm trying to hack it by iterating through all the subviews of UITableView. I'm pretty sure that the only subviews of UITableView is: backgroundView, UITableViewCells, and Headers/Footers. So I just need to determine which are UITableViewCells (very easy), which is the background view (also easy) and which are headers/footers (not so easy). I'm pretty sure I can do it this way but it's definitely a hack and I'd rather find a better way if possible.
UPDATE
I'm overriding the - (void) setDelegate:(id<UITableViewDelegate>)delegate method and checking to see if the delegate responds to the appropriate selectors to generate headers/footers (and set BOOL's appropriately). This is making it a lot easier to determine what a particular subview is since the progression of header -> cells -> footer is predictable.
You say you can't use UITableView delegate methods, but did you consider letting the UITableView subclass object be its own delegate? Create a separate property on the subclass called realDelegate and pass any other delegate calls through to that.

Storyboards and Table View Sections

I've noticed that in Storyboards, when dragging out a Table View object from the Library, you also get a chance to configure table view sections, and bunch of other options. For example, if the content is going to be static cells or dynamic prototypes, etc.
Here's a look at the Table View in Interface Builder (.storyboard file):
and here's how the Table View looks like in a .xib file:
So my question is - is it possible to configure/style (drag buttons, images, etc. into cells) a table view in a .xib file using Interface Builder or it can only be done programmatically?
Can be done either way. I prefer doing it in IB as the layout is much easier when you can see what you are doing. For dynamic prototypes, you only design a single cell and the contents will be populated in the cellForRowAtIndexPath method. With a Static Cell tableview, you can design the whole thing (many sections and many rows). The requirement for a Static Cell tableview is that the class HAS to be of type UITableViewController, while for Dynamic Prototypes, it can be either a UITableViewController or (my preference for more flexibility) a UIViewController with a UITableView.
Hint - if this is a Static cell tableview and you only design a screen full of sections and rows, be sure to turn off scrolling for the tableview.

How to initiate display of one view from another?

Assume you do not have a UIController to do the job. From inside the UIView .. how would you replace self with another UIView?
It's not very clear what you are trying to do from your question. UIController is not a class, for instance.
Ideally your app should be structured something like this:
UIViewController subclass
Controls a set of objects that are all on screen at one time. For example, any number of UITextFields, UIButtons, UIViews and UILabels.
has methods (IBActions and other delegate methods) which are triggered by user interaction with the controls and inputs.
has IBOutlets which allow it to manipulate what the user sees on screen. For instance an IBOutlet attached to a UILabel allows changing the text when a user presses a button.
UIView is only generally subclassed if you need custom drawing code, or some kind of custom control. Don't put application logic here if you can help it, and you can usually help it.
You can have multiple UIViewControllers but they usually function very independently. Often View Controllers don't maintain references for other view controllers. If they do it's loose couplings like the delegate pattern.
Bottom line: if you have two views controllers that need to communicate with each-other, you need to have a reference to one from the other. This usually occurs in the form of a property on one or both of the view controllers, and is connected either by interface builder or at run time when you create them.
You can add another subview using self.addSubview: you may also want to check self.bringSubviewToFront:
For more information, check the docs:
http://developer.apple.com/library/ios/#documentation/uikit/reference/uiview_class/uiview/uiview.html

How to bind nib custom view to a NSVIew subclass

I have a simple requirement.
On Click of a + button, I am trying to add a custom view to a SplitView.
I have created a class MyCustomView which is a subclass of NSView
In the applications nib file, I have a custom view which contains the buttons etc.
Now How to allocate a new MyCustomView every time ?
Is there an example to do this?
I am hoping something like
MyCustomView *v1 = [[MyCustomView alloc] init];
..
..
[splitView addSubView:v1];
[splitView addSubView:v2];
...
Please help
It's hard to tell exactly what you're describing based on your description but let's see if I understand you. You want to add a "copy" of your custom view assembly into a split view each time "+" is clicked, right?
The absolute best way to do this is to put the custom view assembly that will be copied (the "prototype") in its own xib. For each object you want to represent, you will instantiate a new copy from the xib and give it to some owner then add it to some parent view (a split view in your case ... odd for an unlimited number of views, but I don't have enough detail to say otherwise).
So. In the modern Cocoa world, such a view assembly should likely have its own view controller (NSViewController). This makes things easier for you since the xib's File's Owner will be an instance of your MyCustomViewController, whose -view is connected to the main container view in the xib (your custom view with all its subviews) and whose -representedObject is set to whatever model object your custom view represents. Your app will then maintain a list (an array or a dictionary, perhaps) of all the view controllers for the model objects. See this SO question/answer for a run-down of how to load from nibs/xibs.
This is basically how an NSCollectionView works (though the views must all be the same size - might not work for you). The collection view corresponds to your split view in this case; NSCollectionViewItem corresponds to your MyCustomViewController (and in fact on 10.5 and above NSCollectionViewItem is a subclass of NSViewController); your custom view is the collection view item's main -view. For each model object in its collection, it instantiates an NSCollectionViewItem and loads the view prototype from a xib (ideally, but this is optional), and uses this to set the item's view, then it sets the item's represented object (the model object).
I hope this clarifies things a bit. You've got some reading to do in order to understand enough of the nuts and bolts, but if you're still stuck, you might try editing your question to clarify or opening a new, more specific question.

Best way to have UITableView within another table?

I have a grouped UITableView in class A and if you select a row in section 0, I want it to open up to another UITableView. In the first view, I have a lot of other methods and buttons and custom designed stuff, so I don't want to create another XIB for the other table view since I'll have to copy over all the methods and custom designed stuff. I was thinking creating another XIB, but subclassing the class under the original class A this way I can use the methods of class A without having to redefine them again in the new class. But I'm having problems with this. Is there a better way? Can I have two table views in one XIB, and just hide one till the other is called up? But that seems a little messy..
If you simply try subclassing the existing viewcontroller it will have the same info for the selected row, hence it would have to grow exponentially in order to make display the right UITableView.
If your concern is redefining methods, then simply create a class that will hold those particular methods and include it in the UITableViewControllers that will be using them, that way you will only define it once. This way you can simply create a new UITableViewController and push it into a navigation controller everytime you select a given cell.
As an alternative of showing all options within one UITableView you can try the following: you can probably try adding a UIScrollView inside the UITableViewCell. I would make it scroll horizontally while keeping the UITableView scroll vertically.