Custom Cell in UITableView programmatically? - objective-c

I have a UITableView which gets loaded with data from a MutableArray. The thing is, within this array I have many different attributes, therefore I would like to enter these attributes into a custom cell programmatically.
Also I would like to include a Slider within the Cell so there will need to be some sort of addition there.

You say you've looked at tutorials, but have you checked out the "UITableView construction, drawing and management (revisited)" post on http://cocoawithlove.com - this pretty much covers the full custom UITableViewCell gamut, including loading a custom view from a NIB.
Irrespective, one potential approach is to create your own custom cell that expends the UITableViewCell and simply add a UISlider property which you'd then set up within the - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier method and add it as a sub view via [[self contentView] addSubview:yourUISlider];, etc.
However, I'd be tempted to have a good look at the http://cocoawithlove.com article, and download the provided project as this will show you the available options in full.

Related

Sharing code between a UITableView and a UICollectionView

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:].

Setting Up a UICollectionView in iOS

I've been hunting around for ways to setup a UICollectionView for an iOS app. Google only turns up a few blogs with posts about what it is, not how it works. Then of course, there's the Apple documentation which is helpful, but doesn't provide as much information as I'd like to be able to setup a UICollectionView.
How can one setup a UICollectionView?
The uicollectionview class is almost identical to the uitableview Class. They share many of the same methods and functions. And if the methods / functions are different, most of the time it's just a matter of swapping out "row" for "cell" and vice versa. However there are a few methods that don't exist on UICollectionView that do on UITableView. First though, I'll explain how to setup a UICollectionView:
Begin by adding your UICollectionView to a current ViewController, or creating a new UICollectionViewController. The steps aren't that much different for the view and controller.
If you're using the View and not the ViewController, make sure that the Delegate and DataSource of the CollectionView is the view controller it's on. Also make sure to add the Delegate and DataSource to your header file: <UICollectionViewDataSource, UICollectionViewDelegate>
Next, make sure to include these three methods in your view controller's class:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
These are the only required methods. The first tells the collection view the number of sections it should have. This should return an integer value. The second method gets the number of cells in each section. Again, this should return an integer value. The last method populates each cell using the data given (usually from an NSArray). This last method should return a CollectionViewCell. If you set breakpoints on this method, you'll notice that it is called once for every cell defined in the numberOfItemsInSection method.
UICollectionViews provide advanced animation methods and allow cells to be deselected and selected (similar to apps like Pages when in 'Edit' mode). However, to my knowledge, UICollectionViews do not provide features such as "swipe to delete" or and kind of disclosure indicator.
UICollectionViews also allow you to create custom cells using xib (AKA nib) files, this allows for some very advanced-looking and unique interfaces without lots of complicated code.
Sadly, UICollectionView is only supported in iOS 6 and up. There are a few projects available such as PSTCollectionView which adds support for CollectionViews in iOS 4.3+, but I haven't figured out how to use them. In my case, when the view loads I just check if the UICollectionView Class is available and if it isn't then I load a Table instead.
Here is a link to Apple's official documentation on Collection Views. You might also want to check out this tutorial.
I created a step-by-step tutorial for setting up UICollectionViews with custom layouts. Hopefully it helps some people to get familiar with the API.

Proper way to build custom UITableViewCell

I've been wondering about UITableView's and their cells for a while. UITableView is a very handy UIView subclass, but there is a lot of predetermined/forced content on a UITableViewCell. I was wondering what is the best way to create a custom UIView (which will end up being a UITableViewCell) for UITableView?
The cell has a certain style that has to be set and there are predetermined UILabels and accessory views that are completely immutable, other than their contents. I find this to be a big waste, but just giving the cell a custom content view (and background view, if one pleases) doesn't prevent or remove these processes or restore the memory.
Is there any way to create a lighter version of a UITableViewCell subclass? Or is there a way to use a UIView with a selection method instead (other than essentially creating a custom UITableView using UIScrollView)?
Any suggestions would be really appreciated.
There are a few methods:
From a XIB file:
ios steps to create custom UITableViewCell with xib file
http://www.icodeblog.com/2009/05/24/custom-uitableviewcell-using-interface-builder/
Using Drawing:
http://cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html
http://developer.apple.com/library/ios/#samplecode/TableViewSuite/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007318 (4th and 5th examples)
There doesn't seem to be an answer for this. UITableViewCells will always have UILabels and other views automatically added to them. I'm not sure if they are allocated before you set them, though. They may have custom setting / allocation methods.

NSTableView Drag and Drop not working

