Storyboard - Multiple UITableViewControllers with the same custom cell - objective-c

I occasionally find myself copy/pasting custom cells between UITableViews in my storyboard which is slowly becoming a maintenance nightmare (i.e., a simple change must now be made to each custom cell across several UITableViews).
Is it possible to create one custom cell in a storyboard or xib file and then reference it from multiple UITableViews? I'm thinking there's got to be a way to specify a NIB name for a static/prototype cell similar to how you can specify a NIB name for a UIViewController when editing a normal xib.
BTW... I know how to do this via code. What I'm looking for is a way to do this from within the storyboard editor itself.

I know of a way, but it does require a little code in a custom class. Make a subclass of UITableViewCell that loads itself from a nib. Then just set the class of your cells to this subclass. For a good method of having a view replace itself with a different version loaded from a nib see this blog post.

Actually, you don't have to create a custom class. If you're coding for version 4.0 and up, you can use a UINib to create it. You can also cache it to improve performance.
UIViewController.h
#property (nonatomic, retain) UINib *cellNib;
#property (nonatomic, retain) IBOutlet MyCustomTableViewCell *customCell;
UIViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Cache the tableviewcell nib
self.cellNib = [UINib nibWithNibName:#"MyCustomTableViewCell" bundle:nil];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCustomTableViewCell";
MyCustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[self.cellNib instantiateWithOwner:self options:nil];
cell = customCell;
self.customCell = nil;
}
// Set title and such
}
Don't forget about releasing it in dealloc if not using ARC

Related

How to access TVPosterView inside UICollectionViewCell

