adding subviews to cell contentView - iphone

So I have the following code:
static NSString *CellIdentifier = #"RecommendationCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"TableViewCell"] autorelease];
}
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[indicator setCenter:CGPointMake(0, 15)];
[indicator startAnimating];
[indicator hidesWhenStopped];
UILabel *someLabel.........
UIView *containerView = [[UIView alloc] initWithFrame:CGRectZero];
[containerView setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
[containerView setAutoresizesSubviews:YES];
[containerView setBackgroundColor:[UIColor clearColor]];
[containerView addSubview:indicator];
[containerView addSubview:someLabel];
[containerView setFrameSize:CGSizeMake(indicator.frameWidth+self.loadingGeniusLabel_.frameWidth, 30)];
[containerView setCenter:CGPointMake(cell.contentView.center.x, 15)];
[cell.contentView addSubview:containerView];
[indicator release];
[containerView release];
return cell;
My question is, is the code above efficient/clean? The reason I ask is because if the cell that we get is from the reusable deck, then it would have the UIActivityIndicator and the necessary view in it right? Do I just have to add the subviews only if I am allocating a new cell (i.e: when the cell == nil)?

is the code above efficient/clean?
No
if the cell that we get is from the reusable deck, then it would have the UIActivityIndicator and the necessary view in it right
Yes, but since you are using the generic UITableViewCell, you won't be able to access the UIActivityIndicator after adding it once. You'll need to create a subclass of UITableViewCell to do this efficiently.
Do I just have to add the subviews only if I am allocating a new cell (i.e: when the cell == nil)?
Yes
Only call addSubview outside of the if (cell == nil) block if you absolutely need to, it's an expensive method call and will seriously impact your frames per second when scrolling the table.
Your best bet is subclassing UITableViewCell. That way, any objects/UIViews (or UIView subclasses) that you need to control the value/behavior of differently from cell to cell are better suited as properties on the UITableViewCell subclass. By doing this, you can instantiate them either in the xib file, or in the cell setup (inside that if statement), and then simply change the values for each cell (rather than creating the new objects each time).
Apple's Table View Programming guide discusses this in depth:
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/AboutTableViewsiPhone/AboutTableViewsiPhone.html#//apple_ref/doc/uid/TP40007451
Apple's sample project shows a couple different ways for managing table cells efficiently:
https://developer.apple.com/library/ios/#samplecode/TableViewSuite/Introduction/Intro.html

Related

removing contentview objects only in particular row?

when I add label in UItablevewcell if it is as nil in contentview of cell.If it is not nil,
I am taking that label through tag without allocating.it is the right procedure to reuse the cell.
but when I dont want that label in second row , i have to hide it.How can I remove the label in second row only
without hiding.I need it in first row.
For example you can use different cell identifiers when dequeue and create them. #"Cell With Label" and #"Cell Without Label" for instance.
Or you can tag this label by label.tag = MY_INT_TAG and search it by UILabel *label = [cell viewWithTag:MY_INT_TAG] to remove it from super view in the second row. It works when you don't want to subclass UITableViewCell.
if (indexPath.row == 0) {
UILabel *label = [[UILabel alloc] init];
label.tag = TAG;
[cell.contentView addSubview:label];
} else if (indexPath.row == 1) {
UILabel *label = [cell.contentView viewWithTag:TAG];
[label removeFromSuperView];
}
When you are reusing the cells which have no common elements, the best practice is to clear the cell subviews (all added elements) before reusing it.
This way you can add the elements each time depending on your needs...
You can do this:
for(UIView *view in cell.contentView.subviews){
[view removeFromSuperview];
}
or if you want to be more fancy:
[cell.contentView.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
Of course if you want to clear only 1 particular element in a particular row, then you must assign a unique tag to the element when you add it to the cell's contentview, then remove it by accessing it through it's tag value:
Add it to the cell:
UIImageView *rightArrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrow.png"]];
rightArrow.tag = 111;
rightArrow.frame = CGRectMake(290, 16, 4, 8);
[cell.contentView addSubview:rightArrow];
Remove it from the view for row 2:
if (indexpath.row == 2) {
UIImageView *rightArrow = (UIImageView *)[cell.contentView viewWithTag:111];
if (rightArrow)
[rightArrow removeFromSuperView];
}

UIScrollView and UIPageControl within UITableView

I've seen lots of sources saying it is possible to include a UIScrollView with UIPageControl inside a UITableViewCell to be able to scroll horizontally (through a list of selectable images), but can't find any raw examples that does what I want. I've gotten my scroll view "working", but I am unsure how to go forward - hopefully someone can send me some guidance.
Within my cellForRowAtIndexPath, I create a cell, and add both a scrollView and pageControl as subviews.
UITableViewCell *cell = [self.challengeListView dequeueReusableCellWithIdentifier:SubmitChallengeCellIdentifier];
if(cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SubmitChallengeCellIdentifier] autorelease];
cell.frame = CGRectMake(0, 0, 1000, 50);
}
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, tv.frame.size.width, 50)];
[scrollView setContentSize:CGSizeMake(1000, 50)];
[[cell contentView] addSubview:scrollView];
pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 50, tv.frame.size.width, 50)];
[pageControl setNumberOfPages:4];
[[cell contentView] addSubview:pageControl];
I've attached a screenshot of what's being displayed
the bottom portion of the main view is my UITableView that contains the scrollView/pageControl (and it will scroll horizontally, as I can see the scrollerIndicator showing this), and I've got its method's set to the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
My Scroll view will indicate a "horizontalScroller" and I am able to scroll back and forth, but obviously there's no content there. How do I go about populating the tableView/scrollView with say, a list of "clickable" images? Any direction would be greatly appreciated - preferably not a "hey this guy's done it somewhat similar, check out this link" but maybe more an explanation of how this functionality should be implemented correctly (ESPECIALLY in regards to iOS 3.0+ - it is my understanding Apple has made our lives easier in implementing this)
I've solved my own problem; maybe the reason no once answered me is because its a minor implementation once you understand each view's purpose.
From within cellForRowAtIndexPath:
I created a standard UITableViewCell, however I altered the frame of the cell to my own custom frame of 1000 width by 50 height (catered to my needs for project).
I then created a UIScrollView, set it to the following (keep in mind I have my tableView defined in IB, so I'm mapping some of my height/widths to those values):
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, tv.frame.size.width, 78)];
I then create the desired image view (I realize I will next create a loop that does many images and lays them out across the scroll view):
UIImage *image = [UIImage imageNamed:#"dummy.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(0, 0, 80, 78);
[scrollView addSubview: imageView];
Here's the part I was missing. After adding the scrollView to the cell contents, you need to use the UIPageControl (which didn't seem obvious to me for this implementation at first) to setup the actual "visual horizonal scrolling" affect:
[[cell contentView] addSubview:scrollView];
pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 50, tv.frame.size.width, 50)];
[pageControl setNumberOfPages:4];
[[cell contentView] addSubview:pageControl];
Hope that helps someone's search - I spent quite some time on Google looking for the example I just explained and didn't have much luck other than the general overview of how it would work.

