Using a XIB file for custom Tableview Section Header - objective-c

I wanted to use a xib file to customise a tableview section in xcode (objective C), and here ar my files:
SectionHeaderView.xib is a UIView with a UILabel
SectionHeaderView.m
#import "SectionHeaderView.h"
#implementation SectionHeaderView
#synthesize sectionHeader;
#end
SectionHeaderView.h
#import <UIKit/UIKit.h>
#interface SectionHeaderView : UIView
{
IBOutlet UILabel *sectionHeader;
}
#property (nonatomic, strong) IBOutlet UILabel *sectionHeader;
#end
and in my MasterViewController.m
#import "SectionHeaderView.h"
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
SectionHeaderView *header = [[[NSBundle mainBundle] loadNibNamed:#"SectionHeaderView" owner:self options:nil] objectAtIndex:0];
return header;
}
It works ok till here, however as soon as I set XIB file owner's custom class to "SectionHeaderView" and connect the Label to "sectionHeader" I will get the error "NSUnknownKeyException". I wanted to connect these so I could change the label.text by the following code before returning the haeder:
header.sectionHeader.text = headerText;
I am using storyboard (xcode 4.5) for the MasterViewController.
Would appreciate any help

You can create a UITableViewCell subclass with an associated xib, and use it as the section header. In this example i will call it CustomTableViewHeaderCell.h/.m/.xib and show you how to change the text of a label inside this cell.
Create an outlet property in your CustomTableViewHeaderCell.h
#property (weak, nonatomic) IBOutlet UILabel *sectionHeaderLabel;
Add a UITableViewCell into the empty CustomTableViewHeaderCell.xib
and set the class of the element to CustomTableViewHeaderCell
from the Identity Inspector.
Set also the Identifier (attribute inspector of the cell) for example
CustomIdentifier.
Drag a label into the Content View and connect the outlet from the
CustomTableViewHeaderCell (Not the file owner!).
Then in each ViewController you want to use the table view section header cell:
1) Register your xib to reuse identifier (probably in viewDidLoad):
[_yourTableView registerNib:[UINib nibWithNibName:#"CustomTableViewHeader" bundle:nil] forCellReuseIdentifier:#"CustomIdentifier"];
2) Override viewForHeaderInSection to display your custom cell header view
-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
CustomTableViewHeaderCell * customHeaderCell = [tableView dequeueReusableCellWithIdentifier:#"CustomIdentifier"];
customHeaderCell.sectionHeaderLabel = #"What you want";
return customHeaderCell;
}

Try this: I have tested it in my app and its working:
NSArray *viewArray = [[NSBundle mainBundle] loadNibNamed:#"SectionHeaderview" owner:self options:nil];
UIView *view = [viewArray objectAtIndex:0];
UILabel *lblTitle = [view viewWithTag:101];
lblTitle.text = #"Text you want to set";
return view;

You can solve this issue by one of the following way:
1) you have derived SectionHeaderView from UIView. derive this class with UIViewController instead. This will resolve your issue.
2) Instead of using IBOutlet property, Set Tag of UILabel in view (say 101).
Discard SectionHeaderview class.
Keep SectionHeaderView.XIB, delete .m and .h files only.
use following code ins Viewforheader method of MasterViewController class:
{
UIViewController *vc=[[UIViewController alloc] initWithNibName:#"SectionHeaderview" bundle:nil]
UILable *lblTitle =[vc.view viewWithTag:101];
lblTitle.text =#"Text you want to set";
return vc.view;
}

Related

UITableViewCell - registerNib subclass not finding nib as subview

In the viewDidLoad in my UITableView I am calling:
[self registerNib:[UINib nibWithNibName:#"MyCell" bundle:nil] forCellReuseIdentifier:#"MyCellRI"];
Then in the cellForRowAtIndexPath I have the following:
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCellRI"];
[cell setup:[items objectAtIndex:indexPath.row]];
Inside the setup method I have a look on [self subviews] to go through the subviews and assign content based on key/value but in this loop I find UITableViewCellContentView's but I do not see the my custom view defined in the NIB.
In the NIB I have assigned Files Owner & The custom class of my cell. The cell is showing I just can't find the NIB view in the subviews.
Please advise.
James
Iterating through subviews is hard work) Try to avoid it. it is not very good practice because it is not reliable. For instance if you decided to change your custom cell UI you will have to change method where you're iterating through subviews.
Try to do following:
Let's say that there is custom cell with UIImage and 3 strings (e.g. person's name, age and cell phone number)
Implement 3 functions in your custom cell to set this values as follows:
MyCustomCell.h
... .h file standart code and then at the end ...
#propert(nonatomic, strong) IBOutlet UIImageView *myImage;
#propert(nonatomic, strong) IBOutlet UILabel *myFirstLabel;
#propert(nonatomic, strong) IBOutlet UILabel *mySecondLabel;
#propert(nonatomic, strong) IBOutlet UILabel *myThirdLabel;
- (void)setUIimage:(UIImage *)_image;
- (void)setFirstString:(NSString *)_string;
- (void)setSecondString:(NSString *)_string;
- (void)setThirdString:(NSString *)_string;
MyCustomCell.m:
#synthesize myImageView, myFirstLabel, mySecondLabel, myThirdLabel;
- (void)setUIimage:(UIImage *)_image {
self.myImageView.image = _image;
}
- (void)setFirstString:(NSString *)_string {
self.myFirstLabel.text = _string;
}
- (void)setSecondString:(NSString *)_string {
self.mySecondLabel.text = _string;
}
- (void)setThirdString:(NSString *)_string {
self.myThirdLabel.text = _string;
}
and finaly in your tableView controller cellForRowAtIndexPath
[myCustomCell setUiImage:someImage];
[myCustomCell setFirstString:oneString];
[myCustomCell setSecondString:twoString];
[myCustomCell setThirdString:threeString];
Hope this approach will help you. Feel free to ask if you have some questions

UITextField inside UITableViewCell won't become first responder

I use a UITableViewController to display the details of KoreanFood. The first cell is a custom UITableViewCell (OverviewCell) with an Image and two UITextFields, which I created and layout in Storyboard (AutoLayout).
I subclassed UITableviewCell like this:
// OverviewCell.h
#interface OverviewCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UITextField *englishTitleTF;
#property (strong, nonatomic) IBOutlet UITextField *koreanTitleTF;
#property (strong, nonatomic) IBOutlet UIImageView *myImageView;
#property (strong, nonatomic) UIImage *thumbnail;
My textfields in Storyboard are set to enabled/UserInteractionenabled and the delegate is my TVC. When I create the cells I also do this in code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == GENERAL_SECTION) {
static NSString *overviewCellID = #"overviewCell";
OverviewCell *overviewCell = [tableView dequeueReusableCellWithIdentifier:overviewCellID forIndexPath:indexPath];
overviewCell.englishTitleTF.text = self.generalInfo.titleEnglish;
overviewCell.koreanTitleTF.text = self.generalInfo.titleKorean;
overviewCell.englishTitleTF.enabled = YES;
overviewCell.koreanTitleTF.enabled = NO;
//BOOL test = [overviewCell.englishTitleTF becomeFirstResponder];
overviewCell.koreanTitleTF.delegate = self;
overviewCell.englishTitleTF.delegate = self;
UIImageView *imageView = [[UIImageView alloc] initWithImage:self.generalInfo.thumbnail];
overviewCell.myImageView = imageView;
overviewCell.myImageView.frame = CGRectMake(25, 25, 95, 95);
[overviewCell addSubview:overviewCell.myImageView];
return overviewCell;
}
The comment with the BOOL is NO, and I just don't know why... As I set the text and it's displayed correctly, I know the Outlets are set and the Cell isn't nil (I checked that).
Why does this not become first responder?
I also tried some suggestions inside the OverviewCell subclass like the hit test or implementing the setSelected: / setEditing: methods. But a becomeFirstResponder to the textField here doesn't change anything as well.
A view can't become first responder until it's been added to the responder chain, which happens automatically when the view gets added as a subview of a view that's in a window in the application object's windows list. In other words, your cell hasn't been added to the table view yet, so it's not connected to anything, and hence can't become first responder.
Try sending becomeFirstResponder from a method that gets called after the table view has finished loading its cells. And of course, don't do this:
overviewCell.koreanTitleTF.enabled = NO;

