iPhone: How to use UITableViewCell in two table views - iphone

I have 2 classes that inherit UITableViewControllers. And this two table views should use the same custom UITableViewCell. So how can I use a Custom UITableViewCell from a Nib File in two different classes ? The owner of the xib can be only 1 class.
#interface Class1 : UITableViewController<UITableViewDataSource,
UITableViewDelegate> {
UITableViewCell *myCustomTableRow;
}
#property (nonatomic, retain) IBOutlet UITableViewCell *myCustomTableRow;
#interface Class2 : UITableViewController<UITableViewDataSource,
UITableViewDelegate> {
UITableViewCell *myCustomTableRow;
}
#property (nonatomic, retain) IBOutlet UITableViewCell *myCustomTableRow;

Create a UITableViewController sub-class say CommonTableView. Make this as the owner of nib file. Then inherit the two classes you want to implement from CommonTableView Class. This will work fine.

Based on my opinion , You should have to create Custom cell and and for other table you should have to create only XIB for that and use .h and .m file same...
just change controller in XIB...
Hope you understand..
See this How to create Customcell and this

Use UINib class. It allows for instantiation without parent, and also improves the performance by caching nib file contents.
#interface Class1 : UITableViewController<UITableViewDataSource, UITableViewDelegate> {
UINib *myCustomCellNib;
}
... and the similar thing for Class2. Then, in viewDidLoad
myCustomCellNib = [UINib nibWithNibName:#"myCustomCellNibName" bundle:nil];
then, when creating the cell:
NSArray* objects = [myCustomCellNib instantiateWithOwner:nil options:nil];
// given that your cell is defined as a first object in your nib
cell = [objects objectAtIndex:0];

Related

Common views in viewControllers - Code re-usability

I have few common views in most of my viewControllers. What I noticed is that I can reuse single code for all viewControllers which is absolutely wise. For this I decided to create a class Utils which has static methods like
+(void)createCommonViews:(float)yAxis:(NSString*)text;
In my case common views are three labels and two images.
Problem : I am not able to add these views from Utils. I am wondering how can I send self as a parameter so that I may add the views from Utils. It may be wrong to add views outside the viewController. In that case what can be the solution? Taking all these views in a UIView, setting return type of Utils method as UIView and then adding UIView to viewController (after calling method from viewController) might solve my problem. But what I am looking for is some other solution.
+(void) createCommonViews:(float)yAxis withText:(NSString*) text toTarget:(UIViewController*) target
{
//create views
[target addSuview:view];
}
But I think returning a Uiview and then adding it in the UIViewController afterwards, is a far better solution.
The method you're attempting is to have your view object as a singleton. This is uncommon at best, at worst a crash waiting to happen. Better design is for each of your view controller classes to have its own instance of your custom view, like so:
#interface MyCommonView : UIView
// ...
#end
#interface MyViewController_A : UIViewController {
MyCommonView *commonView;
}
#property (strong, nonatomic) IBOutlet MyCommonView *commonView;
#end
// Meanwhile somewhere else...
#interface MyViewController_B : UIViewController {
MyCommonView *commonView;
}
#property (strong, nonatomic) IBOutlet MyCommonView *commonView;
#end
Create a viewController that acts as a parent view for all your common stuff, call it CommonViewController then implement this in all the viewcontrollers you want it to appear
-(void) viewDidLoad
{
[self.view addSubView:[[CommonViewController alloc] initWithRect:..];
}
Or alternatively using xib files

Objective-C: Problems accessing objects in other UIViewControllers

So I have a Custom UITableViewCell that holds a reference to its containing view controller (the VC that has its table in it).
// MyCell.h
#import <UIKit/UIKit.h>
#import "RootViewController.h"
#interface MyCell : UITableViewCell
#property (nonatomic, strong) IBOutlet RootViewController *rootViewController;
-(IBAction)checkBoxClicked:(UIButton*)sender;
// MyCell.m
#implementation MyCell
#synthesize rootViewController = _rootViewController;
-(IBAction)checkBoxClicked:(UIButton*)sender
{
[self setCheckBoxChecked:!_checkBoxChecked];
[_rootViewController refreshVisibleViewForCellTagged:self.tag];
}
In my cell I have a button that changes a variable and then calls a function in my rootViewController. The method is actually called however when I try to access any object in the RootViewController inside of the refreshVisibleViewForCellTagged method they are are '0x0' / nil;
// RootViewController.h
#interface RootViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) IBOutlet UITableView *myTableView;
// RootViewController.m
- (void) refreshVisibleViewForCellTagged:(NSInteger)cellTag
{
UITableView *tableView = self.myTableView; // nil
NSIndexPath *indexPath = [self.myTableView indexPathForSelectedRow]; // nil
MyCell *selectedCell = (MyCell*)[self.myTableView cellForRowAtIndexPath:indexPath]; // nil
if (selectedCell.tag == cellTag) {
NSLog(#"Refresh one way.");
} else {
NSLog(#"Do something else.");
}
}
Can anyone shed some light as to why I cant access any objects/variables in the RootController from within the method 'refreshVisibleViewForCellTagged'?
Please and thank you!
** My big question is Why can't I access any objects when calling a method in a view controller From a different view controller. There is some great programming truth that I am not aware of here, is it a permissions issue? Im not using #class (forward classing) in this instance.
As #trojanfoe said, delegation is a better way to do it.
Instead of #import "RootViewController.h", it is better to adop delegation. Because UITableViewCell is a child and RootViewController is the parent view. You don't want the child to talk directly with the parent.
To adopt delegation:
in MyCell.h file
remove #import "RootViewController.h".
revise MyCell.h as follows:
#protocol MyCellDelegate; // if you need to have forward declaration
#interface MyCell : UITableViewCell
// #property (nonatomic, strong) IBOutlet RootViewController *rootViewController;
#property (nonatomic) id <MyCellDelegate> delegate;
#end
#protocol MyCellDelegate <NSObject>
- (void)refreshVisibleViewForCellTagged:(NSInteger)cellTag;
#end
in MyCell.m.
#synthesize delegate;
-(IBAction)checkBoxClicked:(UIButton*)sender {
[self setCheckBoxChecked:!_checkBoxChecked];
//[_rootViewController refreshVisibleViewForCellTagged:self.tag];
[self.delegate refreshVisibleViewForCellTagged:self.tag];
}
in RootViewController.h adopt the delegation of MyCell
#import "MyCell.h"
#interface RootViewController : UIViewController <MyCellDelegate>
in RootViewController.m.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = // your implementation
//assuming all your cells are of MyCell kind
// set RootViewController as the delegate of each cell
((MyCell *)cell).delegate = self;
return cell;
}
implement the delegate method in RootViewController.m.
- (void)refreshVisibleViewForCellTagged:(NSInteger)cellTag {
// whatever you have
}
P.S. The above codes are for illustration. I didn't run them. If some part doesn't work, let me know, and I'll revise it.
The reason those objects in RootViewController are nil in the way you call, is because you are not accessing the same instance of RootViewController. It is a different (new) instance and hence all objects are nil.
Ignore the fact that view controllers are even involved. What you have are OBJECTS, connected together in a certain pattern. Accessing data in another view controller is no different from accessing data in any other object. There's no "magic" with view controllers, other than they have a few standardized connections to other objects.
IMHO, this is a poor design. For starters, your cell shouldn't need a reference to the view controller that the table it's in is in (read that twice, it barely makes sense just because the very idea of it is confusing). You have a strong reference to this view controller. So what happens when the OS tries to deallocate your view controller? It will never be able to, because the table view cell as a strong reference to it, keeping its retain count at 1. The same situation holds true for the cell. You risk running into a retain cycle here. Generally, child views should have weak references to their parents.
But this isn't even really a true parents/child relationship. I would suggest instead an approach like this, which all occurs in your view controller that contains the table view:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Assuming you set a reuse identifier "cellId" in the nib for your table view cell...
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:#"cellId"];
if (!cell) {
// If you didn't get a valid cell reference back, unload a cell from the nib
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"MyCell" owner:nil options:nil];
for (id obj in nibArray) {
if ([obj isMemberOfClass:[MyCell class]]) {
// Assign cell to obj, and add a target action for the checkmark
cell = (MyCell *)obj;
[cell.checkMarkButton addTarget:self action:#selector(checkPressed:) forControlEvents:whateverEventYouWant];
break;
}
}
}
// Set the tag of the cell here, since we may get a different cell back from the reuse queue
cell.checkMarkButton.tag = indexPath.row;
return cell;
}
Now set up the method for the clicking of the checkmark button
- (void)checkPressed:(id)sender {
UIButton *checkmark = (UIButton *)sender;
// This will give you the row of the checked button
int checkedCellRow = checkmark.tag;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:checkedCellRow inSection:0];
// Now you can grab a reference to that cell if you need to
MyCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
}
This way, you keep all of the controller-related stuff in your controller class (i.e. how to handle the checkmark button being pressed), and you don't need to deal with this whackiness of referencing the view controller of your cell's table.
EDIT: I guess I should also help answer your questions...First of all, if you're saying that in your refreshVisibleViewForCell method, you're getting a nil value for self.myTableView, are you sure it is hooked up properly in IB? Even if it's hooked up, click the little x to unhook it and hook it up again to be sure. Also make sure you've #synthesized your myTableView property. Without seeing more code, an IB issue is my best guess as to why you're getting a nil value for tableView. A nil value here will result in a nil indexPath and selectedCell, also. As for your big question, you can access properties of objects within your view controller. Those properties can, of course, be objects. So in your example, if you have a tag property on selectedCell, you can access it from anywhere that you have a valid reference to selectedCell. If selectedCell is nil, the property will be nil. #class is better suited for header files. For instance, if you wanted to make your custom cell a property of your view controller, you might say:
#import <UIKit/UIKit.h>
#class MyCell;
#interface RootViewController : UIViewController
#property (nonatomic, strong) MyCell *cell;
#end
Then, in your implementation file, you would actually import MyCell.h. Giving the #class forward declaration just keeps you from having to import all of the details about the MyCell class in your header file. The header doesn't need to know about all of the properties and methods of MyCell, just that you intend on using it in the implementation file. So you #class in the header, #import in the implementation.
in RootViewController.h:
#interface RootViewController : UITableViewController <UITableViewDelegate>
in RootViewController.m:
- (void) refreshVisibleViewForCellTagged:(NSInteger)cellTag {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
MyCell *selectedCell = (MyCell*)[self.myTableView cellForRowAtIndexPath:indexPath]; // nil
etc...
I'm not seeing declarations of myTableView in your RootViewController. But if your RootViewController implements UITableViewController, you can use self.tableView to access the tableview. You don't need to keep a reference to it by yourself.
#RachelD, if your RootView is more complicated than just a UITableViewController consider using a separate class, such as RootTableViewController. Then in your RootView xib, create IBOutlet for RootTableViewController to reference it. Like this:
// RootTableViewController definition
#interface RootTableViewController : UITableViewController
{
}
// RootViewController definition
#interface RootViewController : UIViewController
{
RootTableViewController *table_c;
}
#property (nonatomic, retain) IBOutlet RootTableViewController *table_c;
Note that you need to drag an "Object" into the "Objects" section (for RootViewController) in the interface builder, and type RootTableViewController in the Custom Class section for this object. Right click this object, make sure its IBOutlet, view, 2 delegates are correctly set.
The reason why your myTableView is nil is because it's not properly initialized. I mean, if you don't use UITableViewController you are responsible for assigning it manually via interface builder or something.

Can't pass UITableViewCell didSelectRowAtIndexPath data to another view controller

I have currently got a UITableView in my main view controller. I want the data of the selected row to be passed to another view controller in my project.
Here's what I have got so far, although there is an error. Why is this? I have referenced the class and the header file in my .h file.
I would really appreciate some help with this as I've tried everything I can think of.
Simply synthesize the object in the destination view.
It will work fine...
like:
in .h
#property(nonatomic, retain) UILabel *note;
in .m
#synthesize note;
Very Simple,I think note is a UILabel,
according to You error
please use this code in AddNote. h file
NSString *noteStr; //Ios 4
IBoutlet UILabel *note;
#property(nonatomic, retain) NSString *noteStr; //Ios 4
#property(nonatomic, retain) UILabel *note;
#property(nonatomic, strong) NSString *noteStr;
#property(nonatomic, strong) UILabel *note; //Ios 5
In AddNote. .m file
#synthesize note,noteStr;
self.note.text=noteStr; //in ViewWillAppear
Some Time Application is crash Because Memory IF you Work In Ios 4 Please Correct this code
an.noteStr - [selectNote retain];
Declare static variable for global use.
Using keyword extern
Create Common.h
extern NSMutableArray *gMyBasketCollectionList;
common.m
NSMutableArray *gMyBasketCollectionList;
Use This Mutable Aarray to Store Your Data and Display in Any other
include common.h in First View And SecondView
Add Object to mutable Array in First View And
Show SecondView And Display access that Array

Assigning the outlet for a UITableViewCell using UINib

I looked at the TableViewUpdates/TVAnimationGestures from Apple's WWDC 2010 code and am having trouble duplicating a UITableViewCell subclass. This is what I've done:
I created a new UITableViewCell subclass with some simple properties:
#interface TargetDetailTableViewCell : UITableViewCell
#property (nonatomic, retain) IBOutlet UILabel *DescriptionLabel;
#property (nonatomic, retain) IBOutlet UILabel *ValueLabel;
#property (nonatomic, retain) IBOutlet UIImageView *DotImageView;
In the .m, I just release memory. In IB, I change my class to TargetDetailTableViewCell for the UITableViewCell I just dragged into IB. I connect the outlets from the TargetDetailTableViewCell to the appropriate labels and image view.
In the class I want to use this:
#class TargetDetailTableViewCell;
//some properties
#property (nonatomic, assign) IBOutlet TargetDetailTableViewCell *TargetCell;
In the .m:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *TargetCellIdentifier = #"TargetDetailTableViewCellIdentifier";
TargetDetailTableViewCell *cell = (TargetDetailTableViewCell *)[tableView dequeueReusableCellWithIdentifier:TargetCellIdentifier];
if (cell == nil) {
UINib *nib = [UINib nibWithNibName:#"TargetDetailTableViewCell" bundle:nil];
[nib instantiateWithOwner:self options:nil];
cell = self.TargetCell;
self.TargetCell = nil;
}
// set some labels
return cell;
}
When I run it, I get the error: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
The only think I can see that is different between Apple's example and mine is that when I ctlr-click on the subclass of UITableViewCell in their IB, they have a File's Owner property set. I have no idea how they connected that outlet as it is declared as a property in the class they use the cell, but there is no physical IB connection they make. Can someone explain that to me or what I am doing wrong?
Also, if anyone can explain this, that would be great:
UINib *nib = [UINib nibWithNibName:#"TargetDetailTableViewCell" bundle:nil];
[nib instantiateWithOwner:self options:nil];
cell = self.TargetCell;
self.TargetCell = nil;
It seems like you create the nib and the owner of the nib that gets instantiated from memory is the class you are in or self (my viewcontroller). Then the last two lines confuse me. It's like you tell your cell to point to the newly created object, then you set the newly created object to nil. Which in my head I think, the cell now points to nil as well. Thanks.
You need to have an owner in your custom table view cell nib and that owner needs to be your TableViewDataSource class (i.e. the table view controller which implements the cellForRowAtIndexPath method and has the TargetCell outlet to the table cell).
You also need to connect this TargetCell outlet from the file owner (the TableViewController) to your custom table view.
The reason for this is that when you load the nib, with your table view controller as the owner, it will then set the outlet that you have (the TargetCell property) to point to the table view cell defined in your nib.
You then copy this reference to the cell method variable, configure it and return it. You assign the property to nil after copying it because you only needed it as a bootstrap to get a reference to the object in the nib for use in the cellForRowAtIndexPath method.

Class is not key value coding-compliant [duplicate]

This question already has answers here:
Xcode - How to fix 'NSUnknownKeyException', reason: … this class is not key value coding-compliant for the key X" error?
(79 answers)
Closed 7 years ago.
I know the meaning of this error, but I'm really struggling with it, and I need someone's help :
2010-09-21 15:03:11.562 Stocks[5605:207] *** Terminating app due to uncaught
exception 'NSUnknownKeyException', reason: '[<NSObject 0x499fb20>
setValue:forUndefinedKey:]: this class is not key value coding-compliant
for the key actionText.'
There is my code here :
AlertCell.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface AlertCell : UITableViewCell {
IBOutlet UILabel *actionText;
}
#property (retain, nonatomic) UILabel *actionText;
#end
And
AlertCell.m
#implementation AlertCell
#synthesize actionText;
- (void)dealloc {
[actionText release];
[super dealloc];
}
#end
The problem happens just there :
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
AlertCell *cell =
(AlertCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"AlertCell"
owner:nil
options:nil];
for (id oneObject in nib) {
if ([oneObject isKindOfClass:[UITableViewCell class]]) {
cell = (AlertCell *)oneObject;
break;
}
}
}
cell.actionText.text = [arrayAlert objectAtIndex:indexPath.row];
return cell;
}
On this line :
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"AlertCell"
owner:nil
options:nil];
As asked, here is my header for the TableViewCOntroller :
#import <UIKit/UIKit.h>
#interface AlertesViewController : UITableViewController {
NSMutableArray *arrayAlert;
}
And you can see my XIB file (as XML): http://pastebin.com/FDVzLYZu
#end
Can anyone help me ? Thanks a lot !
you probably based your code on a web tutorial such as the one at http://www.e-string.com/content/custom-uitableviewcells-interface-builder or http://www.icodeblog.com/2009/05/24/custom-uitableviewcell-using-interface-builder/
Your problem (and I'm 99% sure this is where you tripped up, I just made the same mistake) is that in interface builder you linked your IBOutlets from File's Owner when you should link them from the cell view. This is why you are getting the errors.
There are a couple of options to resolve this - i'll let you decide which is the most appropriate.
The reason it's failing is because the owner is being passed as nil. You're binding the actionText outlet to the file's owner in IB, but then when loading the nib, the owner is nil. I'd guess that when loading with a nil owner behind the scenes an NSObject is used, which is why you're getting the key/value error.
My previous advice to pass the cell as the owner would also fail as I didn't know how the Nib is constructed; the cell is nil as you've yet to create it (and dequeue is passing nil back, so even pass cell as the owner is still essentially passing nil).
Two options:
Instantiate a new cell in your -cellForRowAtIndexPath:(NSIndexPath *)indexPath implementation, and pass that new cell as the owner (but i'd guess that this isn't the best solution for you)
Or, and I'd suggest this is the better solution, change the binding of actionText in your nib file to the Alert Cell and not the file's owner (You have File's Owner and an Alert Cell - bind the UILabel to the actionText outlet of the Alert Cell, and not the File's owner, which is what's being done at present) - I suspect this is what you want. With that in mind file's owner can become an NSObject again.
------- Original answer kept below as the file's owner class is also a common cause for this error -------
It suggests that you've 'instantiated' an AlertCell in InterfaceBuilder, and you're binding something to actiontext, but the class isn't set to AlertCell, it's still NSObject?
Take a look at the class text box on the identify tab of the tool palette for that object in Interface Builder. The class should be AlertCell, but i'd guess it's still set to NSObject.
As an aside, and feel free to ignore this advice, but there are a couple of extra things i'd encourage you to do, purely from an Objective C expectations/conventions point of view:
Name your files after your class (upper case the first character of the filename).
Prefix your class names; two uppercase characters, typically your initials (i'd name it DWAlertCell, for example).
I had the same problem, and I could not find the mentioned variable/element in any file. I followed the following steps.
Environment: Xcode 6
Go to the Xib file, and right click (Ctrl+Mouse Click) on each UI
element to see its associations.
In its referencing outlets section, you will be able to locate the
erroneous association.
Remove that association
Clean the solution and build folder
Reset Simulator
Rebuild and run the project.
check UIControl Referencing OutLet Property Name from nib/StoryBoard File
Make sure select cell to put custom class not File's Owner to add custom class
on right side top
and if You have done so, Before you make any changes remove outlet connection and then remove custom class from File's Owner and add Class to Cell view
Maybe it helps:
Indentity Inspector when you have selected file's owner - UITableViewCell,
when you have selected the cell inside Objects Your custom cell nane
After 2 hours on this, I realized that i forgot to
#synthesize tableView = _tableView;
Hope it helps.
Mário
#import <UIKit/UIKit.h>
#interface ReceptionDetailCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *productName;
#property (weak, nonatomic) IBOutlet UILabel *billType;
#property (weak, nonatomic) IBOutlet UILabel *bookTime;
#property (weak, nonatomic) IBOutlet UILabel *doctorName;
#property (weak, nonatomic) IBOutlet UILabel *downBillManName;
#end
Checkout out the Cell code and storyboard setting, whether they are all exist, maybe you have changed their names.