How to access a UIActivityIndicatorView in a UITableView's section header view?

I want to do something pretty simple with my UITableView: I want to add a UIActivityIndicatorView to a section's header view, and make it animate or disappear whenever I want.
I had no trouble adding the UIActivityIndicatorView to the header view using tableView:viewForHeaderInSection:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView* customView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 60.0)];
// create the title
UILabel * headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(15.0, 12.0, 310.0, 22.0)];
headerLabel.text = #"some random title here";
[customView addSubview:headerLabel];
[headerLabel release];
// Add a UIActivityIndicatorView in section 1
if(section == 1)
{
[activityIndicator startAnimating];
[customView addSubview:activityIndicator];
}
return [customView autorelease];
}
activityIndicator is a property of my controller's class.
I alloc it in the viewDidLoad method:
- (void)viewDidLoad
{
(...)
activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(200, 10, 25, 25)];
}
This way I can send messages to it (like -startAnimating or -stopAnimating) whenever I want.
The problem is that the activityIndicator disappears as soon as I scroll the tableView (I guess it is because the tableView:viewForHeaderInSection: method is called a second time).
How else can I add an activityIndicatorView to the section's header view and still be able to send messages to it afterwards? (with the activityIndicator not disapearing when I scroll down of course)
Thank you very much!
If you try to use the same activity indicator in multiple places then it is probably getting moved from one place to the other. I believe you need a different one for every single section header. You might want to use a MutableArray to keep track of the header views you create so you can reuse them if you find one in the array that doesn't have a superview, sort of like dequeuing and reusing cells.
This is just a guess as I haven't done this, but I'm pretty sure the issue is trying to reuse the same view in multiple places.
The problem seemed to be caused by re-creating a customView and adding the activityIndicator as a subview every time tableView:viewForHeaderInSection: is called.
Not using subviews helped me fix it:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
// Add a UIActivityIndicatorView in section 1
if(section == 1)
{
[activityIndicator startAnimating];
return activityIndicator;
}
UIView* customView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 60.0)];
// create the title
UILabel * headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(15.0, 12.0, 310.0, 22.0)];
headerLabel.text = #"some random title here";
[customView addSubview:headerLabel];
[headerLabel release];
return [customView autorelease];
}
(it looks quite ugly though, the activityIndicator takes the whole width of the section. I'd better create a unique customView for section 1 and add the activityIndicator as a subView once and for all).

iPhone UITableView PlainStyle with custom background image - done "entirely" in code

I have been all over the place, seems the UITableView with a static background issue is well documented, but no one with a straight forward solution?
Im building my TableViews entirely in code, like this:
UIViewController *tableViewController = [[TableViewController alloc] init];
navigationController = [[UINavigationController alloc]
initWithRootViewController:tableViewController];
[tableViewController release];
[window addSubview:navigationController.view];
The window is my main UIWindow build for me in the app delegate. From here on I need to build a few different TableViews (controlled by the navigationController), some with fetchedResultsControllers, custom cells and so on. I prefer to do this completely in code, not using nib's as this would result in either having customization spread between code and IB or having to build and maintain 6+ different Nibs.
I simply can't find a working example where a tableViewController Class sets it's own background image. If I do this inside one of my TableViews (extending UITableViewController):
self.tableView.backgroundColor = backgroundColor;
I, of course, get the tableView's background colored (which incidentally colors the cell's as well, think the cell's inherits their color from the tableView?) but I wish to have a static background image that my cells slide up and down on top of. Not a "background image" that slides up and down with the users gestures.
Exactly what the GroupedStyle tableView offers, but in a PlainStyle tableView:) .. and done using code, not IB.
I guess I have to clear the background color of the table view, then set the Cells color when configuring them so they don't turn out transparent. And then somehow "sneak" a background image below the tableView view from inside the tableView instance?
How will I go about this, the best solution would to be able to do this in viewDidLoad or any other function inside my TableViewController, to keep all my customization in one place.
Hope someone can help me, Im all 'googled out' :) Thanks!
You need to set up your controller as a UIViewController, not a UITableViewController. Then add the tableview programmatically above a background imageView.
#interface SomeController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
...
UITableView *tableView;
...
}
#property (nonatomic, retain) UITableView *tableView;
#end
#implementation SomeController
#synthesize tableView;
...
- (void)loadView {
[super loadView];
UIImageView *v = [[[UIImageView alloc] initWithFrame:self.view.bounds] autorelease];
[v setImage:[UIImage imageNamed:#"table_background.png"]];
[self.view addSubview:v];
self.tableView = [[[UITableView alloc] initWithFrame:self.view.bounds] autorelease];
[self.tableView setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:self.tableView];
}
...
#end
All of this jumping through hoops is unnecessary if you're targeting > iOS 3.2, where you can use the new backgroundView property to set a UIImageView directly, e.g.:
// In your UITableViewController subclass
- (void)viewDidLoad
{
[super viewDidLoad];
UIImageView *view = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background.png"]];
self.tableView.backgroundView = view;
}
Ok, now it is running:)
My tableView was not populated with my cells, so breakPointing through the thing I found out
that even though I had implemented the TableViewDataSource and TableViewDelegate, this was only in the main view, I needed to set the delegate and datasource of the tableview to = self.
For others seeking an answer to this here is the method as it ended up with Coneybeares help:
- (void)loadView {
[super loadView];
UIImageView *imageView = [[[UIImageView alloc] initWithFrame:self.view.bounds] autorelease];
[imageView setImage:[UIImage imageNamed:#"carbon_background.png"]];
[self.view addSubview:imageView];
[self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds] autorelease];
[self.tableView setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:self.tableView];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
Thanks Coneybeare.
It doesn't crash anymore and the background image turns up just perfect (along with my navigationController in the top)
However, still no visible tableView? just the background image and the navigationControllerBar:
This is my implementation:
- (void)loadView {
[super loadView];
UIImageView *imageView = [[[UIImageView alloc] initWithFrame:self.view.bounds] autorelease];
[imageView setImage:[UIImage imageNamed:#"carbon_background.png"]];
[self.view addSubview:imageView];
[self.tableView = [[UIView alloc] initWithFrame:self.view.bounds] autorelease];
[self.tableView setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:self.tableView];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)theTableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section {
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [theTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = #"Hello, World";
return cell;
}
//commit edit, didSelectRow, memory … etc.
The forum wasn't up for an entire .m file in one go.
Please tell me if I left something out that could help indicate an error.
I thought maybe it was the order of the layers and tried this without any luck:
[self.view sendSubviewToBack:imageView];
Hope I missed something obvious.
Thanks for your time:)
Some tips:
If using a UISplitViewController:
splitViewController.view.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
If using a UINavigationController:
navigationController.view.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
If using both a UISplitViewController and a UINavigationController:
navigationController.view.backgroundColor = [UIColor clearColor];
splitViewController.view.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
Be sure to set the background color of any UIView you want to see through that is on top of the UINavigationController or UISplitViewController to [UIColor clearColor].

Can't get keyboard to resign consistently with UITextView in UITableViewCell

I have a UITextView within a UITableView cell. I have been unable to get the keyboard to consistently resign after editing. Detecting DidEndEditing hasn't worked. Adding my own "done" button to the toolbar brings intermittent results. Advice?
(Note: This is UITextView not UITextField. Thanks)
Do you dismiss the table view's controller after you're done editing? I've encountered a non-deterministic crash that occurred when performing [textView resignFirstResponder] plus a call (something like [self doneClicked:nil]) that would dismiss the view controller that hosted the UITableView.
It would release the UITextView and when the call came back into the UITextView method that originated the didEndEditing call, it would crash or behave inconsistently (since the view had been released)..
The solution was to call everything after some delay:
[self performSelector:#selector(doneClicked:) withObject:nil afterDelay:0.5]
adding the textview to the cell:
cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
managedTextView = [[[UITextView alloc] initWithFrame:CGRectMake(7,8,260, 30)] autorelease];
managedTextView.delegate = self;
managedTextView.scrollEnabled = YES;
managedTextView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
managedTextView.text=thought.managedthought;
[cell.contentView addSubview: managedTextView];
cell.accessoryType = UITableViewCellAccessoryNone;
done button code:
- (void)saveTextView:(id)sender
{
[managedTextView resignFirstResponder];
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:#selector(save:)];
self.navigationItem.rightBarButtonItem = saveButton;
[saveButton release];
...
}
(the "new" save button is used when saving the entire UITableViewController)