I'm trying to set up very basic drag and drop for my NSTableView. The table view has a single column (with a custom cell). The column is bound to an NSArrayController, and the array controller's content array is bound to an NSArray on my controller object. The data displays fine in the table. I connected the dataSource and delegate outlets of the table view to my controller object, and then implemented these methods:
- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard
{
NSLog(#"dragging");
return YES;
}
- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)op
{
return NSDragOperationEvery;
}
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info
row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation
{
return YES;
}
I also registered the drag types in -awakeFromNib:
#define MyDragType #"MyDragType"
- (void)awakeFromNib
{
[super awakeFromNib];
[_myTable registerForDraggedTypes:[NSArray arrayWithObjects:MyDragType, nil]];
}
The problem is that the -tableView:writeRowsWithIndexes:toPasteboard: method is never called. I've looked at a bunch of examples and I can't figure out anything I'm doing wrong. Could the problem be that I'm using a custom cell? Is there something I'm supposed to override in the cell subclass to enable this functionality?
EDIT: Confirmed. Switching the custom cell for a regular NSTextFieldCell made dragging work. Now, how do I make drag and drop work with my custom cell?
I was banging my head against the wall in search of a more elegant solution to the same problem, and then I came across this:
http://www.wooji-juice.com/blog/cocoa-10-bindings.html
For instance, if you want to support drag-and-drop from a table, you need to set up a data source for it — even if you’re using bindings to supply the actual data, you can set a data source on it, and Cocoa needs one to handle the tableView:writeRowsWithIndexes:toPasteboard and related messages.
Yep. If you've got everything bound to your array controller, what you can do is to have the array controller implement the necessary drag/drop functions and then set the table view's data source to the array controller with setDataSource:.
I fixed the issue. There seems to be an issue with using bindings with custom NSCells in a table view. Switching to the traditional NSTableViewDataSource methods rather than bindings and an array controller solved it.
What works for me is to call initTextCell rather than init or initImageCell within the initializer of my custom cell (in my case, init). It doesn't seem to matter whether the superclass is NSCell or NSActionCell. Also, I have binding, and dragging still works.
That should be sufficient to allow the drag to start. Are you sure you've connected the delegate methods?
I ran into this problem, your custom cell needs to extend NSActionCell not NSCell if you want drag and drop to work properly. There is probably something you could implement in NSCell that would make it all work too, but I didn't dig any further after switching to NSActionCell. At least, that fixed the issue for me.
I ran into the problem. I have a NSCell subclass, and I did implement the tableView:writeRowsWithIndexes:toPasteboard and the dataSource was set for the NSTableView. Dragging would not work.
If I set the cell type in my init for the custom subclass
self.type = NSTextCellType;
Then I get dragging. If I don't, it defaults to NSNullCellType and dragging doesn't work. I'm guessing the people who got it working by using another subclass NSTextFieldCell works because the cell type is different.
I also observe the similar issue, NStableView drag & drop is not working. I have 4 column in my tableview and two of them are custom cells. Dragging for non-custom cell is working fine even though its working on cell separator as well however its not working with custom cells.
My custom cell was subclassed from NSButtonCell which was causing the
issue. So as suggested, I changed my parent class from NSButtonCell to
NSActionCell. Now, dragging is working perfectly.
It works with both NSCell as well as NSActionCell however I required action on my cell so used NSActionCell.

How to use NSCollectionView and Outlets properly?

I'm desperately trying to connect controls of NSViews which will reside in a NSCollectionView using outlets. The collection view is fed using an NSArrayController.
I created the NSView in a separate NIB file and in the implementation of NSCollectionViewItem I overwrote copyWithZone to load it:
-(id)copyWithZone:(NSZone *)zone
{
id result = [super copyWithZone:zone];
[NSBundle loadNibNamed:#"InputView" owner:result];
return result;
}
I've used this approach according to this instructions.
Unfortunately this is what happening:
The NSView looks like this:
The NSCollectionView resides in a NSScrollView and the scrollbar is set to enable automatically.
But as you can see there's no scrollbar.
I don't really understand what I need to do so the NSCollectionView knows the dimensions of its NSViews.
It has worked before when I didn't have a seperate NIB-file, but then I couldn't make outlet connections from the view to the item :-(
How many item are in the array controller? Your output looks correct for what you've described, assuming there are at least 14 things in the controller (1 view per item). The sizing is just off. It's not clear which problem you're trying to solve.
Perhaps you were looking for a grid, and so need to call setMaximumNumberOfColumns:? Or perhaps your views aren't being resized as you expect (check -maxItemSize and -minItemSize)?