Just wondering why we can't we set the data source to UITableview in the below way rather than returning the data count in one method and returning the data for cell in different method.
Why don't we do something like below?.
UITableView *tableView = [UITableView alloc] init];
tableView.datasource = [NSArray arrayWithObjects:#"row1", #"row2", nil];
tableView.cellIdentifier = #"Identifier";
tableView.cell = [UITableViewCell alloc] init];
//delegate methods implemented as properties,
tableView.rowHeight = 50.0;
tableView.headerHeight = 100.0
tableView.headerView = [UIView alloc] init];
[self.view addSubView:tableView];
We can do this right, may not be 100 % correct, we can think of adding some more properties to make the TableView in the same way it works now.
Whey Apple designed the TableView in the way it works now.Helpful if some one could explain the pros and cons of this design with the existing design.
The problem with this implementation is that you are not getting dequeued cell which will spike your's device memory once you reached up to app's allocated maximum level of memory. On every table row, a new cell is created.
Moreover cellForRowIndexPath method gives you flexibility in customising cell based on row index path.
What if you are rendering two different custom cell, which contains different views, how can you differentiate between them without using cellForRowIndex delegate's method.
Related
I ran into difficulties with SSCollectionView and SSCollectionViewItem.
First of all I'd like to get it initialized from IB. But that won't work for me.
I have a SelectFooViewController which is:
#interface SelectFooViewController : SSCollectionViewController { ... }
and am using it as filesOwner of the corresponding XIB.
SelectFooViewController* selectFooVC = [[SelectFooViewController alloc]
initWithNibName:#"SelectFooViewController" bundle:nil];
But since it wont work I had to initialize its properties inside viewDidLoad() myself.
Furthermore I am not able to display anything except the backgroundColor of my SSCollectionViewItems. What I want is a textLabel and an image .
- (SSCollectionViewItem *)collectionView:(SSCollectionView *)aCollectionView itemForIndexPath:(NSIndexPath *)indexPath {
SSCollectionViewItem *item = [[[SSCollectionViewItem alloc] initWithStyle:SSCollectionViewItemStyleImage reuseIdentifier:itemIdentifier] autorelease];
SSLabel* label = [[SSLabel alloc] init];
[label setText:#"foo"];
item.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"foo.png"]];
item.textLabel = label;
[label autorelease];
return item;
}
I can confirm that the delegate methods (for determining the number Of rows, sections and such) are implemented and working as expected. But my items are all empty - but react onclick with the expected popup.
Does anyone see an error in what I did? - Thanks...
EDIT: I was also not able to display a local image by changing SSCatalog project
I just figured out, that I have to set the frame of each property (textLabel, detailTextLabel and imageView) myself. That fixed it.
When you create instance SelectFooViewController just insert this line
selectFooVC.view;
or
selectFooVC.view.hidden = NO;
And then add it to the view.
This is because the view is not initalised until you explicitly access it. Hence your items are loaded only when you click it and not immediately. You can call it a hack but i don't call it one. :-)
I'm implementing a TTTableViewController with a searchViewController part, and I'm running into issues when displaying the results of the search in the search's TableView.
If I add TTTableTextItems to the datasource items property, it works fine, but if I try to add a TTTableCaptionItem or a TTTableImageItem or any custom cell I've created, the rendering is messed up.
Can anyone confirm that it's possible to use custom table cells in a searchViewController and perhaps point me in the right direction for how to make it work?
I've attached 3 screen shot to show what's working and what's not working.
No. 1: Works with this code:
[self.items addObject:[TTTableTextItem itemWithText:[item objectForKey:#"title"]]];
No 2. Breaks the layout with this code:
[self.items addObject:[TTTableCaptionItem itemWithText:[item objectForKey:#"title"]
caption:[item objectForKey:#"excerpt"]]];
No. 3 Also breaks the layout with this code:
[self.items addObject:[TTTableImageItem itemWithText:[item objectForKey:#"title"]
imageURL:[item objectForKey:#"thumbnail"]]];
OK, I figured it out. The key is setting the the variableHeightRows property to YES in the TTTableViewController that you're using as the search controller. Below is the code I've used.
TTTableViewController* searchController = [[[TTTableViewController alloc] init] autorelease];
searchController.dataSource = [[[MagazineSearchDataSource alloc] init] autorelease];
searchController.variableHeightRows = YES;
self.searchViewController = searchController;
self.tableView.tableHeaderView = _searchController.searchBar;
I am creating a custom cell class wherein I am putting different types of subviews in my init method but with frame as CGRectZero.
self.subTitleLabel = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];[self.contentView addSubview:self.subTitleLabel];
self.scannedProductLabel = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
[self.contentView addSubview:self.scannedProductLabel];
self.requestStatusLabel = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
[self.contentView addSubview:self.requestStatusLabel];
In my layoutSubviews method, I am using these labels based on my need. Like, for one type of cell I will use first label and for other type will use another label.
if ([self.cellType isEqualToString:#"CustomerDetails"] ) {
//self.productImageView.frame = CGRectMake(aContentRect.origin.x + kCellOffset, 0.0f, aTitleCellWidth , floorf(aHeight/4));
self.titleLabel.frame = CGRectMake(aContentRect.origin.x + kCellOffset, 0.0f, aTitleCellWidth , floorf(aHeight/2));
self.subTitleLabel.frame = CGRectMake(aContentRect.origin.x + kCellOffset, floorf(aHeight/2), aTitleCellWidth, floorf(aHeight/4));
self.requestStatusLabel.frame = CGRectMake(aContentRect.origin.x + kCellOffset, floorf((aHeight/2) + (aHeight/4)), aTitleCellWidth , floorf(aHeight/4));
}
My question is that is it a good idea to do this from memory perspective. As though my purpose is resolved but my custom cell object contains sub views which are in the memory but not visible. If yes, then what is the alternate approach for this kinda scenario.
If not all of the subviews are going to get used for each cell type, I suggest lazily creating each subview when it is first accessed. This could be accomplished by overriding its getter like this:
- (UILabel)subTitleLabel {
if (subTitleLabel == nil) {
subTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(kSubTitleX, kSubTitleY, kSubTitleWidth, kSubTitleHeight)];
[self.contentView addSubview:subTitleLabel];
}
return subTitleLabel;
}
This way, subTitleLabel is created and added to the content view the first time it is accessed through its getter. If you never call the getter, e.g., because the type of cell doesn't require it, self.subTitleLabel will never get called and subTitleLabel will never get created.
I wouldn't think memory issues here would be that big of a problem. If you are correctly reusing cells then you probably only create 5-10 cells in your list that are constantly recycled.
Though one alternative solution would be to use the celltype as the reuse identifier and only create the subviews that are needed for that type. This way when you get a cell for a particular type you know it will have only the required fields. This could create more cells though which may actually take up more memory.
I have a UITableView with cells that contain a UISwitch control. It's similar to the table view in the iPhone's Clock app shown below...
(source: epicself.com)
In my app's cellForRowAtIndexPath method, I create and attach the UISwitch control like so...
CGRect frameSwitch = CGRectMake(215.0, 10.0, 94.0, 27.0);
UISwitch *switchEnabled = [[UISwitch alloc] initWithFrame:frameSwitch];
[switchEnabled addTarget:self action:#selector(switchToggled:) forControlEvents:UIControlEventValueChanged];
cell.accessoryView = switchEnabled;
My question is, when the switch is toggled by the user and the switchToggled method is called, how can I tell which table cell it belongs to? I can't really do much with it without knowing it's context.
Thanks so much in advance for your help!
In your action method, you can cast the superview of the passed-in UISwitch (which is the UITableViewCell itself), then pass that to the tableView's indexPathForCell method.
indexPathForCell will return a NSIndexPath object, which you can then use to index to your datamodel to modify. Then all you gotta do is call reloadData on your tableView.
Also, in cellForRowAtIndexPath, you should set the UISwitch's state based on your model.
First of all, fix the memory leak:
UISwitch *switchEnabled = [[[UISwitch alloc] initWithFrame:frameSwitch] autorelease];
Then pick one of these options:
switchEnabled.tag = someInt; // before adding it to the cell
NSLog(#"switched %d",switch.tag); // to see which switch was toggled
or
UITableViewCell *parentCell = switchEnabled.superview;
// + some magic to see which cell this actually is
I have a grouped UITableView that has 3 sections and a number of cells in each section that each need to be a different custom cell, with different display requirements.
I currently have a large if statement in cellForRowAtIndexPath, with each branch instantiating and returning the appropriate custom cell based on interrogating the indexPath's section and row properties. e.g.
if (indexPath.section == 0 && indexPath.row == 0) {
// instantiate and return cell A
} else if (indexPath.section == 1 && indexPath.row == 2) {
// instantiate and return cell B
} //etc etc
What is best practice for this scenario? A large if statement does the job, but is it the best implementation?
By far the best way to do this is described in this post from Cocoa With Love. Basically it's a custom controller for each cell type but it ends up being really simple to implement.
One method I've used once or twice is a nested series of NSArray objects that I initialize in viewDidLoad:
// sec<n>Row<n>Cell are IB Outlets to UITableViewCell objects defined in your .xib
NSArray *firstSectionRows = [[NSArray alloc] initWithObjects: sec1row1Cell, sec1row2cell, nil];
NSArray *secondSectionRows = [[NSArray alloc] initWithObjects: sec2row1Cell, sec2row2cell, nil];
// cellTree is an instance variable
cellTree = [[NSArray alloc] initWithObjects: firstSectionRows, secondSectionRows, nil];
[firstSectionRows release];
[secondSectionRows release];
Then in your tableView:cellForRowAtIndexPath: method would look like this:
...
return [[cellTree objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
...
This assumes that each of your cells is unique, in which case you don't even need to try and dequeue reusable cells. If you have more than one of the same type of cell in any section, you'll have to create/dequeue them appropriately and assign them a unique cell-type identifier.
If you have a large or complicated structure you might be able to do your array setup in a .plist file and use NSArray's initWithContentsOfFile: method to read it in. However you will have to do some kind of KVC magic to get cell objects from the strings in your array:
return [self valueForKey:[[cellTree objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]];
On complex table views I've considered splitting up the UITableViewDataSource routines into sub-delegates, one delegate for each section. So for 3 sections, I would create 3 separate objects each one responsible for providing the UITableViewDataSource methods for that section of the table. Then the UITableView's delegate just dispatches to one of the appropriate sub-delegates depending on which section the request is for.
I think this is pretty much common practice. Just be sure that you specify a unique cell identifier for those different cells. So that caching of the cells works properly.