unrecognized selector sent to instance when clicking on UITableViewCell - ios

Looking around i can see this type of error has been various from person to person solution wise.
Basically i have a UITableViewController inside a ViewController, inside that tableView is a list of friends pulled from our online database. My goal was to acheive a sliding effect gesture from left to right, i found many sample codes and ended up using LRSlidingTableViewCell, at first we tried bringing over the files and calling them appropriately, the cheat code way i presume you would call it. And it threw an exception same as what im getting now. Accept i tried a new method since were going to need to call this sliding effect on 2 different views (seques) and decided to include them in a MyClass that i can call globally, i know the coding we use for calling global variables is solid, we have it working where it outputs us a current user login on every single page view we have. So now that ive discribed my situation. And what im trying to acomplish...
Heres my error code,
2013-01-25 13:29:40.397 myappbeta[5496:907] -[UITableViewCell openDrawer]: unrecognized selector sent to instance 0x1ddaacb0
2013-01-25 13:29:40.399 myappbeta[5496:907] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewCell openDrawer]: unrecognized selector sent to instance 0x1ddaacb0'
*** First throw call stack:
(0x39deb2a3 0x33cda97f 0x39deee07 0x39ded531 0x39d44f68 0x123b5 0x32fdf26d 0x33061ea1 0x34be6a6f 0x39dc05df 0x39dc0291 0x39dbef01 0x39d31ebd 0x39d31d49 0x380ff2eb 0x32f712f9 0xd129 0x3759db20)
libc++abi.dylib: terminate called throwing an exception
Heres MyClass.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface JBSlidingTableViewCell : UITableViewCell {
UIView* _bottomDrawer;
UIView* _topDrawer;
UILabel* _titleLabel;
}
// Callback: Called when the bottom drawer is about to be shown. Add subviews here.
- (void)bottomDrawerWillAppear;
// Callback: Called when the bottom drawer has disappeared and is about to be released.
// Release subviews here.
- (void)bottomDrawerDidDisappear;
// Creates the bottom drawer, then opens the top drawer to reveal it.
- (void)openDrawer;
// Closes the top drawer over the bottom drawer, then releases the bottom drawer.
- (void)closeDrawer;
#property (nonatomic, retain) UILabel* titleLabel;
#property (nonatomic, retain) UIView* bottomDrawer;
#property (nonatomic, retain) UIView* topDrawer;
#end
#interface MyClass : NSObject {
}
+ (NSString*)str;
+ (void)setStr:(NSString*)newStr;
+ (void)uploadImg:(UIImage*)img;
#end
and now the MyClass.m
#import "MyClass.h"
#import "QuartzCore/QuartzCore.h"
static NSString* str;
#implementation JBSlidingTableViewCell
#synthesize bottomDrawer = _bottomDrawer;
#synthesize topDrawer = _topDrawer;
#synthesize titleLabel = _titleLabel;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier {
self = [super initWithStyle:style reuseIdentifier:identifier];
if (nil != self) {
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 13, 304, 20)];
self.titleLabel.font = [UIFont boldSystemFontOfSize:17];
self.titleLabel.textColor = [UIColor blackColor];
self.titleLabel.backgroundColor = [UIColor clearColor];
[self.topDrawer addSubview:self.titleLabel];
_bottomDrawer = nil;
// Top drawer
self.topDrawer = [[UIView alloc] init];
self.topDrawer.backgroundColor = [UIColor whiteColor];
[self.contentView addSubview:self.topDrawer];
}
return self;
}
- (void)dealloc {
_bottomDrawer = nil;
_topDrawer = nil;
_titleLabel = nil;
}
- (void)closeDrawer {
if (self.topDrawer.hidden) {
CATransition* animation = [CATransition animation];
animation.delegate = self;
animation.type = kCATransitionPush;
animation.subtype = kCATransitionFromLeft;
animation.duration = 0.2f;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.contentView.layer addAnimation:animation forKey:nil];
self.contentView.hidden = NO;
self.topDrawer.hidden = NO;
}
}
- (void)openDrawer {
self.bottomDrawer = [[UIView alloc] initWithFrame:self.bounds];
[self bottomDrawerWillAppear];
[self insertSubview:self.bottomDrawer belowSubview:self.contentView];
CATransition* animation = [CATransition animation];
animation.type = kCATransitionPush;
animation.subtype = kCATransitionFromRight;
animation.duration = 0.2f;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.contentView.layer addAnimation:animation forKey:nil];
self.topDrawer.hidden = YES;
self.contentView.hidden = YES;
}
- (void)bottomDrawerDidDisappear {
// Can be overridden by subclasses.
}
- (void)bottomDrawerWillAppear {
self.bottomDrawer.backgroundColor = [UIColor lightGrayColor];
UILabel* bottomDrawerLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 13, 304, 20)];
bottomDrawerLabel.font = [UIFont boldSystemFontOfSize:17];
bottomDrawerLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0];
bottomDrawerLabel.shadowColor = [UIColor colorWithWhite:1.0 alpha:0.75];
bottomDrawerLabel.shadowOffset = CGSizeMake(0, 1);
bottomDrawerLabel.backgroundColor = [UIColor clearColor];
bottomDrawerLabel.text = [NSString stringWithFormat:#"Bottom drawer!%d",rand()];
[self.bottomDrawer addSubview:bottomDrawerLabel];}
- (void)layoutSubviews {
[super layoutSubviews];
self.topDrawer.frame = self.contentView.bounds;
}
- (void)animationDidStop:(CAAnimation*)anim finished:(BOOL)flag {
[self bottomDrawerDidDisappear];
[self.bottomDrawer removeFromSuperview];
self.bottomDrawer = nil;
}
#end
#implementation MyClass
+ (NSString*)str {
return str;
}
+ (void)setStr:(NSString*)newStr {
if (str != newStr) {
str = [newStr copy];
}
}
+ (void)uploadImg:(UIImage*)img {
NSData *imageData = UIImageJPEGRepresentation(img, 0.9);
NSMutableDictionary *userDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:[MyClass str],#"email", nil];
NSString *returnString;
NSString *urlString = #"MYURL";
NSURL *url = [NSURL URLWithString:urlString];
NSString *filename = [MyClass str];
NSMutableURLRequest *request= [[NSMutableURLRequest alloc] initWithURL:url] ;
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"POST"];
NSString *boundary = #"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data; boundary=%#",boundary];
[request addValue:contentType forHTTPHeaderField: #"Content-Type"];
NSMutableData *postbody = [NSMutableData data];
//NSMutableDictionary * params = [[NSMutableDictionary alloc] initWithObjects:[[NSArray alloc] initWithObjects:#"great problem", #"infront of my house",nil] forKeys:[[NSArray alloc] initWithObjects:#"problem",#"location",nil]];
NSMutableString *tempVal = [[NSMutableString alloc] init];
for(NSString * key in userDict)
{
[tempVal appendFormat:#"\r\n--%#\r\n", boundary];
[tempVal appendFormat:#"Content-Disposition: form-data; name=\"%#\"\r\n\r\n%#",key,[userDict objectForKey:key]];
}
NSString *postData = [tempVal description];
//here Webservices is my class name
[postbody appendData:[postData dataUsingEncoding:NSUTF8StringEncoding]];
if(imageData != nil)
{
[postbody appendData:[[NSString stringWithFormat:#"\r\n--%#\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[[NSString stringWithFormat:#"Content-Disposition: form-data; name=\"userfile\"; filetype=\"image/jpeg\"; filename=\"%#.jpg\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[#"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postbody appendData:[NSData dataWithData:imageData]];
}
[postbody appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"requesting upload");
// NSString *str2 = [[NSString alloc] initWithData:postbody encoding:NSUTF8StringEncoding];
// NSLog(str2);
[request setHTTPBody:postbody];
NSLog(#"uploading");
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
//NSLog(returnString);
NSLog(returnString);
}
#end
Now here are the files and code to where the view in the storyboard is called.
friendsListTableViewController.h
#import <UIKit/UIKit.h>
#import "TBXML.h"
#import <MessageUI/MessageUI.h>
#interface friendsListTableViewController: UIViewController <UIScrollViewDelegate, UITableViewDataSource, UITableViewDelegate, MFMailComposeViewControllerDelegate, NSObject, UIScrollViewDelegate>{
NSMutableArray *friendList;
TBXML * tbxml;
NSMutableArray *friends;
IBOutlet UILabel *friendsFullNames;
IBOutlet UIImage *imageFile;
IBOutlet UITableView *tableView;
#private
NSIndexPath* _openedCellIndexPath;
NSArray* _regularCellStrings;
UITableView* _tableView;
}
- (IBAction)inviteFriends:(id)sender;
#property (nonatomic, strong) NSMutableArray *friends;
#property (nonatomic, strong) NSMutableArray *friendList;
#property (nonatomic, retain) IBOutlet UITableView* tableView;
#end
and the friendsListTableViewController.m
#import "friendsListTableViewController.h"
#import "loginViewController.h"
#import "MyClass.h"
#interface friendsListTableViewController ()
#property (nonatomic, readonly) JBSlidingTableViewCell* openedCell;
#property (nonatomic, retain) NSIndexPath* openedCellIndexPath;
#property (nonatomic, copy) NSArray* regularCellStrings;
- (void)closeOpenedCell;
#end
#implementation friendsListTableViewController
#synthesize openedCellIndexPath = _openedCellIndexPath;
#synthesize regularCellStrings = _regularCellStrings;
#synthesize tableView = _tableView;
#synthesize friends, friendList;
- (id)initWithCoder:(NSCoder*)aDecoder {
self = [super initWithCoder:aDecoder];
if (nil != self) {
_openedCellIndexPath = nil;
self.regularCellStrings = [NSArray arrayWithObjects:#"First default cell", #"Second default cell", nil];
}
return self;
}
- (void)dealloc {
_openedCellIndexPath = nil;
tableView = nil;
}
- (JBSlidingTableViewCell*)openedCell {
JBSlidingTableViewCell* cell;
if (nil == self.openedCellIndexPath) {
cell = nil;
} else {
cell = (JBSlidingTableViewCell*)[self.tableView cellForRowAtIndexPath:self.openedCellIndexPath];
}
return cell;
}
#pragma mark -
#pragma mark Private Methods
- (void)closeOpenedCell {
[self.openedCell closeDrawer];
self.openedCellIndexPath = nil;
}
#pragma mark -
#pragma mark UIScrollViewDelegate Methods
- (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView {
[self closeOpenedCell];
}
#pragma mark -
#pragma mark UITableViewDataSource Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView {
return 1;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView setDelegate:self];
[self.tableView setDataSource:self];
friendList = [[NSMutableArray alloc] initWithCapacity:0];
//USED TO CONNECT TO SERVERS XML
//UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Your Userid Below :D" message:[MyClass str] delegate:self cancelButtonTitle:#"Try Agains" otherButtonTitles:nil, nil];
//[alert show];
NSString *someOtherString = [NSString stringWithFormat: #"MYURL?userid=%#", [MyClass str]];
//UIAlertView *alert2 = [[UIAlertView alloc] initWithTitle:#"Your Userid Below :D" message:someOtherString delegate:self cancelButtonTitle:#"Try Agains" otherButtonTitles:nil, nil];
//[alert2 show];
NSData *xmlData = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:someOtherString]];
tbxml = [[TBXML alloc]initWithXMLData:xmlData];
//USED FOR LOCAL XML //PLEASE USE ONLY WHEN IN DEVELOPMENT/TESTING
//NSString *xmlData = [[NSBundle mainBundle] pathForResource:#"friendlist" ofType:#"xml"];
//NSData *data = [[NSData alloc] initWithContentsOfFile:xmlData];
//tbxml = [[TBXML alloc]initWithXMLData:data];
//strings
// Obtain root element
TBXMLElement * root = tbxml.rootXMLElement;
if (root)
{
TBXMLElement * allFriends = [TBXML childElementNamed:#"friend" parentElement:root];
while (allFriends !=nil)
{
TBXMLElement * fname = [TBXML childElementNamed:#"fname" parentElement:allFriends];
NSString *firstName = [TBXML textForElement:fname];
TBXMLElement * lname = [TBXML childElementNamed:#"lname" parentElement:allFriends];
NSString *lastName = [TBXML textForElement:lname];
NSString *fullname = [NSString stringWithFormat:#"%# %#", firstName, lastName];
[friendList addObject:fullname];
allFriends = [TBXML nextSiblingNamed:#"friend" searchFromElement:allFriends];
}
//TBXMLElement *fname = [TBXML childElementNamed:#"fname" parentElement:elem_PLANT];
}
}
#pragma mark - UITableViewDelegate + UITableViewDatasource
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
return [friendList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell* cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (nil == cell) {
cell = [[[JBSlidingTableViewCell alloc] init]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
friendsFullNames = (UILabel *)[cell viewWithTag:12];
friendsFullNames.text = [friendList objectAtIndex:indexPath.row];
imageFile = (UIImage *)[cell viewWithTag:13];
//imageFile = [NSString stringWithFormat:#"%d", NUMBER_OF_ROWS - indexPath.row];
//cell.textLabel.text = [friendList objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
[self closeOpenedCell];
[(JBSlidingTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath] openDrawer];
self.openedCellIndexPath = indexPath;
}
/*
// 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
- (IBAction)inviteFriends:(id)sender {
if ([MFMailComposeViewController canSendMail]){
// Email Subject
NSString *emailTitle = #"Woot";
// Email Content
NSString *messageBody = #"Some HTML Content";
// To address
NSArray *toRecipents = [NSArray arrayWithObject:#"myemail#dress.com"];
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self;
[mc setSubject:emailTitle];
[mc setMessageBody:messageBody isHTML:YES];
[mc setToRecipients:toRecipents];
// Present mail view controller on screen
[self presentViewController:mc animated:YES completion:NULL];
}
else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No Account Found" message:#"You need to add an email account to your device" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:nil];
// optional - add more buttons:
[alert addButtonWithTitle:#"Add Account"];
[alert show];
}
}
- (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
switch (result)
{
case MFMailComposeResultCancelled:
NSLog(#"Mail cancelled");
break;
case MFMailComposeResultSaved:
NSLog(#"Mail saved");
break;
case MFMailComposeResultSent:
NSLog(#"Mail sent");
break;
case MFMailComposeResultFailed:
NSLog(#"Mail sent failure: %#", [error localizedDescription]);
break;
default:
break;
}
// Close the Mail Interface
[self dismissViewControllerAnimated:YES completion:NULL];
}
#end
In the storyboard its identifier "Cell" is correct, and the tableView that contains the cells is pulling the information correctly from the database perfectly fine.
We are receiving this error when we click the cell, in atempt to test the sliding functionality.
I know its a lot of source code to lok through. But i appreciate any time and effort put into helping me out.
Thanks

Is prototype's class set to JBSlidingTableViewCell? It seems not. (see i said rubbish here)
In general:
If you are using dynamic prototype cell in storyboard (but you allocate cell subviews programmatically so why you need prototype at all?):
you should create it with just ... = [[[JBSlidingTableViewCell alloc] init]; Then the customizations that you put in your initWithStyle:reuseIdentifier: you can put in awakeFromNib method (or you can customize cell in storyboard), if you need reuse id one for whole class, then just override reuseIdentifier getter, and return id there.

Related

Beyond Bounds Error

My app has a table view with a list of jobs it gets from a Parse table. The user can hit the "+" button on the navigation bar to go to another screen to create a new job. Once the new job is created, it returns to the list of jobs with the one that was just created now being added to the list. The problem is that I can select the two jobs that were already in the list with no problems but when I try to select the new job, the app crashes with this error:
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
Here is the code for the jobs list:
#import "UnassignedJobs.h"
#import "AppDelegate.h"
#import "NavController.h"
#import "LogInViewController.h"
#import "NewJobViewController.h"
#import "JobDetailViewController.h"
#import <Parse/Parse.h>
#interface UnassignedJobs ()
#property (nonatomic, strong) NSMutableArray *jobs;
#property (nonatomic, strong) NSMutableArray *objectIds;
#property (nonatomic, strong) UIActivityIndicatorView *loadingIndicator;
#property (nonatomic, strong) UIRefreshControl *refresh;
#end
#implementation UnassignedJobs
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView setDataSource:self];
[self.tableView setDelegate:self];
UIBarButtonItem *logoutButton = [[UIBarButtonItem alloc]initWithTitle:#"Logout" style:UIBarButtonItemStylePlain target:self action:#selector(logoutPressed)];
self.navigationItem.leftBarButtonItem = logoutButton;
UIBarButtonItem *newJobButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(createJob)];
self.navigationItem.rightBarButtonItem = newJobButton;
NSString *currentUserFullName = [[PFUser currentUser]objectForKey:#"Name"];
if ([currentUserFullName isEqualToString:#"Cory Pollard"] || [currentUserFullName isEqualToString:#"Richie Ray"]) {
newJobButton.enabled = YES;
}
else {
newJobButton.enabled = NO;
}
CGFloat width = CGRectGetWidth(self.view.bounds);
CGFloat height = CGRectGetHeight(self.view.bounds);
self.loadingIndicator = [[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(width / 2, height / 2, 37, 37)];
self.loadingIndicator.center = CGPointMake(width / 2, height / 2 - 37);
self.loadingIndicator.autoresizingMask = (UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin);
self.loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
self.loadingIndicator.hidesWhenStopped = YES;
[self.view addSubview:self.loadingIndicator];
[self.loadingIndicator startAnimating];
[self getJobs];
self.refresh = [[UIRefreshControl alloc]init];
self.refresh.tintColor = [UIColor blackColor];
[self.refresh addTarget:self action:#selector(refreshData) forControlEvents:UIControlEventValueChanged];
self.refreshControl = self.refresh;
}
- (void)viewDidAppear:(BOOL)animated {
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)getJobs {
self.jobs = [[NSMutableArray alloc]init];
self.objectIds = [[NSMutableArray alloc]init];
PFQuery *query = [PFQuery queryWithClassName:#"Jobs"];
[query setLimit:1000];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (NSDictionary *objectDictionary in objects) {
NSString *assigned = [objectDictionary objectForKey:#"assigned"];
if ([assigned isEqualToString:#"no"]) {
[self.jobs addObject:objectDictionary];
// self.objectIds = [objects valueForKeyPath:#"objectId"];
}
for (int i = 0; i < self.jobs.count; i++) {
[self.objectIds addObject:[self.jobs valueForKeyPath:#"objectId"]];
}
}
dispatch_async(dispatch_get_main_queue(), ^ {
[self.tableView reloadData];
[self.loadingIndicator stopAnimating];
});
}
else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (void)createJob {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
NewJobViewController *jobCreateScreen = [[NewJobViewController alloc]initWithNibName:#"NewJobViewController" bundle:nil];
NavController *newJobNav = [[NavController alloc]initWithRootViewController:jobCreateScreen];
newJobNav.delegate = jobCreateScreen;
appDelegate.window.rootViewController = newJobNav;
}
- (void)logoutPressed {
[PFUser logOut];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
LogInViewController *loginScreen = [[LogInViewController alloc]initWithNibName:#"LogInViewController" bundle:nil];
NavController *loginNavController = [[NavController alloc]initWithRootViewController:loginScreen];
loginNavController.delegate = loginScreen;
appDelegate.window.rootViewController = loginNavController;
}
- (void)refreshData {
[self getJobs];
[self.refresh endRefreshing];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return self.jobs.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
NSDictionary *jobDictionary = [self.jobs objectAtIndex:[indexPath row]];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"UITableViewCell"];
}
if (cell) {
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor blackColor];
cell.textLabel.text = [jobDictionary objectForKey:#"job"];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *jobDictionary = [self.jobs objectAtIndex:[indexPath row]];
JobDetailViewController *jobDetails = [[JobDetailViewController alloc]initWithNibName:#"JobDetailViewController" bundle:nil];
jobDetails.jobName = [jobDictionary objectForKey:#"job"];
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
// [formatter setDateFormat:#"MM-dd-yyyy"];
formatter.dateStyle = NSDateFormatterLongStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
jobDetails.jobDate = [formatter stringFromDate:[jobDictionary objectForKey:#"date"]];
jobDetails.objectId = [[self.objectIds objectAtIndex:indexPath.row]objectAtIndex:indexPath.row];
jobDetails.assignedWorker = [jobDictionary objectForKey:#"worker"];
jobDetails.details = [jobDictionary objectForKey:#"details"];
[self.navigationController pushViewController:jobDetails animated:YES];
}
#end
And here is the code to create a new job:
#import "NewJobViewController.h"
#import "AppDelegate.h"
#import "NavController.h"
#import "TabController.h"
#import "UnassignedJobs.h"
#import "AssignedJobs.h"
#import "MyJobs.h"
#import "Users.h"
#import "CompletedJobs.h"
#import <Parse/Parse.h>
#interface NewJobViewController ()
#property (weak, nonatomic) IBOutlet UITextField *jobName;
#property (weak, nonatomic) IBOutlet UITextView *detailTextView;
#property (weak, nonatomic) IBOutlet UIDatePicker *datePicker;
#property (weak, nonatomic) IBOutlet UIButton *createJobButton;
#property (nonatomic, strong) NSDate *jobDate;
#property (nonatomic, strong) NSString *dateString;
#end
#implementation NewJobViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.title = #"Create Job";
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]initWithTitle:#"Cancel" style:UIBarButtonItemStylePlain target:self action:#selector(cancel)];
self.navigationItem.leftBarButtonItem = cancelButton;
[self.datePicker addTarget:self action:#selector(updateDateString) forControlEvents:UIControlEventValueChanged];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)returnToMainScreen {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
UnassignedJobs *unassignedJobs = [[UnassignedJobs alloc]initWithNibName:#"UnassignedJobs" bundle:nil];
unassignedJobs.title = #"Unassigned";
NavController *navController = [[NavController alloc]initWithRootViewController:unassignedJobs];
navController.delegate = unassignedJobs;
AssignedJobs *assignedJobs = [[AssignedJobs alloc]initWithNibName:#"AssignedJobs" bundle:nil];
assignedJobs.title = #"Assigned";
NavController *assignedNav = [[NavController alloc]initWithRootViewController:assignedJobs];
assignedNav.delegate = assignedJobs;
CompletedJobs *completed = [[CompletedJobs alloc]initWithNibName:#"CompletedJobs" bundle:nil];
completed.title = #"Completed";
NavController *completedNav = [[NavController alloc]initWithRootViewController:completed];
completedNav.delegate = completed;
MyJobs *myJobs = [[MyJobs alloc]initWithNibName:#"MyJobs" bundle:nil];
myJobs.title = #"My Jobs";
NavController *myNav = [[NavController alloc]initWithRootViewController:myJobs];
myNav.delegate = myJobs;
Users *userList = [[Users alloc]initWithNibName:#"Users" bundle:nil];
userList.title = #"Users";
NavController *userNav = [[NavController alloc]initWithRootViewController:userList];
userNav.delegate = userList;
TabController *tabController = [[TabController alloc]init];
tabController.viewControllers = #[navController, assignedNav, completedNav, myNav, userNav];
appDelegate.window.rootViewController = tabController;
}
- (void)updateDateString {
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
formatter.dateStyle = NSDateFormatterLongStyle;
formatter.timeStyle = NSDateFormatterMediumStyle;
self.dateString = [formatter stringFromDate:self.datePicker.date];
self.jobDate = [formatter dateFromString:self.dateString];
}
- (IBAction)createJob:(id)sender {
PFObject *job = [PFObject objectWithClassName:#"Jobs"];
job[#"job"] = self.jobName.text;
job[#"details"] = self.detailTextView.text;
job[#"assigned"] = #"no";
job[#"date"] = self.jobDate;
[job saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
// Close this window and return to unassigned jobs
[self returnToMainScreen];
}
else {
NSString *errorString = [[error userInfo] objectForKey:#"error"];
UIAlertView *errorAlert = [[UIAlertView alloc]initWithTitle:#"Job could not be saved!" message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[errorAlert show];
}
}];
}
- (void)cancel {
[self returnToMainScreen];
}
# pragma mark UITextView Delegate Methods
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if([text isEqualToString:#"\n"]) {
[textView resignFirstResponder];
return NO;
}
return YES;
}
# pragma mark UITextField Delegate Methods
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}
#end
you have to reload the jobs data source, and show the table again,
so,
#implementation UnassignedJobs ...
- (void)viewDidAppear:(BOOL)animated {
[self getJobs];
[self.table reloadData];
}
and dont forget to clean the mutable arrays
- (void)getJobs {
[self.jobs removeAllObjects];
[self.objectIds removeAllObjects];
self.jobs = [[NSMutableArray alloc]init];
self.objectIds = [[NSMutableArray alloc]init];
...}
MaKo is correct in stating you need to reload the jobs data source (because after you pop back to the table controller, the data source won't know about the new row until it is reloaded. However, you don't need to re-initiate the arrays (even if you did this, you wouldn't need to remove all the objects first since it's redundant). It would be better practice to maintain your existing arrays properly than to create new ones each time.

NSCoding: Save custom object and load to table view iOS

I try to understand the application to save the data. I have a custom object.
#interface CellObject : NSObject <NSCoding>
#property (nonatomic, strong) NSString *links;
#property (nonatomic, strong) NSString *title;
#property (assign) BOOL isFavorite;
#end
#import "CellObject.h"
#implementation CellObject
#synthesize title, links;
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:title forKey:#"title"];
[aCoder encodeObject:links forKey:#"links"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self){
[self setTitle:[aDecoder decodeObjectForKey:#"title"]];
[self setLinks:[aDecoder decodeObjectForKey:#"links"]];
}
return self;
}
And the controller to the table in cells which must be my objects.
#import "SettingsViewController.h"
#import "SettingsCell.h"
#import "CellObject.h"
#define kFileName #"archive"
#define kDataKey #"Data"
#implementation SettingsViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
self.title = #"Settings";
[[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIFont fontWithName:#"Helvetica" size:20.0f], NSFontAttributeName, nil] forState:UIControlStateNormal];
}
return self;
}
-(NSString *)dataFilePath {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFileName];
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:app];
[_rssObjectArray removeAllObjects];
_rssObjectArray = nil;
if(!_rssObjectArray)
{
_rssObjectArray = [[NSMutableArray alloc]init];
}
_resourceTV = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) style:UITableViewStylePlain];
[_resourceTV setAutoresizesSubviews:YES];
[_resourceTV setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
[_resourceTV setDataSource:self];
[_resourceTV setDelegate:self];
[self.view addSubview:_resourceTV];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(showAlert)];
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSMutableData* data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
CellObject *cellObj = [unarchiver decodeObjectForKey:kDataKey];
[unarchiver finishDecoding];
}
}
-(void)applicationWillResignActive:(NSNotification *)notification
{
CellObject *cellObj = [[CellObject alloc] init];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:cellObj forKey:kDataKey];
[archiver finishEncoding];
[data writeToFile:[self dataFilePath] atomically:YES];
}
- (void)showAlert
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Enter name" message:#"And internet adress" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok", nil];
alertView.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
UITextField *bottomField = [alertView textFieldAtIndex:0];
[bottomField setPlaceholder:#"Name"];
UITextField *footerField = [alertView textFieldAtIndex:1];
[footerField setPlaceholder:#"Internet adress"];
footerField.secureTextEntry = NO;
alertView.tag = -1;
[alertView show];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _rssObjectArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = [SettingsCell cellIdentifier];
SettingsCell *sCell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!sCell)
{
sCell = [[SettingsCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
CellObject *object = [_rssObjectArray objectAtIndex:indexPath.row];
sCell.linkLabel.text = object.links;
sCell.nameLabel.text = object.title;
sCell.favorite.selected = object.isFavorite;
return sCell;
}
//change rows in table
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[_rssObjectArray removeObjectAtIndex:indexPath.row];
[_resourceTV deleteRowsAtIndexPaths:#[indexPath] withRowAnimation: UITableViewRowAnimationTop];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50;
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex && (alertView.alertViewStyle == UIAlertViewStyleLoginAndPasswordInput))
{
CellObject *object = nil;
if (alertView.tag == -1) {
object = [[CellObject alloc]init];
[_rssObjectArray addObject:object];
} else {
object = [_rssObjectArray objectAtIndex:alertView.tag];
}
object.links = [[alertView textFieldAtIndex:0] text];
object.title = [[alertView textFieldAtIndex:1] text];
[_resourceTV reloadData];
}
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Enter name" message:#"And internet adress" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok", nil];
alertView.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
UITextField *bottomField = [alertView textFieldAtIndex:0];
[bottomField setPlaceholder:#"Name"];
UITextField *footerField = [alertView textFieldAtIndex:1];
[footerField setPlaceholder:#"Internet adress"];
footerField.secureTextEntry = NO;
CellObject *cellObj = [_rssObjectArray objectAtIndex:indexPath.row];
bottomField.text = cellObj.links;
footerField.text = cellObj.title;
alertView.tag = indexPath.row;
[alertView show];
}
#end
On the Internet find tutorials, but they are not very helpful, tell me how to do so would object persists after restarting the application, they remained in the cells?
In your viewDidLoad:
- (void)viewDidLoad
{
// ...
if ([[NSFileManager defaultManager] fileExistsAtPath:[self dataFilePath]]) { // Check if the file exist.
NSMutableData* data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]]; // Retrieve the data from the file
self.rssObjectArray = [[NSKeyedUnarchiver unarchiveObjectWithData:data] mutableCopy];
}
}
And your
- (NSString *)dataFilePath
{
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:#"CustomFeed.plist"];
/*BOOL result = [NSKeyedArchiver archiveRootObject:self.rssObjectArray toFile:path];
// This line above erase the file when it's called, remove all the code i commented.
if(result == YES)
{
NSLog(#"Array saved");
}*/
return path;
}
With these modifications, i tested your application and it's work.

How to add dynamically rows to a UITableView in iOS?

I'm developing a view controller for a chat application and I want to show a UIViewController that contains a UITableView (where messages are shown with different format [if is your message or if is a message from other person], a UITextField (to write your messages) and a UIButton (to send the message)
I'm using SRWebSocket example but they use a UITableViewController (that runs perfectly but don't allow me to modify tableview size or to add the others components to the view by storyboard)
This is the code that I have in my Controller:
ChatViewController.h
#import <UIKit/UIKit.h>
#import "SRWebSocket.h"
#import "ChatCell.h"
#import "Message.h"
#import "Person.h"
#import "Program.h"
#import "DateFactory.h"
#interface ChatViewController : UIViewController <UITableViewDataSource,UITableViewDelegate,SRWebSocketDelegate, UITextViewDelegate, UITextFieldDelegate>
#property (strong, nonatomic) NSDictionary *programSegue;
#property (retain, nonatomic) IBOutlet UITableView *tableView;
#property (nonatomic, retain) IBOutlet UITextView *inputView;
- (IBAction)goingUp:(id)sender;
#property (weak, nonatomic) IBOutlet UITextField *inputText;
#end
ChatViewController.m
Code that fails:
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:_messages.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
in:
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
{
NSLog(#"Received \"%#\"", message);
NSError *e;
NSDictionary *allJSON =
[NSJSONSerialization JSONObjectWithData: [message dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &e];
NSString *kindJSON = [allJSON objectForKey:#"kind"];
NSString *userJSON = [allJSON objectForKey:#"user"];
NSString *messageJSON = [allJSON objectForKey:#"message"];
NSArray *membersJSON = [allJSON objectForKey:#"members"];
DateFactory *dateFactory = [DateFactory alloc];
NSString *formatDate = #"dd/MM/YYYY HH:mm";
NSString *dateString = [dateFactory dateToString:[NSDate date] withFormat:formatDate];
switch([#[#"join", #"talk", #"quit"] indexOfObject:kindJSON]){
// join
case 0:
break;
// talk
case 1:
[_messages addObject:[[Message alloc] initWithMessage:messageJSON fromMe:NO]];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:_messages.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView scrollRectToVisible:self.tableView.tableFooterView.frame animated:YES];
break;
// quit
case 2:
[[self.navigationItem.titleView.subviews objectAtIndex:1] setText:
[NSString stringWithFormat:#"Sin conexión desde %#", dateString]];
break;
}
}
ERROR
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert row 0 into section 0, but there are only 0 rows in section 0 after the update'
Full code:
#import "ChatViewController.h"
#interface ChatViewController ()
#end
#implementation ChatViewController{
SRWebSocket *_webSocket;
NSMutableArray *_messages;
Person *person;
Program *program;
}
#synthesize programSegue;
#synthesize tableView;
#synthesize inputText;
#synthesize inputView = _inputView;
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
return [inputText resignFirstResponder];
}
#pragma mark - View lifecycle
- (void)viewDidLoad;
{
[super viewDidLoad];
[inputText setDelegate:self];
person = [programSegue objectForKey:#"PERSON"];
program = [programSegue objectForKey:#"PROGRAM"];
self.navigationItem.title = person.name;
// Creates picture to be shown in navigation bar
UIButton* picture = (UIButton *) [[UIImageView alloc] initWithImage:[UIImage imageNamed:person.imageURL]];
CGRect buttonFrame = picture.frame;
buttonFrame.size = CGSizeMake(38, 38);
picture.frame = buttonFrame;
UIBarButtonItem *pictureItem = [[UIBarButtonItem alloc] initWithCustomView:picture];
self.navigationItem.rightBarButtonItem = pictureItem;
// Set title and subtitle
CGRect frame = self.navigationController.navigationBar.frame;
UIView *twoLineTitleView = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetWidth(frame), 0, CGRectGetWidth(frame), CGRectGetHeight(frame))];
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 6, CGRectGetWidth(frame), 20)];
titleLabel.backgroundColor = [UIColor clearColor];
[titleLabel setTextColor:[UIColor whiteColor]];
titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[titleLabel setTextAlignment:NSTextAlignmentCenter];
[titleLabel setFont:[UIFont boldSystemFontOfSize:16]];
[titleLabel setShadowColor:[UIColor grayColor]];
titleLabel.text = person.name;
[twoLineTitleView addSubview:titleLabel];
UILabel *subTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 26, CGRectGetWidth(frame), 14)];
subTitleLabel.backgroundColor = [UIColor clearColor];
[subTitleLabel setTextColor:[UIColor whiteColor]];
subTitleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[subTitleLabel setTextAlignment:NSTextAlignmentCenter];
[subTitleLabel setFont:[UIFont boldSystemFontOfSize:12]];
[titleLabel setShadowColor:[UIColor grayColor]];
subTitleLabel.text = #"subtitleg";
[twoLineTitleView addSubview:subTitleLabel];
self.navigationItem.titleView = twoLineTitleView;
// Start messages
_messages = [[NSMutableArray alloc] init];
[self.tableView reloadData];
}
- (void)_reconnect;
{
_webSocket.delegate = nil;
[_webSocket close];
_webSocket = [[SRWebSocket alloc] initWithURLRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:
[NSString stringWithFormat:#"ws://81.45.19.228:8000/room/chat?username=enrimr&pid=%#", person.name]]]];
_webSocket.delegate = self;
//self.title = #"Opening Connection...";
[[self.navigationItem.titleView.subviews objectAtIndex:1] setText:#"Conectando..."];
[_webSocket open];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self _reconnect];
}
- (void)reconnect:(id)sender;
{
[self _reconnect];
}
- (void)viewDidAppear:(BOOL)animated;
{
[super viewDidAppear:animated];
[_inputView becomeFirstResponder];
[self.tableView scrollRectToVisible:self.tableView.tableFooterView.frame animated:YES];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
_webSocket.delegate = nil;
[_webSocket close];
_webSocket = nil;
}
#pragma mark - UITableViewController
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
return _messages.count;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
{
ChatCell *chatCell = (id)cell;
Message *message = [_messages objectAtIndex:indexPath.row];
chatCell.text.text = message.message;
chatCell.date.text = message.fromMe ? #"Me" : #"Other";
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
Message *message = [_messages objectAtIndex:indexPath.row];
ChatCell *cell = (ChatCell *)[self.tableView dequeueReusableCellWithIdentifier:#"programCell" forIndexPath:indexPath];
if (!cell) {
if (message.fromMe){
cell = [[ChatCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"SentCell"];
[cell.text setText:message.message];
[cell.date setText:#"00:00"];
}
else {
cell = [[ChatCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"ReceivedCell"];
[cell.text setText:message.message];
[cell.date setText:#"00:00"];
}
}
return cell;
}
#pragma mark - SRWebSocketDelegate
- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
{
NSLog(#"Websocket Connected");
//self.title = #"Connected!";
[[self.navigationItem.titleView.subviews objectAtIndex:1] setText:#"Conectado"];
}
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
{
NSLog(#":( Websocket Failed With Error %#", error);
self.title = #"Connection Failed! (see logs)";
_webSocket = nil;
}
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
{
NSLog(#"Received \"%#\"", message);
NSError *e;
NSDictionary *allJSON =
[NSJSONSerialization JSONObjectWithData: [message dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &e];
NSString *kindJSON = [allJSON objectForKey:#"kind"];
NSString *userJSON = [allJSON objectForKey:#"user"];
NSString *messageJSON = [allJSON objectForKey:#"message"];
NSArray *membersJSON = [allJSON objectForKey:#"members"];
DateFactory *dateFactory = [DateFactory alloc];
NSString *formatDate = #"dd/MM/YYYY HH:mm";
NSString *dateString = [dateFactory dateToString:[NSDate date] withFormat:formatDate];
switch([#[#"join", #"talk", #"quit"] indexOfObject:kindJSON]){
// join
case 0:
break;
// talk
case 1:
[_messages addObject:[[Message alloc] initWithMessage:messageJSON fromMe:NO]];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:_messages.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView scrollRectToVisible:self.tableView.tableFooterView.frame animated:YES];
break;
// quit
case 2:
[[self.navigationItem.titleView.subviews objectAtIndex:1] setText:
[NSString stringWithFormat:#"Sin conexión desde %#", dateString]];
break;
}
}
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
{
NSLog(#"WebSocket closed");
//self.title = #"Connection Closed! (see logs)";
[[self.navigationItem.titleView.subviews objectAtIndex:1] setText:#"Offline"];
_webSocket = nil;
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
{
if ([text rangeOfString:#"\n"].location != NSNotFound) {
NSString *message = [[textView.text stringByReplacingCharactersInRange:range withString:text] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[_webSocket send:message];
[_messages addObject:[[Message alloc] initWithMessage:message fromMe:YES]];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:_messages.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView scrollRectToVisible:self.tableView.tableFooterView.frame animated:YES];
textView.text = #"";
return NO;
}
return YES;
}
- (void) animateTextField: (UITextField*) textField up: (BOOL)up
{
const int movementDistance = 218;
const float movementDuration = 0.3f;
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
- (IBAction)goingUp:(id)sender {
[self animateTextField:inputText up:TRUE];
}
#end
When you use insertRowsAtIndexPaths you have to first update the table view data source. So before you call the insertRowsAtIndexPaths you should do something like _messages addObject:newMessage.
Just as a helper rule, whenever you update the rows of a table view without using reloadData method, you have to update the tableView`s data source to reflect the index paths that will be updated. So if you delete on row from your table view, the data associated with that row must be deleted from data source, also if you add a row to the table view, you have to add the associated data of the new row into the data source. ALWAYS UPDATE THE DATASOURCE FIRST.
And every time you update the rows of a table view you should use the update method between beginUpdates and endUpdates method calls.
The problem was that I forgot to set
[tableView setDataSource:self];
[tableView setDelegate:self];
in my viewDidLoad. These two lines will fix my problem.

iOS UITableView content not loading - Property access result unused

I'm trying to load a table with content from Twitter. The table is in a UIView and being created in the drawRect()...but I keep getting a warning:
Property access result unused - getters should not be used for side effects
on each.
Nothing show up in my table.
Here's my .h file:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import <Twitter/Twitter.h>
#import "ColorController.h"
#interface TwitterController : UIView <UITableViewDelegate, UITableViewDataSource> {
UIButton* btnCloseView;
UITableView* tblTweets;
UIImageView* imgTwitterIcon;
ColorController* colorManager;
NSMutableArray* tweetsArray;
NSString* twitterID;
}
#property (nonatomic, retain) NSString* twitterID;
- (void) getTweets;
- (void) closeWin;
#end
and my .m
#import "TwitterController.h"
#implementation TwitterController
#synthesize twitterID;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
colorManager = [ColorController new];
}
return self;
}
- (void)drawRect:(CGRect)rect {
imgTwitterIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"imgTwitterBird"]];
CGRect twitterIconFrame = [imgTwitterIcon frame];
twitterIconFrame.origin.x = 50.0;
twitterIconFrame.origin.y = 20.0;
tblTweets = [[UITableView alloc] initWithFrame:CGRectMake(50.0, 25.0, 220.0, 500.0)];
tblTweets.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
tblTweets.separatorColor = [colorManager setColor:176.0:196.0:222.0];
tblTweets.layer.borderWidth = 1.0;
tblTweets.rowHeight = 20.0;
tblTweets.scrollEnabled = YES;
tblTweets.delegate.self;
tblTweets.dataSource.self;
UIImage* imgCloseButton = [UIImage imageNamed:#"btnCloseWindow.png"];
CGSize imageSize = imgCloseButton.size;
btnCloseView = [[UIButton alloc] initWithFrame: CGRectMake(220.0, 550.0, imageSize.width, imageSize.height)];
[btnCloseView setImage:[UIImage imageNamed:#"btnCloseWindow.png"] forState:UIControlStateNormal];
[btnCloseView addTarget:self action:#selector(closeWin:) forControlEvents:UIControlEventTouchUpInside];
[self getTweets];
[self addSubview:tblTweets];
[self addSubview:imgTwitterIcon];
[self addSubview:btnCloseView];
}
- (void) getTweets {
//array to hold tweets
tweetsArray = [[NSMutableArray alloc] init];
///set up a NSURL to the twitter API
NSURL* twitterAPI = [NSURL URLWithString:[NSString stringWithFormat:#"https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&screen_name=%#&count=10", twitterID]];
//get last 10 tweets (max is 20)
TWRequest *twitterRequest = [[TWRequest alloc] initWithURL:twitterAPI
parameters:nil requestMethod:TWRequestMethodGET];
// Notice this is a block, it is the handler to process the response
[twitterRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if ([urlResponse statusCode] == 200) {
// The response from Twitter is in JSON format
// Move the response into a dictionary and print
NSError *error;
NSDictionary *tweetsDict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
for(NSDictionary* thisTweetDict in tweetsDict) {
[tweetsArray addObject:[thisTweetDict objectForKey:#"text"]];
}
[tblTweets reloadData];
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
}
#pragma mark Table Management
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [tweetsArray count];
NSLog(#"%i", [tweetsArray count]);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [tweetsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"tableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.textColor = [UIColor colorWithRed:66.0/255.0 green:66.0/255.0 blue:66.0/255.0 alpha:1];
cell.textLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size: 13.0];
cell.textLabel.text = [tweetsArray objectAtIndex:indexPath.row];
CGRect cellFrame = [cell frame];
cellFrame.size.height = 25.0;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString* thisTweet = [tweetsArray objectAtIndex:indexPath.row];
}
#pragma mark Close Window
- (void) closeWin {
NSMutableDictionary* userData = [[NSMutableDictionary alloc] init];
[userData setObject:#"closeTwitter" forKey:#"theEvent"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"theMessenger" object:self userInfo: userData];
}
#end
drawRect is used to draw stuff inside this views, using drawing functions
You should move your views additions to the layoutSubviews
Instead of - (void)drawRect:(CGRect)rect use - (void)layoutSubviews
This may or may not solve your issues, but nevertheless its the correct approach

Memory management in viewDidUnload - should I nil both array and objects inside?

I have a tableview to load news from internet. I and trying to nil all properties in viewDidUnload.
- (void)viewDidUnload
{
self.newsArray = nil;
self.newsTableView = nil;
self.indicatorView = nil;
// self.iconDownLoader = nil;
self.downloadArray = nil;
[super viewDidUnload];
}
Every time the app crash in viewDidUnload. If I comment self.iconDownLoader = nil;, it will be fine. So can any one tell me why does this happen? Thanks you.
---------------------NewsViewController.m--------------------------
//
// NewsViewController.m
//
// Created by on 18/01/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "NewsViewController.h"
#import "ASIHTTPRequest.h"
#import "SBJson.h"
#import "NewsModel.h"
#import "NewsDetailViewController.h"
#define kCustomRowCount 6
#define IconPlaceHolder #"Spinner"
#implementation NewsViewController
#synthesize appDelegate, newsTableViewCell, newsTableView, indicatorView;
#synthesize iconDownLoader, newsArray, downloadArray;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// setup appDelegate
self.appDelegate = (SydneyAppDelegate *)[[UIApplication sharedApplication] delegate];
// initial arrays
self.newsArray = [[NSMutableArray alloc] init];
self.downloadArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
if(self.appDelegate.reachable) {
[self getNews];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No Connection" message:#"No Internet connection. Please try again later." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
- (void)viewDidUnload
{
self.newsArray = nil;
self.newsTableView = nil;
self.indicatorView = nil;
// self.iconDownLoader = nil;
self.downloadArray = nil;
[super viewDidUnload];
}
#pragma mark - ASIHTTPRequest
- (void) getNews
{
NSURL *url = [NSURL URLWithString:#"http://ferrarimaseratisydney.com/api/getPublicNews.html"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
- (void) requestFinished:(ASIHTTPRequest *)request
{
NSString *responseString = [request responseString];
NSArray *json = [responseString JSONValue];
for (id aNewsInJson in json)
{
NewsModel *aNews = [[NewsModel alloc] initWithJson:aNewsInJson];
[self.newsArray addObject:aNews];
}
[self.indicatorView removeFromSuperview];
[self.newsTableView reloadData];
}
- (void) requestFailed:(ASIHTTPRequest *)request
{
NSError *error;
error = [request error];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// Navigation logic may go here. Create and push another view controller.
NewsDetailViewController *newsDetailViewController = [[NewsDetailViewController alloc] init];
// transform news array
newsDetailViewController.news = [self.newsArray objectAtIndex:indexPath.row];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:newsDetailViewController animated:YES];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.newsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"NewsCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"NewsTableViewCell" owner:self options:nil];
cell = self.newsTableViewCell;
self.newsTableViewCell = nil;
}
// read from newsModel
NewsModel *news = [self.newsArray objectAtIndex:indexPath.row];
UILabel *label;
label = (UILabel *)[cell viewWithTag:10];
label.text = [NSString stringWithString:news.title];
label = nil;
label = (UILabel *)[cell viewWithTag:11];
label.text = [NSString stringWithString:news.description];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:12];
imageView.image = news.image;
if (news.image == nil)
{
imageView.image = [UIImage imageNamed:IconPlaceHolder];
self.iconDownLoader = [[IconDownLoader alloc] init];
self.iconDownLoader.url = news.imageUrl;
self.iconDownLoader.delegate = self;
self.iconDownLoader.indexPath = indexPath;
if (self.appDelegate.ip4 == YES)
{
self.iconDownLoader.width = 300;
self.iconDownLoader.height = 150;
}
else
{
self.iconDownLoader.width = 150;
self.iconDownLoader.height = 75;
}
[self.downloadArray addObject:self.iconDownLoader];
[self.iconDownLoader start];
}
return cell;
}
#pragma mark - IconDownLoaderDelegate
- (void)iconDownLoadFinsh:(NSData *)imageData row:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.newsTableView cellForRowAtIndexPath:indexPath];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:12];
if (imageData != 0)
{
imageView.image = [UIImage imageWithData:imageData];
}
else
{
imageView.image = [UIImage imageNamed:#"icon57"];
}
NewsModel *newsModel = [self.newsArray objectAtIndex:indexPath.row];
newsModel.image = [UIImage imageWithData:imageData];
}
#end
-----------------------IconDownLoader.m-------------------
//
// IconDownLoader.m
//
// Created by on 24/11/11.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import "IconDownLoader.h"
#import "ASIHTTPRequest.h"
#implementation IconDownLoader
#synthesize delegate = _delegate;
#synthesize url = _url;
#synthesize indexPath = _indexPath;
#synthesize width = _width;
#synthesize height = _height;
#synthesize request = _request;
- (void)start {
NSString *originalString = #"width=%s&height=%s";
NSString *newString = [NSString stringWithFormat:#"width=%d&height=%d&type=jpg", self.width, self.height];
NSString *resizedURL = [self.url stringByReplacingOccurrencesOfString:originalString withString:newString];
NSURL *url = [NSURL URLWithString:[resizedURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
_request = [ASIHTTPRequest requestWithURL:url];
if (_indexPath) {
_request.userInfo = [NSDictionary dictionaryWithObject:_indexPath forKey:#"indexPath"];
}
[_request setDelegate:self];
[_request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request {
NSInteger statusCode = request.responseStatusCode;
switch (statusCode) {
case 401: // Not Authorized: either you need to provide authentication credentials, or the credentials provided aren't valid.
break;
case 200: {
NSData *responseData = [request responseData];
if (!responseData) {
UIAlertView *alertView;
alertView = [[UIAlertView alloc] initWithTitle:#"Oops" message:[NSString stringWithFormat:#"Download failed in row %d", _indexPath.row] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
return;
}
[_delegate iconDownLoadFinsh:responseData row:[request.userInfo objectForKey:#"indexPath"]];
}
break;
default:{
}
}
}
- (void)dealloc {
if (_request != nil) {
[_request clearDelegatesAndCancel];
}
}
#end
Generally in viewDidUnload you should only release and zero out all references to the nib objects you own.
That said, you can destroy model objects in viewDidUnload too if they consume a lot of memory. You should remember that viewDidUnload is a counterpart to viewDidLoad, so a good rule of thumb is to destroy only those objects in viewDidUnload which you create in viewDidLoad. You should also remember that viewDidUnload is not called when the view controller is released – only when its view is.
In your case I would not release newsArray and downloadArray just because you create them in init.... I would just send them removeAllObjects instead.
As for the crash, you create a new shared icon downloader every time a cell needs an image, which is a bit awkward. If you do need a shared downloader instance, you should not recreate it for each and every cell. It looks like you want to keep all the downloaders you created in an ivar array instead because each downloader is one-shot and is responsible for loading a single image.
Not enough information here to tell, but probably you have released iconDownloader directly in some other part of the code we cannot see without setting the reference to nil at that time.
Then in viewDidUnload you are trying to release an invalid reference.
For #synthesize, use:
#synthesize iconDownLoader = _iconDownloader;
then fix all of the compiler warnings to use self.icondownloder instead of iconDownloader, and eliminate all uses of "release" (assuming your property is marked as retain which it should be).
In fact, perhaps your whole problem is that the property is not a retain property so you make the iconDOwnloader and it's promptly released.

Resources