I am using AQGridView class and I am trying to load a cell from an XIB. I have setup the XIB like a Custom Cell for a UITableView, but when I attempt to load the cell, it is simply blank. I was wondering if there was an easier way to get the XIB to load.
AQGridViewCell need to load the cell from an xib
- (AQGridViewCell *) gridView: (AQGridView *) gridView cellForItemAtIndex: (NSUInteger) index
{
static NSString * CellIdentifier = #"cellID";
gridCell * cell = (gridCell *)[gridView dequeueReusableCellWithIdentifier: CellIdentifier];
if ( cell == nil ){
gridCell = [[gridViewCell alloc] initWithFrame: CGRectMake(0,0,_gridView.frame.size.width/2-4,
_gridView.frame.size.height/2-8)
reuseIdentifier:CellIdentifier];
cell = gridCell;
self.gridCell = nil;
}
cell.title = #"Test Grid Item";
cell.date = #"Apr. 7, 2011";
return ( cell );
}
Here's an article that describes how to load an AQGridViewCell from nib, with example code. Check out the section called "A reusable AQGridViewCell".
(Thanks to pt2ph8 for pointing out contentView.)
From what I've understood, I think it shows as blank because what gets displayed is the cell's contentView. I ended up loading my custom view from IB and adding it as a subview of the cell's contentView when the cell is requested.
AQGridView's developers once claimed on GitHub that proper IB support will be added in the future, but that post is dated August 2010, so don't hold your breath.
This took me a while, but I figured a different way than the blog post jlstrecker mentioned.
Create a subclass of AQGridViewCell - let's call it
MyGridViewCell.
Create a nib for that cell, link it up in IB.
Pub a view ON TOP of the cell's view in IB. That's right, a view
on top of a view. Make the size the exact same.
For that view on
top of the view (let's call it view2), set the tag property (can
be done in IB) to 1.
Put everything you want to link up on top of
view2, decorate your cell, whatever you'd like.
Use the following code (of course, change it to your needs) in your subclass of AQGridViewController:
`
- (AQGridViewCell *)gridView:(AQGridView *)aGridView cellForItemAtIndex:(NSUInteger)index {
static NSString *CellIdentifier = #"MyGridViewCell";
MyGridViewCell *cell = (MyGridViewCell *)[self.gridView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = (ZZProductGridViewCell *)[[[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil] objectAtIndex:0];
}
[cell.contentView addSubview:[cell viewWithTag:1]]; //THIS IS THE IMPORTANT PART
return cell;
}
Enjoy!
I'm not familiar with AQGridView, but I believe you can leverage NSBundle's Nib loading capabilities. An excerpt from AdvancedTableViewCells sample project illustrates the idea:
RootViewController.h
#interface RootViewController : UITableViewController
{
ApplicationCell *tmpCell;
}
RootViewController.m
ApplicationCell *cell = (ApplicationCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"IndividualSubviewsBasedApplicationCell" owner:self options:nil];
cell = tmpCell;
self.tmpCell = nil;
}
Inside the IndividualSubviewsBasedApplicationCell.xib you would have to set the outlet of the UITableViewCell within to be the RootViewController's tmpCell property. Then, as a side effect of invoking NSBundle's loadNibNamed method, the tmpCell property gets set on the RootViewController via the Nib loading mechanism.
What you can do is do your xib (uiview) unpacking/loading in the subclass itself (which does have a different init method than a uitableviewcell)
you can also connect any outlets to this xib and add its entire view as a subview, or maybe replace contentview).
To make it even faster you can make uinib of this xib and reuse it to save disk i/o.
Build your cell normally using IB, then in your subclass of AQGridViewCell, add
- (void)awakeFromNib{
self.contentView.backgroundColor = [UIColor clearColor];
}
Related
I have a custom UITableViewCell defined in a xib. It has two views in its Content View, a label and a text view.
In my table view controller, using either
JJDTextInputCell *cell = [[SDLTextInputCell alloc] init];
or
JJDTextInputCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TextInputCell" forIndexPath:indexPath];
the IBOutlet is null, so the table view does not show the default text or any text I try to display using
cell.descriptionLabel.text = #"foo";
What is the proper way to initialize custom UITableView cells created using interface builder?
AFAIK, there are two ways to dequeue table cell, you can find them here in the doc. Find them under Creating Table View Cells. You are using dequeueReusableCellWithIdentifier:forIndexPath: which always returns a valid cell BUT you need to use registerNib:forCellReuseIdentifier: function in pair with this. You have to register the class and then you will get a valid cell. Check out this question for reference.
If you are using XIB for custom cell you should use
[self.tableView registerNib:[UINib nibWithNibName:#"TableViewCell" bundle:nil] forCellReuseIdentifier:"CellIdentifier"];
for registering a cell instead of
[self.tableView registerClass:[TableViewCell class] forCellReuseIdentifier:"CellIdentifier"];
Initialize them from xib file.
JJDTextInputCell *cell = [[[NSBundle mainBundle] loadNibNamed:#"JJDTextInputCell" owner:self options:nil] objectAtIndex:0];
So here I am upgrading a working ios6 app to ios7, and now I can't receive taps or other actions on custom buttons (or other subviews) inside my tableviewcells.
Edit:
My code:
Here is where I deploy my PlaceCell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"PlaceCell";
PlaceCell *cell = [tableView dequeueReusableCellWithIdentifier: cellIdentifier];
if (!cell) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PlaceCell" owner:self options:nil];
cell = [nib lastObject];
cell.reuseIdentifier = cellIdentifier;
}
[cell configureCellWithPlace: [self.places objectAtIndex:indexPath.row]];
cell.delegate = self;
cell.userInteractionEnabled = YES;
return cell;
}
And then it is a normal custom cell with buttons which are connected to some actions by the interface.
It works perfectly with iOS6, but it does nothing with iOS7.
Thank you for your help.
Solved with:
[cell.contentView setUserInteractionEnabled: NO];
Put your button into cell's contentView.
This happens when your Cell's view in xib file is not a UITableViewCell, but only a UIView. Make sure that that the xib's top view is a UITableViewCell.
You can easily check it by looking into the first child of the main view inside the interface builder. If the first subview is not "Content View" then you should rebuild the cell with UITableViewCell on the top.
Also make sure that button is a subview of the "Content View".
I had a similar problem. I had dragged a UIView into the xib to use as my UITableViewCell. Even though I changed the classname to a subclass of a UITableViewCell in Interface Builder, the events on my buttons still didn't fire. Since it was originally a UIView, IB never knew about contentView and didn't add my controls to it.
Dragging a "real" UITableViewCell into the xib, changing its class to the one I wanted, and then rewiring up the IBOutlets fixed everything. Didn't need to mess with delaysContentTouches or other properties either.
Moral of the story: drag the right thing onto your xibs.
It seems to be that when you use interface builder to customize a cell subclass all the views added are added below the contentView. This is why setting userInteractionEnabled = NO on the content view works, because touch events are allow to pass through.
I used po [view recursiveDescription] with lldb to determine this.
I made a custom table view cell - i have a header, implementation, and nib. In the nib I set the style to custom, dragged a label on it and made an outlet in the nibs file owner.
From my UITableView Controller I have this code:
static NSString *CellIdentifier = #"adbActivityCell";
adbActivityCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
//cell =[[adbActivityCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.price.text = [NSString stringWithFormat:#"cell #%d", indexPath.item + 1];
return cell;
If I run this as is XCode tells me that the UITableView Controller is not key value compliant for the label property (the label is named "price"). If I comment out the two lines above and uncomment that one line my application runs, but the label doesn't show up at all, even if I set default text for it.
I've spent quite a lot of time researching tutorials and questions on here with no luck.
Its all about view hierarchy.
You have to add your label outlet to the custom UITableViewCell, because it is the superView of your label in view heirarchy.
That means label is contained in custom cell thats why you have add outlet to custom cell.
self.view->tableView->CustomCellView->UILabel
In your customCell.h file set the IBOutlet of the label. Your problem will be solved.
Is there an easy way of having a tableview cell like we see here with numbering like this and the border around. Is this created using different sections?
You need to create a custom UITableViewCell.
If you're using storyboards look here:
See this link http://mobile.tutsplus.com/tutorials/iphone/customizing-uitableview-cell/
If not here is a rundown:
Basically create a new class that inherits from UITableViewCell and a XIB. Drag a UITableViewCell to the XIB and set it to the class that you created previously.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CustomCellIdentifier = #"CustomCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (cell == nil) {
//*- Load your custom XIB. objectAtIndex:0 says load the first item in the XIB. Should be your UITableViewCell that you dragged onto your XIB in Interface Builder.
cell = [[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil] objectAtIndex:0];
}
//*- Customize the cell, i.e., cell.myLabel.text = #"Text";
return cell;
}
Using this technique you can layout your cell with three labels, one for the number and one for the name of the song and one for the song time. Add a background image view for the border and color.
A simple way to get the song number in the table is to use the indexpath.
cell.myLabel.text = [NSString stringWithFormat:#"%i", indexPath.row + 1];
I have a basic UITableView with four sections. I control the content in each section with a Switch statement.
I programmatically create a button, which should appear in the rows of the first THREE sections, but should NOT appear in the fourth. However, the button is appearing randomly in the fourth section's rows.
I presume this is because a cell is being reused, but as I create each section's rows with the Switch statement, I cannot see how this is happening. Any ideas appreciated.
I am using a custom cell configured so:`
static NSString *CustomCellIdentifier = #"DashboardCell";
DashboardCell *cell = (DashboardCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil) { NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DashboardCell"
owner:self options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[DashboardCell class]])
cell = (DashboardCell *)oneObject;
}
// Configure the cell.`
The code to create this button is: `
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(200, 11, 50, 50);
UIImage *iConnect = [UIImage imageNamed:#"connect.png"];
[button setImage:iConnect forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonSelected:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:button];`
You need a different reuse identifier for each type of content. So here you have two types of content - cell's that have a UIButton and cells that don't.
Use the indexPath of the tableView:cellForRowAtIndexPath: method to select a reuse identifier of either #"CellWithButton" or #"CellWithoutButton".
What is actually happening in your code is that all cells are given the same reuse identifier, meaning that they all get put into the same object pool. This means that when you use [tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier]; that you are retrieving a cell from this one pool (which potentially contains cells that have no UIButton and cells that do). Therefore the dequeue method can randomly return a cell that has already had a UIButton added to it. If you use two reuse identifiers, the UITableView will setup two object pools and will correctly deposit and retrieve the appropriate cells from each.
Or you can use one reuse pool and check the cell for a UIButton each time you retrieve one using the dequeue method.
In your DashboardCell, create a property #property (nonatomic, assign) BOOL buttonEnabled. Then in your awakeFromNib, always create the button and set button.hidden = YES. Override the setter of your property
- (void)setButtonEnabled:(BOOL)enabled {
buttonEnabled = enabled;
button.hidden = !enabled;
}
And finally override prepareForReuse
- (void)prepareForReuse {
[super prepareForReuse];
self.buttonEnabled = NO;
}
And now you can enbale/disable in your configure part of the method cellForRowAtIndexPath
You can use two different cell identifiers depending on the section. Otherwise you would need to see whether the button existed in the cell that's returned from dequeueReusableCellWithIdentifier: and add one or remove an existing one if necessary.
If you're going to be reusing these cells and there's some simple logic behind showing or hiding each cell's button the first thing I would recommend is that you create the button in Interface Builder and connect it as an outlet to your UITableViewDelegate.
I would then create a setup method for the cell that you can run at any time, any number of times without it breaking it:
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString *CustomCellIdentifier = #"DashboardCell";
DashboardCell *cell = (DashboardCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil) { NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DashboardCell"
owner:self options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[DashboardCell class]])
cell = (DashboardCell *)oneObject;
}
// Configure the cell.
[cell setupWithSomeConfigOptions:someConfigOptions]
return cell;
}
And in your cell subclass you would have the method -(void)setupWithSomeOptions:(SomeOptions)someOptions, which would be something along the lines of:
-(void)setupWithSomeOptions:(SomeOptions)someOptions
{
// some setup code
self.myButtonOutlet.hidden = someOptions.somePropertyToCheckIfButtonShouldBeHidden;
// some more setup code
}