As usual I encountered problem it is not well explained in internet and Apple documentation is limited.
I have UICollectionView with cells. I wanted to present movie poster inside each cell.I imported TVUIkit and addedd framework inside project settings.
My FilmCelkaCollectionViewCell.h contains
API_AVAILABLE(tvos(12.0))
#interface FilmCelkaCollectionViewCell : UICollectionViewCell
#property (strong, nonatomic) IBOutlet TVPosterView *widokPlakatu;
#end
FilmCelkaCollectionViewCell.m does not contain any methods.
And in my collectionviewcontroller cell definition is as follow:
- (nonnull __kindof UICollectionViewCell *)collectionView:(nonnull UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
UICollectionViewCell *celka = (FilmCelkaCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:#"filmCela" forIndexPath:indexPath];
celka.backgroundColor =[UIColor greenColor];
return celka;
}
I added green background to see better area in simulator. But problem is I don't know how to access property TVPosterView *widokPlakatu - the only thing I can access is celka.contentView. I watched WWDC2018 video about "What's new in tvos12" and still have questions:
Does TVPosterView acts like CollectionViewCell and I can use it with UICollectionView without cells?
Is TVPosterView some special #property that cannot be accessed and must be initialized somewhere,somehow? I defined property in storyboard.
Is it forbidden to put views inside the CollectionViewCell?
OK I found solution by accident.
This
FilmCelkaCollectionViewCell *celka = [collectionView dequeueReusableCellWithReuseIdentifier:#"filmCela" forIndexPath:indexPath];
will work while typecasting will not
UICollectionViewCell *celka = (FilmCelkaCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"filmCela" forIndexPath:indexPath];

Creating a custom UITableViewCell

I'm trying to create a custom UITableViewCell.
From XCode 4.6 interface builder, I've set the Style property of the cell to Custom. And added controls to the cell using drag and drop. 2 UILables and a UIButton. It looks like this.
I created a separate class deriving from UITableViewCell to assign the properties of the 3 UI elements and make the changes there. I've set the cell's custom class as DashboardCell from the Identity Inspector as well.
DashboardCell.h
#import <UIKit/UIKit.h>
#interface DashboardCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *numberOfMails;
#property (weak, nonatomic) IBOutlet UILabel *mailType;
#property (weak, nonatomic) IBOutlet UIButton *numberOfOverdueMails;
#end
DashboardCell.m
#import "DashboardCell.h"
#implementation DashboardCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.numberOfOverdueMails setBackgroundColor:[UIColor colorWithRed:244/255.0f green:119/255.0f blue:125/255.0f alpha:1.0f]];
[self.numberOfOverdueMails setTitle:#"lol" forState:UIControlStateNormal];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
In the TableViewController, I have modified the following method to return my custom cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
DashboardCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[DashboardCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
return cell;
}
My problem is even though the custom button shows up, the changes I've done (changing the background color of the button, changing the title of one UILabel) aren't showing up. What seems to be the mistake here?
The method initWithStyle:reuseIdentifier: will not be called because you're using interface builder to create a cell.
You can set the background color and title by overriding the method awakeFromNib.
You can also set these in the method tableView:cellForRowAtIndexPath:
If you get your cell from a xib or storyboard, dequeueReusableCellWithIdentifier:forIndexPath: will always return a cell -- if one exists it will reuse it, if not it will create one from the template in IB. Therefore, your if(cell ==nil) clause will never be satisfied, and in fact is no longer needed. If you want to use an init method, then use initWithCoder:

How to Share Methods Between Classes

I want to set the text of a label to the text of a cell from a different class.
In my MasterViewController I have:
// SAVE TEXT OF SELECTED CELL
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cellString = cell.textLabel.text;
}
//SHARES VARIABLES BETWEEN CLASSES
- (ChemProjectMasterViewController*) sharedVariable
{
static ChemProjectMasterViewController *myVariable = nil;
if (myVariable == nil)
{
myVariable = [[[self class] alloc] init];
myVariable.sharedCellString = cellString;
}
return myVariable;
}
I also have this in the .h
#interface ChemProjectMasterViewController : UITableViewController
#property (nonatomic)NSString *cellString;
#property (nonatomic)NSString *sharedCellString;
+(ChemProjectMasterViewController*) sharedVariable;
#end
Now the class I want to access this method from is DetailViewController. In that I have:
detailDescriptionLabel.text = [ChemProjectMasterViewController sharedVariable].sharedCellString;
Where the detailDescriptionLabel is just a text label.
**
The program compiles fine but I have the label text set to change when I click a button. The app runs smooth until I hit the button which causes it to crash. The main goal is to set the label text in DetailViewController to the text of a cell from MasterViewController. Thank you for any help!
I am seeing few things.
First one, in .h you have declared as class method (+), while in .m it is instance (-).
Second one is #property (nonatomic)NSString *sharedCellString; which is unsafe unretained, make it strong.
And kindly let me know, if it works ?

Creating a Custom Table Cell, Cannot Access Property

IMPORTANT EDIT: I posted the wrong error code, like an idiot. I was posting the error for an attempt I had previously made to fix the issue, instead of the first error. Disregard my dumbness, please.
I'm creating a Facebook Feed app in Xcode, and I'm running into trouble in the creation of custom cells for a table. I'm trying to assign values to two UILabels on the custom cell, and it's giving me the error "No visible #interface for 'JSONFeedItemCell' declares the selector 'nameLabel'". My code is as follows:
Master View Controller
- (void)viewDidLoad
{
UINib *nib = [UINib nibWithNibName:#"JSONFeedItemCell" bundle:nil];
[[self tableView] registerNib:nib forCellReuseIdentifier:#"JSONFeedItemCell"];
... // other stuff, not relevant
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
JSONFeedItemCell *cell = [tableView dequeueReusableCellWithIdentifier:
#"JSONFeedItemCell"];
NSDictionary *p = [[[JSONFeedItemStore sharedStore] allItems]
objectAtIndex:[indexPath row]];
[[cell nameLabel] setText:#"The Name"];
return cell;
}
Cell Class
#import <Foundation/Foundation.h>
#interface JSONFeedItemCell : UITableViewCell
{
}
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
#property (weak, nonatomic) IBOutlet UILabel *detailLabel;
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
#end
Let me know if you need any additional information or code, I'd be happy to provide it.
Two things: you have to make sure.
#import "JSONFeedItemCell.h" //in your mainViewController.h
And, as Wolfgang Schreurs suggested, typecast the cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
JSONFeedItemCell *cell = (JSONFeedItemCell *)[tableView dequeueReusableCellWithIdentifier:
#"JSONFeedItemCell"];
NSDictionary *p = [[[JSONFeedItemStore sharedStore] allItems]
objectAtIndex:[indexPath row]];
[[cell nameLabel] setText:#"The Name"];
return cell;
}
EDIT: since you don't use custom setters/getters you have to synthesize the properties
in JSONFeedItemCell.m
#synthesize imageView;
#synthesize detailLabel;
#synthesize nameLabel;
Compiler should warn you if you forgot to do that but with all the possible compiler settings you never know.
Do you maybe have something like a , I call it, circle import? Xcode gets messed up when you have 2 classes which imports each other. Xcode displays sometimes 'random' errors like this. And sometimes helps to clean and organize project, and restart pc. I have actually no idea why, but it helps sometimes.

Is it possible to create a static tableview in a storyboard and add it programmatically later? (iOS5, XCode 4)

I'm trying to create a couple of small, static tableviews and add them to a panel that I have which slides in and out. The panel is created programmatically so I can't lay the tableviews out inside it via storyboard, and anyway I'm not sure if this is possible anyhow: It seems the only way you can lay out static tableviews that work is in a tableviewcontroller, which takes up the whole screen.
If you can't tell I'm pretty new to iOS dev so if I'm not understanding some fundamental concepts here please feel free to explain.
Of course is possible. Here is how it can be done:
Drag a TableViewController to your storyboard.
Set its Size to Freeform, add an identifier and uncheck Resize View From NIB
Select the tableview and set its content to Static Cells. Design your cells.
Set its size
Now, wherever you need to instantiate it do it like this:
// I am using a UITableViewController as an example here
// you probably would like to set your actual controller subclass instead
UITableViewController *tableViewController = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"staticTv"];
UITableView *tableView = tableViewController.tableView;
[self.view addSubview:tableView]; // Or add it to whatever view
Enjoy :)
A UITableViewController isn't necessary to provide the functionality you need to manage a UITableView. I think what you're looking for is the "Delegate" pattern. Any UIViewController can be assigned to be the delegate of the UITableView. For example, I have a "static" table that shows some options in an app I am working on:
#interface LBOptionsViewController : UIViewController <UITableViewDataSource,
UITableViewDelegate>
If you're creating your table views programmatically, you'll probably either be creating them in viewDidLoad or loadView (if you're creating the actual view yourself). After you've created your tableView, assign the delegates:
self.tableView.delegate = self;
self.tableView.dataSource = self;
Then your UIViewController subclass will receive the data delegate messages like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Not sure if this helps you. I have not played with Storyboards much yet.
EDIT: #Alladinian has the right answer! If you're using an property for the view controller make sure you allocate it if you need it to be called by other methods.
I've yet to find a usefully reason to use static table view cells over dynamic. Table views were pretty scary when I started iOS programming. I used sqlite in my first app YIKES.
So yeah, you should just import the UITableView Data Source and Delegate and follow up by adding the table view to your panel (assuming it's a uiview and you can add the table view as a subview).
Anyways in your ViewController.h include UITableViewDataSource and UITableViewDelegate.
#interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
Next, add properties for a UITableView and an NSMutableArray:
#property (strong, nonatomic) UITableView* tableView;
#property (strong, nonatomic) NSMutableArray* tableViewContents;
In your ViewController's .m:
#synthesize tableView;
#synthesize tableViewContents;
inside ViewDidLoad:
self.tableViewContents = [NSMutableArray arrayWithObjects:#"Cell 1",#"Cell 2",#"Cell 3",nil];
[self.tableView setDelegate:self]
[self.tableView setDatasource:self]
In the .m file:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.tableViewContents.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSUInteger row = [indexPath row];
index = row;
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
cell.textLabel.text = [tableViewContents objectAtIndex:row];
return cell;
}