UILabel text not changing, however xx.title is working

I have two view controller. In first view controller I have list of names. When I click on that, I want the same name to be displayed in second view controller.
I have below code.
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// PropDetailViewController is second view controller
PropDetailViewController *prop = [self.storyboard instantiateViewControllerWithIdentifier:#"prop"];
ListOfProperty *propList = [propListFinal objectAtIndex:indexPath.row];
NSString *myText = [NSString stringWithFormat:#"%#", propList.addressOfFlat];
prop.detailLabel.text = myText;
prop.title = myText;
[self.navigationController pushViewController:prop animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
and in PropDetailViewController, I have #property (retain, nonatomic) IBOutlet UILabel *detailLabel;.
What I was expecting is when I click say Name 1, I will see Name 1 as text in UILabel and on the UINavigationBar too. However I only see Name 1 on navigation bar and not on UILabel.
It is not advisable to access an UIView item at that point in the program flow. When setting the value of prop.detailLabel.text the view may not have been loaded. When the view is loaded later then the UIView is updated with the default settings given in the XIB file in IB.
You should rather set an NSString property, lets say
#property (retain, nonatomic) NSString *propName;
assign it before pushing the view controller as you do. But use this property and not the UILable. And in PropDetailViewController in viewDidLoad do the following:
(void) viewDidLoad {
// call super viewDidLoad and all the works ...
self.detailLabel.text = propName;
}
Instead of viewDidLoad you could use viewWillAppear. Because viewDidLoad COULD be executed already when you assign the property's value.
If you want to be on the save side then invent a new init method where you hand over all the values that you want to be set upfront.
But I never did that in combination with storyboard (where you may use instantiate... rather than init...) and therefore I cannot give any advise out of the top of my head.
Another clean approach would be to stick with the propName property but to implement a custom setter -(void)setPropName:(NSString)propName; where you set the property (probably _propName if you autosynthesize) AND set the UILable text plus setting the UILable text within viewDidLoad.
Try this:
in .h
#property (retain, nonatomic) NSString *detailText;
in PropDetailViewController.m
Change line of code with
NSString *myText = [NSString stringWithFormat:#"%#", propList.addressOfFlat];
prop.detailText = myText;
prop.title = myText;
in ViewDidLoad:
[self.detailLabel setText:self.detailText];

loadNibNamed - Could not load NIB in bundle: NSBundle

I'm trying to create a TableViewCell, using a XIB file, but I get this error at execution time:
2013-01-10 17:54:50.297 MainApp[6778:b603] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle (loaded)' with name 'DropDownCell''
* Call stack at first throw:
This is what I'm trying to do.
I have a project, with a first Menu. In this project/workspace, I have the class for my first Menu. Inside this project, I have another workspace, with the classes for my second Menu. I mean, this workspace is for the SubViewController's and classes for the options selected in my first Menu. In this workspace, I'm trying to create a DropDownMenu,using the demo from apple, but my app crash. This demo creates the cell's in the table, using a XIB file.
This is the DropDownCell class:
DropDownCell.h
#import <UIKit/UIKit.h>
#interface DropDownCell : UITableViewCell{
IBOutlet UILabel *textLabel;
IBOutlet UIImageView *arrow_up;
IBOutlet UIImageView *arrow_down;
BOOL isOpen;
}
-(void)setOpen;
-(void)setClosed;
#property (nonatomic)BOOL isOpen;
#property (nonatomic,retain) IBOutlet UILabel *textLabel;
#property (nonatomic,retain) IBOutlet UIImageView *arrow_up;
#property (nonatomic,retain) IBOutlet UIImageView *arrow_down;
#end
DropDownCell.m
#import "DropDownCell.h"
#implementation DropDownCell
#synthesize textLabel, arrow_up, arrow_down, isOpen;
-(void)setOpen{
[arrow_down setHidden:YES];
[arrow_up setHidden:NO];
[self setIsOpen:YES];
}
-(void)setClosed{
[arrow_down setHidden:NO];
[arrow_up setHidden:YES];
[self setIsOpen:NO];
}
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if(self){
}
return self;
}
-(void)setSelected:(BOOL)selected animated:(BOOL)animated{
[super setSelected:selected animated:animated];
}
-(void)dealloc{
[super dealloc];
}
#end
And the DropDownCell.xib has one UITableViewCell with a UILabel.
I have another UITableViewController, which uses the DropDownCell XIB. This is the method:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"MenuCell";
static NSString *DropDownCellIdentifier = #"DropDownCell";
if([indexPath row] == 0){
DropDownCell *cell = (DropDownCell*)[tableView dequeueReusableCellWithIdentifier:DropDownCellIdentifier];
if(cell == nil){
NSArray *topLevelObjects = [[NSBundle mainBundle]loadNibNamed:#"DropDownCell" owner:self options:nil];
for(id currentObject in topLevelObjects){
if([currentObject isKindOfClass:[DropDownCell class]]){
cell = (DropDownCell*)currentObject;
break;
}
}
}
[[cell textLabel] setText:#"Option 1"];
return cell;
}
But my app crashes when loads the NIB file... What can be the reason? I'm using Xcode 4.3, and I'm not using storyboards.
#user1600801, There may be one of these reasons:-
1) Disable/Uncheck "Use Autolayout" in File Inspector for that Custom cell.
2) Your Target is not set for that Custom Cell.
To set it select your custom cell .Xib file,
Select "File Inspector",
Under "Target membership" check if your project name is selected? If not, Then check/enable it.
3) Check your Custom Cell class name and Cell identifiers.
Do you have Auto Layout enabled for that NIB, and are compiling for
The problem is that you are not even loading the bundle. See this question:
NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle
It seems like you can sort it out by just removing files from your project and put again there.
Try to remove the references of the nib file and clean the project.
Then add the nib file back to the project and build.
This resolves the same exception happened in my project.

Can't reload Table View in tab bar controller

Hi I have a tab tab controller and my first tab includes a view with:
3 text fields
a submit button
a tableView
Once I fill in the text fields I click submit and it adds the information to my managedObjectContext which is an sqlite database (CoreData).
As soon as I click submit I want the tableView to reload to include the added object. Currently my tableView will display the data in the database but it will only add the new row when I stop and re-run the simulator
This is the code for when the add button is tapped, it is here that I can't get the reload tableView working because it says tableView is an undeclared identifier, what have i missed?
-(IBAction)addButtonTapped:(id)sender {
NSLog (#"Add Button Tapped");
NSLog(#"Adding %# units of item code %# at $%# each",quantityTextField.text,productTextField.text,priceTextField.text);
Products_MarketAppDelegate* delegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext* managedObjectContext = delegate.managedObjectContext;
NSManagedObject* newProduct;
newProduct = [NSEntityDescription insertNewObjectForEntityForName:#"Product" inManagedObjectContext:managedObjectContext];
[newProduct setValue:productTextField.text forKey:#"itemCode"];
[newProduct setValue:quantityTextField.text forKey:#"quantity"];
[newProduct setValue:priceTextField.text forKey:#"price"];
if ([managedObjectContext hasChanges])
NSLog(#"Managed Object Changed");
NSError* error;
[managedObjectContext save:&error];
// Insert Reload Table Code Here
// ** I have tried the following and it gives an error "Use of undeclared identifier 'tableView'"
//[tableView reloadData];
//[self.tableView reloadData];
}
As you can see below I have added the UITableViewDelegate & UITableViewDataSource in the header file. I have also hooked up the tableview in IB so that the delegate and datasource connections are linked to file's owner.
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface FirstViewController : UIViewController
<UIApplicationDelegate, UITableViewDataSource,UITableViewDelegate,NSFetchedResultsControllerDelegate>
{
IBOutlet UITextField *productTextField;
IBOutlet UITextField *quantityTextField;
IBOutlet UITextField *priceTextField;
NSMutableArray *items;
NSFetchedResultsController *fetchedResultsController;
}
#property (nonatomic, retain) NSMutableArray *items;
#property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
-(IBAction)addButtonTapped:(id)sender;
#end
This is the code to fill the tableView which works correctly
#pragma mark TableView
-(NSInteger)numberOfSectionsInTableView: (UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell
Product* productItem =[fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"%# x %# # $%#",productItem.quantity,productItem.itemCode,productItem.price];
return cell;
}
I have searched for answers on this site and on others but I must be doing something different and the solutions aren't helping me
Your UIViewController does not currently have an instance variable pointing to your tableview. Set one up:
#property (nonatomic, retain) IBOutlet UITableView *myTableView;
Remember to synthesize this in your .m
#synthesize myTableView;
Then in your code you can call
[self.myTableView reloadData];
You might have got confused by looking at code examples that use a UITableViewController instead of a UIViewController. The UITableViewController already has an instance variable called tableView, so your subclass wouldn't need it's own tableView instance variable declared. But you're using a UIViewController, so you must declare a tableView instance variable.
Thanks #MattyG for all your help. At first I wasn't sure if I was going against the norm and thats why it wasn't working.
I ended up solving the problem due to your suggestions & it works perfectly! I used the debugger and found that that although we had created a property for the table I had not created an IBOutlet and linked it in my nib file with:
IBOutlet UITableView *myTableView;
I guess this meant that I was telling myTableView to reload but it wasn't hooked up to my table and thus couldn't use the datasource methods.