Editable UITableView with a textfield on each cell - objective-c

I am new to the iOS world and I want to know how to make a UITableView with custom cells that look and behave like the one you have when you try to configure some WiFi connexion on your device. (You know the UITableView with cells containing UITextFields with blue font where you set up the ip address and all that stuff... ).

To make a custom cell layout do involve a bit of coding, so I hope that dosen't frighten you.
First thing is creating a new UITableViewCell subclass. Let's call it InLineEditTableViewCell. Your interface InLineEditTableViewCell.h could look something like this:
#import <UIKit/UIKit.h>
#interface InLineEditTableViewCell : UITableViewCell
#property (nonatomic, retain) UILabel *titleLabel;
#property (nonatomic, retain) UITextField *propertyTextField;
#end
And your InLineEditTableViewCell.m could look like this:
#import "InLineEditTableViewCell.h"
#implementation InLineEditTableViewCell
#synthesize titleLabel=_titleLabel;
#synthesize propertyTextField=_propertyTextField;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Here you layout your self.titleLabel and self.propertyTextField as you want them, like they are in the WiFi settings.
}
return self;
}
- (void)dealloc
{
[_titleLabel release], _titleLabel = nil;
[_propertyTextField release], _propertyTextField = nil;
[super dealloc];
}
#end
Next thing is you set-up your UITableView as you normally would in your view controller. When doing this you have to implement the UITablesViewDataSource protocol method - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. Before inserting your implementation for this, remember to #import "InLineEditTableViewCell" in your view controller. After doing this the implementation is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
InLineEditTableViewCell *cell = (InLineEditTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"your-static-cell-identifier"];
if (!cell) {
cell = [[[InLineEditTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"your-static-cell-identifier"] autorelease];
}
// Setup your custom cell as your wish
cell.titleLabel.text = #"Your title text";
}
That's it! You now have custom cells in your UITableView.
Good luck!

Related

Not able to obtain correct indexPath.row value from objective C file in Swift file

File: ContactsViewController.m
In this file I am using the didSelectRowAtIndexPath method to push a new View Controller to show information about the name that was pressed on the Table View Controller. The View Controller that will be displaying the information about the name is being implemented in Swift. The part that I am referring to in this code is in the didSelectRowAtIndexPath method, line:
_myIndex = indexPath.row;
I believe indexPath.row should return the index of the name that was tapped in the Table View Controller.
#import "ContactsViewController.h"
#import "Contacts-Swift.h"
#interface ContactsViewController ()
#property (nonatomic, readwrite, strong) NSMutableArray* contacts;
#property (nonatomic, readwrite) NSInteger myIndex;
#end
#implementation ContactsViewController
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
/*NSArray *contactArray = #[#"Johnny Appleseed", #"Paul Bunyan", #"Calamity Jane"];
_contacts = [NSMutableArray arrayWithArray:contactArray];*/
/*Contact *c1 = [[Contact alloc] initWithName: #"Johnny"];
Contact *c2 = [[Contact alloc] initWithName: #"Paul Bunyan"];
Contact *c3 = [[Contact alloc] initWithName: #"Calamity Jane"];*/
// _contacts = [NSMutableArray arrayWithArray: #[c1, c2, c3]];
self.contacts = [NSMutableArray array];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.tableView registerClass: [UITableViewCell class]
forCellReuseIdentifier:#"UITableViewCell"];
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return self.contacts.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell" forIndexPath:indexPath];
Contact *contact = self.contacts[indexPath.row];
cell.textLabel.text = contact.name;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ContactsViewController *viewController = [self.navigationController.storyboard instantiateViewControllerWithIdentifier:#"the"];
[self.navigationController pushViewController:viewController animated:YES];
_myIndex = indexPath.row;
}
File: ContactsViewController.h
This is the header file that I am using in order to have access to the objective C methods and variables when working in the swift file. (I am not very familiar with objective C so there is a strong possibility that this implementation is what is causing my problems).
#import <UIKit/UIKit.h>
#interface ContactsViewController : UITableViewController <UITableViewDelegate>
#property (nonatomic, readonly) NSInteger myIndex;
#property (nonatomic, readonly, strong) NSMutableArray* contacts;
#end
File: ExistingContactViewController.swift
In the ExistingContactViewController I am just trying to set the firstName label equal to the text that is present in the contacts array at indexPath.row (in the ContactsViewController.m file).
import UIKit
#objc class ExistingContactViewController: UIViewController {
var contactsObject = ContactsViewController()
#IBOutlet weak var firstName: UILabel!
#IBOutlet weak var lastName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
print("\(contactsObject.myIndex)")
firstName.text = contactsObject.contacts[contactsObject.myIndex] as? String
}
When clicking names that are added to the Table View Controller the only index that is ever printed
print("\(contactsObject.myIndex)")
is 0. Which tells me that I am not capturing the index of the name that is tapped.
Image of myStoryboard The bottom most scene is the one that I am trying to change the First Name label to display the name of the cell that was tapped.
I have not yet been able change the title of this label when clicking on a cell. I have been able to implement this functionality when using just swift files (through watching numerous videos). I am sure that there is a key concept I am missing in the objective C files so any suggestions and/or pointers are much appreciated. If any additional details are needed let me know!
Thanks.
Seems that in your didSelectRowAtIndexPath: you are pushing a ContactsViewController to the navigation stack and if I understand correctly it should be instance of ExistingContactViewController. Also you should set the contactsObject property of the ExistingContactViewController before pushing it in the navigation stack (before viewDidLoad is executed) otherwise it's value will always be a new ContactsViewController which, probably, is causing the issue.
I hope this helps!

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:

