Multiple UITableViewCell types implementation - iphone

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.

Related

UTableView DataSource Setup

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.

can't map single object (UIView, UIGesture) with two other objects (UIView)?

I have some confusion. Not really a bug or issue I am facing. I want to know something and I am not sure this is right place for this kind of question but here is my question:
In reference to my Question Here I was trying to map single gesture on two views.
Now I am keen to know that why this is not working? I went on basics that in case I have any NSObject subclass lets say if NSString and I add this in an NSMutableArray as follows:
NSString *strTest = #"Test String";
[aryTest1 addObject:strTest];
[aryTest2 addObject:strTest];
String gets added in both of array. Now what I am doing is I created a label, set tag for the label and added that in two views as follows:
UILabel *lblTemp = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 20)];
[lblTemp setTag:1];
[lblTemp setText:#"Label"];
[vw1 addSubview:lblTemp];
[vw2 addSubview:lblTemp];
Now every view has an array of subview. When we try to add an object in that view's subview why this is not being added in both of view?
I am getting a result that my second view is showing the label but first view is not having the label.
If I comment the last line of my code and don't add label in vw2 the label will be added in vw1. Than I tried a different thing.
UILabel *lblTemp = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 20)];
[lblTemp setTag:1];
NSMutableArray ary1 = [[NSMutableArray alloc] init];
NSMutableArray ary2 = [[NSMutableArray alloc] init];
[ary1 addObject:lblTemp];
[ary2 addObject:lblTemp];
both array has count 1. Thats mean that I have label added in my array. after that I tried to add array's object in view than even ame problem. Label is only being added in second view:
[vw1 addSubview:[ary1 objectAtIndex:0]];
[vw2 addSubview:[ary2 objectAtIndex:0]];
When I printed retainCount of Label after initialize, adding in array1 and array2 everytime the retaincount was 1. So I think the array are not keeping the reference of same object. Its a new object copied in array. Than why label is not being added in same view?
What is actual internal process for a object to add on any view's subview. As of I know subview is an Array type than what happening? Can anyone explain me?
Thanks in Advance
The difference is that addSubview automatically removes a view from it's super view before adding it to the new view (See the class reference: http://developer.apple.com/library/ios/ipad/#documentation/uikit/reference/uiview_class/uiview/uiview.html) while an array will happily accept an object even if it is in another array.
You can create a copy of the label and place it in the other view though if this is what you need to do.

SSCollectionView SSCollectionViewItem - no Items displayed

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

If I add a UISwitch control to each of my table view cells, how can I tell which cell it belongs to?

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

IB objects vs manually allocated objects in init/viewDidLoad

When I programmatically allocated a UILabel in my custom initWithNibName method, and later in viewDidLoad, tried to assign a string to it, the label was not pointing to anything. I didn't release it; the label shows on the screen. If I create the label in IB, and assign text to it in viewDidLoad, it works.
Is it against a rule to set up manually allocated objects in viewDidLoad?
Why is it not pointing to anything, even though viewDidLoad is called after my init?
From the doc of viewDidLoad:
This method is called after the view controller has loaded its associated views into memory. This method is called regardless of whether the views were stored in a nib file or created programmatically in the loadView method. This method is most commonly used to perform additional initialization steps on views that are loaded from nib files.
In my init:
_descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 218, 280, 10)];
_descriptionLabel.numberOfLines = 0;
_descriptionLabel.lineBreakMode = UILineBreakModeWordWrap;
_descriptionLabel.font = [UIFont systemFontOfSize:12.0];
_descriptionLabel.adjustsFontSizeToFitWidth = NO;
_descriptionLabel.text = #"Description not found.";
_descriptionLabel.backgroundColor = [UIColor clearColor];
In viewDidLoad, the variable's value is 0x0.
It's the same with my manually allocated UIButton, which is fully working once the view loads.
If you want to create the UILabel programatically you can, but you still do it in viewDidLoad (as opposed to initWithNibName).
Don't be afraid to do UI setup in viewDidLoad. It is provided to add any static UI elements BEFORE the view appears on screen.
The view will not appear until just before viewDidAppear:(BOOL)animated is called.
If you have dynamic content configure it in viewWillAppear:(BOOL)animated. (This looks like your situation)
Then make sure you add it to the view:
[self.view addSubview:myLabel];
If you need future access to your new label, you will need to create an ivar to hold a pointer to it.
In the code posted, the _descriptionLabel UILabel is not retained and will be released before the view is drawn.
Try
_descriptionLabel = [[[UILabel alloc] initWithFrame:CGRectMake(20, 218, 280, 10)] retain];
Make sure you put [_descriptionLabel release] in dealloc. This assumes _descriptionLabel is an instance variable.
This basically applies to any object you create with alloc, copy, or new.