Datasource not returned by sub class of parent class implementing UITableView, throwing exception

I want to show some media of different categories (e.g. mostViewed, starred) in a UITableView. I created APPParentViewController which implements UITableViewDataSource and UITableViewDelegate protocol. In cellForRowAtIndexPath method in APPParentViewController, I return the appropriate cell which is filled with data coming from an array.
The array is actually instantiated in the init method in a sub class of APPParentViewController, which I exemplarily called APPChildViewController. There is one APPChildViewController for each media category. They just differ in the way the array is instantiated, the content of the array so to say.
I instantiate all APPChildViewController classes in another UIViewController ([[APPChildViewController alloc] init]) and then initially select one APPChildViewController to view (all happens in viewDidLoad method of that UIViewController). Working so far.
But when I want to show another APPChildViewController simply by removing the old view and adding the requested view (when the user requested it by pressing a button), I am getting the following exception:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:' ***
This is the code I am using (I removed everything which is not important in my opinion, so I hope it's still comprehensible):
APPParentViewController.h
#import <UIKit/UIKit.h>
#interface APPParentViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) NSArray *media;
#property (strong, nonatomic) UITableView *tableView;
#end
APPParentViewController.m
#import "APPParentViewController.h"
#import "APPCell.h"
#interface APPParentViewController ()
#end
#implementation APPParentViewController
#synthesize media;
#synthesize tableView;
-(void)viewDidLoad
{
self.tableView = [[UITableView alloc] init];
[self.view addSubview:self.tableView];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.media count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellTableIdentifier = #"CellTableIdentifier";
static BOOL nibsRegistered = NO;
if (!nibsRegistered) {
UINib *nib = [UINib nibWithNibName:#"APPCell" bundle:nil];
[self.tableView registerNib:nib forCellReuseIdentifier:CellTableIdentifier];
nibsRegistered = YES;
}
APPCell *cell = [self.tableView dequeueReusableCellWithIdentifier: CellTableIdentifier];
NSUInteger row = [indexPath row];
NSDictionary *rowData = [self.media objectAtIndex:row];
cell.title = [rowData objectForKey:#"Title"];
return cell;
}
#end
APPChildViewController.h
#import "APPParentViewController.h"
#interface APPChildViewController : APPParentViewController
#end
APPChildViewController.m
#import "APPChildViewController.h"
#import "APPCell.h"
#interface APPChildViewController ()
#end
#implementation APPChildViewController
- (id)init
{
self = [super init];
if (self) {
self.media = fill array...
}
return self;
}
It actually works when I copy the cellForRowAtIndexPath method implementation to all sub classes, but this is obviously not the way inheritance is intended to work...
When you make the switch, some cells will have a reference to the old child's media, and if that object is gone uh oh:
NSDictionary *rowData = [self.media objectAtIndex:row];
As soon so make the switch of dataSource, are you sending:
[self.tableView reloadData];
I'm assuming (hoping!) that you have just the one nib owned by the parent...

Master Detail Storyboard: Table View not showing cells (Objective-C)

I'm having troubles with the master viewController not showing any cells. This is the situation:
The app uses storyboard.
When the app launches, it goes to the navigationController
A button is pressed and connected to the table ViewController and it's set to "push" to it.
I've added the object and made a cell/detailView or whatever.
For some reason, the cell won't show up!!!
Here's the files:
MasterViewController.h:
#import <UIKit/UIKit.h>
#import "CraftingDetail.h"
#import "Crafting.h"
#class CraftingList;
#interface CraftingMaster : UITableViewController
#property (strong, nonatomic) CraftingDetail *detailViewController;
#property (strong, nonatomic) CraftingList *CL;
#end
MasterViewController.m:
#import "CraftingMaster.h"
#import "CraftingList.h"
#interface CraftingMaster ()
#end
#implementation CraftingMaster
#synthesize detailViewController = _detailViewController;
#synthesize CL;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
self.CL = [[CraftingList alloc] init];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return self.CL.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.CL.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
cell.textLabel.text = [self.CL craftingAtIndex:indexPath.row].Title;
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
#end
DetailViewController.h:
#import <UIKit/UIKit.h>
#interface CraftingDetail : UIViewController
#property (strong, nonatomic) IBOutlet UIImageView *Image;
#property (strong, nonatomic) IBOutlet UITextView *Description;
#end
This is an old question, but having an unexpectedly empty table view is a common issue when starting to develop with table views so hopefully this answer will be of use to someone.
Here are some things to check when your table view is devoid of cells and you expected otherwise:
Are your data source object(s) (self.CL in this case) valid? (ie. Are they != nil and point to the correct object?)
Does numberOfSectionsInTableView: return an integer value greater than zero?
Does tableView:numberOfRowsInSection: return an integer value greater than zero?
Here are a couple of problems in MasterViewController.m above that need attention:
InitWithStyle: will not be executed when the view controller is instantiated in a storyboard. Instead, initWithCoder: should be used. I suspect this was the source of JomanJi's pain as this resulted in self.CL not being instantiated. (As an aside, the data source object/property: CL should be instantiated by assigning the value to the _CL ivar directly, not to the property. See "Initializing a property, dot notation" to learn why).
Due to returning (what is likely) the same value for numberOfSectionsInTableView: as tableView:numberOfRowsInSection: (ie. "return self.CL.count;"), the table view will display the same number of sections as there are cells in each section with each sections' cells containing the same data as the other sections. I doubt this effect was what the developer intended. (This is of course unless the count accessor method in CraftingList does something really strange).
Without seeing the code for CraftingList it is impossible to determine exactly what the problem is. However, given the age of the question, I suspect JomanJi has since figured it out on his/her own.

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.