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.
Related
I have a custom UITableViewCell created in a .xib and add it to a TableView. The cell contains a Button to download some data. On Button click the download starts and the Button disappears to show a cancel Button and a custom View with a download progress. After the download is finished I update my model and reload the rows in the visible area of the app.
When I debug, I see that the cellForRowAtIndexPath-methode get called and the model got updated. This means the cancel-Button and the progress-View get set hidden = YES; But they don't disappear. After I scroll the cell out of view and back in, the progress-View is hidden but the cancel-Button not.
The TableView Methodes:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifierHeaderCell = #"PodcastListHeaderCell";
static NSString *cellIdentifierBodyCell = #"PodcastListBodyCell";
// Convert string to date object
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"EEE, d MMM yyyy HH:mm:ss Z"];
if(indexPath.row == 0) {
MGPodcastListHeaderCell *cell = (MGPodcastListHeaderCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifierHeaderCell];
if (cell == nil)
{
...
}
return cell;
}
else {
MGPodcastListBodyCell *cell = (MGPodcastListBodyCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifierBodyCell];
if (cell == nil) {
UIViewController *controller = [[UIViewController alloc] initWithNibName:#"MGPodcastListBodyCell" bundle:nil];
cell = (MGPodcastListBodyCell *)controller.view;
NSMutableDictionary *mediaIntem = self.mediaData[(NSUInteger) (indexPath.row-1)];
cell.mediaTitleLabel.text = mediaIntem[#"title"];
NSDate *date = [dateFormat dateFromString:mediaIntem[#"pubDate"]];
cell.pubDateLabel.text = [date descriptionWithLocale:[NSLocale currentLocale]];
cell.durationLabel.text = mediaIntem [#"duration"];
cell.accessoryType = UITableViewCellAccessoryDetailButton;
cell.podcastId = (NSInteger) (indexPath.row-1);
cell.cellPlayState = [[MGPlayState alloc] initWithPlayState:(NSInteger) [mediaIntem[#"playState"] integerValue]];
[cell setPodcastCellDelegate:self];
}
return cell;
}
}
-(void) downloadButtonPressedOfCell:(NSInteger)podcastId {
APConnection *con = [[APConnection alloc] init];
BOOL reachable = [con reachableHost];
if (reachable)
{
//============Get Media Item =============================
NSMutableDictionary *mediaDict = self.mediaData[(NSUInteger)podcastId];
MGPlayState *pl_state = [[MGPlayState alloc] initWithPlayState:[[mediaDict objectForKey:#"playState"] integerValue]];
NSString *urlString = [mediaDict objectForKey:#"mediaLink"];
/// Finde Pathname
NSString *fileName = [urlString lastPathComponent];
NSLog(#"LastFileComponent: %#", fileName);
NSString *pathName = [NSString stringWithFormat:#"%#/%#",
[APFilePath getMediaContentFolder],
fileName];
/// Request und Operation
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:pathName
append:NO];
//// save Operation for cancle
NSMutableDictionary *operationDict = [[NSMutableDictionary alloc] init];
[operationDict setObject:operation
forKey:#"operation"];
[operationDict setObject:[NSNumber numberWithInt:podcastId]
forKey:#"myIndexPath"];
[operationDict setObject:[mediaDict objectForKey:#"mediaLink"]
forKey:#"mediaLink"];
[[self operationDictArr] addObject:operationDict];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSIndexPath *path = [NSIndexPath indexPathForRow:podcastId+1 inSection:0];
MGPodcastListBodyCell *myCell = (MGPodcastListBodyCell *) [self.podcastListTable cellForRowAtIndexPath:path];
[pl_state setToPlayState:PlayStateDefaultDownloadFinished];
myCell.cellPlayState = pl_state;
//============ Get mediaItem =============================
self.mediaData[(NSUInteger)podcastId][#"playState"] = #4;
/// remove operation from dict
[[self operationDictArr] removeObject:operationDict];
[self.podcastListTable reloadRowsAtIndexPaths:[self.podcastListTable indexPathsForVisibleRows]
withRowAnimation:UITableViewRowAnimationNone];
[self.podcastListTable setNeedsDisplay];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog (#"Error downloadMovie: %#", error);
}];
[operation start];
}
else
{
[EZToastView showToastMessage:NSLocalizedString(#"keineVerbindungKey", "")
withAlignment:EZToastViewAlignmentCenter];
}
}
Custom Cell:
//// MGPodcastListBodyCell.h
#protocol MGPodcastCellDelegate <NSObject>
#required
-(void) downloadButtonPressedOfCell: (NSInteger) podcastId;
-(void) cancleDownloadButtonPressedOfCell: (NSInteger) podcastId;
#end
#interface MGPodcastListBodyCell : UITableViewCell
#property (nonatomic, retain) id <MGPodcastCellDelegate> podcastCellDelegate;
#property (weak, nonatomic) IBOutlet UILabel *mediaTitleLabel;
#property (weak, nonatomic) IBOutlet UILabel *durationLabel;
#property (weak, nonatomic) IBOutlet UIButton *downloadMediaButton;
#property (weak, nonatomic) IBOutlet UIButton *cancelMediaDownloadButton;
#property (weak, nonatomic) IBOutlet MGProgressDownloadView *progressDownloadView;
#property (weak, nonatomic) IBOutlet UILabel *pubDateLabel;
#property (strong, nonatomic) MGPlayState *cellPlayState;
#property (nonatomic) NSInteger podcastId;
- (IBAction) downloadButtonPressed:(UIButton *)sender;
- (IBAction) cancleMediaDownloadButonPressed:(UIButton *)sender;
#end
//MGPodcastListBodyCell.m
#implementation MGPodcastListBodyCell
#synthesize cellPlayState = _cellPlayState;
- (void)setCellPlayState:(MGPlayState *) cellPlayState {
_cellPlayState = cellPlayState;
[self playStateChanged];
}
- (void)awakeFromNib {
[self setup];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setup];
}
return self;
}
- (void)setup
{
UIView *customBackgroundView = [[UIView alloc] init];
customBackgroundView.backgroundColor = [APAppearence sharedInstance].tableCellBackgroundColorMB;
self.backgroundView = customBackgroundView;
self.mediaTitleLabel.textColor = [APAppearence sharedInstance].tableCellMainlabelTextColorMB;
self.durationLabel.textColor = [APAppearence sharedInstance].standardDarkGrayColorMB;
self.tintColor = [APAppearence sharedInstance].tableCellMainlabelTextColorMB;
[self playStateChanged];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void) playStateChanged {
self.downloadMediaButton.hidden = self.cellPlayState.playButtonHidden;
[self.downloadMediaButton setNeedsDisplay];
self.cancelMediaDownloadButton.hidden = self.cellPlayState.cancelButtonHidden;
[self.cancelMediaDownloadButton setNeedsDisplay];
self.progressDownloadView.hidden = self.cellPlayState.progressViewHidden;
[self setNeedsDisplay];
}
- (IBAction) downloadButtonPressed:(UIButton *)sender {
[self.podcastCellDelegate downloadButtonPressedOfCell: self.podcastId];
}
- (IBAction) cancleMediaDownloadButonPressed:(UIButton *)sender {
[self.podcastCellDelegate cancleDownloadButtonPressedOfCell: self.podcastId];
}
#end
So if somebody can tell me, what to do more than reload the cell to update the View I would be very grateful. Thanks.
When you reload the cell you have code as follows...
MGPodcastListBodyCell *cell = (MGPodcastListBodyCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifierBodyCell];
if (cell == nil) {
....
}
In your - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
method. Because the cell is being reused the cell won't be nil the second time around and therefore isn't being updated with any new information.
You need to do something when the cell is not nil to refresh it.
I found the bug. It wasn't a problem with the reloadRowAtIndexPath method. It was a concurrency problem. The download finish state got overwritten by the download progress thread just at the end of downloading and the state was set back to download.
So, thank you all for your help.
When a user taps a cell, I want itemURL to be set to that cells "Item URL" property. Once it does this, it should then send over the itemURL in prepareForSegue over to WebViewController, as I've attempted to do. When I have WebViewController NSLog the itemURL property however, it comes up as null. How can I make sure the value is sent over properly?
MatchCenterViewController.h:
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#import "AsyncImageView.h"
#import "SearchViewController.h"
#import "WebViewController.h"
#interface MatchCenterViewController : UIViewController <UITableViewDataSource>
#property (nonatomic) IBOutlet NSString *itemSearch;
#property (nonatomic, strong) NSArray *imageURLs;
#property (strong, nonatomic) NSString *matchingCategoryCondition;
#property (strong, nonatomic) NSString *matchingCategoryLocation;
#property (strong, nonatomic) NSNumber *matchingCategoryMaxPrice;
#property (strong, nonatomic) NSNumber *matchingCategoryMinPrice;
#property (strong, nonatomic) NSArray *matchCenterArray;
#property (strong, nonatomic) NSString *searchTerm;
#property (strong, nonatomic) NSURL *itemURL;
#end
MatchCenterViewController.m:
#import "MatchCenterViewController.h"
#import <UIKit/UIKit.h>
#interface MatchCenterViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView *matchCenter;
#end
#implementation MatchCenterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewCellStyleSubtitle];
self.matchCenter.frame = CGRectMake(0,50,320,self.view.frame.size.height-100);
_matchCenter.dataSource = self;
_matchCenter.delegate = self;
[self.view addSubview:self.matchCenter];
_matchCenterArray = [[NSArray alloc] init];
}
- (void)viewDidAppear:(BOOL)animated
{
self.matchCenterArray = [[NSArray alloc] init];
[PFCloud callFunctionInBackground:#"MatchCenter"
withParameters:#{
#"test": #"Hi",
}
block:^(NSArray *result, NSError *error) {
if (!error) {
_matchCenterArray = result;
[_matchCenter reloadData];
NSLog(#"Result: '%#'", result);
}
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return _matchCenterArray.count;
}
//the part where i setup sections and the deleting of said sections
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 21.0f;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 21)];
headerView.backgroundColor = [UIColor lightGrayColor];
_searchTerm = [[[[_matchCenterArray objectAtIndex:section] objectForKey:#"Top 3"] objectAtIndex:3]objectForKey:#"Search Term"];
UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 0, 250, 21)];
headerLabel.text = [NSString stringWithFormat:#"%#", _searchTerm];
headerLabel.font = [UIFont boldSystemFontOfSize:[UIFont systemFontSize]];
headerLabel.textColor = [UIColor whiteColor];
headerLabel.backgroundColor = [UIColor lightGrayColor];
[headerView addSubview:headerLabel];
UIButton *deleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
deleteButton.tag = section;
deleteButton.frame = CGRectMake(300, 2, 17, 17);
[deleteButton setImage:[UIImage imageNamed:#"xbutton.png"] forState:UIControlStateNormal];
[deleteButton addTarget:self action:#selector(deleteButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[headerView addSubview:deleteButton];
return headerView;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Initialize cell
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
// if no cell could be dequeued create a new one
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// No cell seperators = clean design
tableView.separatorColor = [UIColor clearColor];
// title of the item
cell.textLabel.text = _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Title"];
cell.textLabel.font = [UIFont boldSystemFontOfSize:14];
// price of the item
cell.detailTextLabel.text = [NSString stringWithFormat:#"$%#", _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Price"]];
cell.detailTextLabel.textColor = [UIColor colorWithRed:0/255.0f green:127/255.0f blue:31/255.0f alpha:1.0f];
// image of the item
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:_matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Image URL"]]];
[[cell imageView] setImage:[UIImage imageWithData:imageData]];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 65;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSURL *itemURL = _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Item URL"];
// NSLog(#"The url is: '%#'", itemURL);
[self performSegueWithIdentifier:#"WebViewSegue" sender:self];
}
- (void)deleteButtonPressed:(id)sender
{
// links button
UIButton *deleteButton = (UIButton *)sender;
// Define the sections title
NSString *sectionName = _searchTerm = [[[[_matchCenterArray objectAtIndex:deleteButton.tag] objectForKey:#"Top 3"] objectAtIndex:3]objectForKey:#"Search Term"];
// Run delete function with respective section header as parameter
[PFCloud callFunctionInBackground:#"deleteFromMatchCenter"
withParameters:
#{#"searchTerm": sectionName,}
block:^(NSDictionary *result, NSError *error) {
if (!error) {
[PFCloud callFunctionInBackground:#"MatchCenter"
withParameters:#{
#"test": #"Hi",
}
block:^(NSArray *result, NSError *error) {
if (!error) {
_matchCenterArray = result;
[_matchCenter reloadData];
NSLog(#"Result: '%#'", result);
}
}];
}
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
WebViewController *controller = (WebViewController *) segue.destinationViewController;
controller.itemURL = self.itemURL;
}
#end
WebViewController.h:
#import <UIKit/UIKit.h>
#import "MatchCenterViewController.h"
#interface WebViewController : UIViewController <UIWebViewDelegate>
#property (strong, nonatomic) NSURL *itemURL;
#property (weak, nonatomic) IBOutlet UIWebView *myWebView;
#end
WebViewController.m:
#import "WebViewController.h"
#interface WebViewController ()
#end
#implementation WebViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"The url is: '%#'", _itemURL);
// _myWebView=[[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
// _myWebView.delegate=self;
// [self.view addSubview:_myWebView];
self.myWebView.delegate = self;
//////////
NSURLRequest *request = [NSURLRequest requestWithURL:_itemURL];
//4
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//5
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil) [self.myWebView loadRequest:request];
else if (error != nil) NSLog(#"Error: %#", error);
}];
[self.myWebView setScalesPageToFit:YES];
//////
//[self.myWebView loadRequest:request];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
In your didSelectRowForIndexPath: instead of
NSURL *itemURL = _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Item URL"];
use
self.itemURL = _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Item URL"];
I know this is probably going to be one big headache, but it needs to be done. I finally got my tableview resizing cells as well as my label in such a way that all the text from each cell is displayed correctly.
NOTE: the comment & bump buttons are NOT the popdown menu I want to displayed when the cell is expanded , those 2 buttons should always be displayed , the menu i want to add would be a set of 4 buttons that would display under those buttons when the cell is expanded
But now I would like to add a pop down menu to each cell like tweetbot, shown below
But this is proving to be quite difficult due to how i am resizing the label using autolayout constraints. I came across this example of how to implement something similar to Tweetbot that seems pretty simple and practical on github here.
The problem is this example assumes all cells are the same size and that their is no dynamic sizing to the individual cells.
PublicFeedViewController.M
#import "PublicFeedViewController.h"
#import "FeedItemCell.h"
#import "AFNetworking.h"
#import "UIImageView+WebCache.h"
#import "InboxDetailViewController.h"
#import "SWRevealViewController.h"
#import "CommentsViewController.h"
#import "NSDate+TimeAgo.h"
#interface PublicFeedViewController (){
NSArray *NameLabel;
NSArray *StatusLabel;
NSMutableArray *feedArray;
}
#end
#implementation PublicFeedViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//The below code prompts the user for push notifications. If allowed, code in AppDelegate takes over and stores the token.
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
// Do any additional setup after loading the view.
self.FeedTable.dataSource=self;
self.FeedTable.delegate=self;
// Set the side bar button action. When it's tapped, it'll show up the sidebar.
_sidebarButton.target = self.revealViewController;
_sidebarButton.action = #selector(revealToggle:);
// Set the gesture
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{#"foo": #"bar"};
[UIApplication sharedApplication].networkActivityIndicatorVisible = TRUE;
[manager POST:#"http://" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
//NSLog(#"JSON: %#", responseObject);
self->feedArray = [responseObject objectForKey:#"feed"];
[self.FeedTable reloadData];
[UIApplication sharedApplication].networkActivityIndicatorVisible = FALSE;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return feedArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *CellIdentifier=#"Cell";
FeedItemCell *Cell=[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(!Cell){
Cell = [[FeedItemCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSLog(#"FEED ARRAY: %#", self->feedArray);
NSDictionary *tempDictionary= [self->feedArray objectAtIndex:indexPath.row];
// Display recipe in the table cell
NSString *thumb_img = [tempDictionary objectForKey:#"thumb_img"];
NSString *thumb_path=#"http://";
NSString *thumb_url = [thumb_path stringByAppendingString:thumb_img];
Cell.NameLabel.text=[tempDictionary objectForKey:#"first_name"];
Cell.StatusLabel.text=[tempDictionary objectForKey:#"message"];
Cell.msg_id=[tempDictionary objectForKey:#"msg_id"];
//Cell.status=[tempDictionary objectForKey:#"message"];
Cell.StatusLabel.lineBreakMode=0;
Cell.StatusLabel.numberOfLines=0;
NSString *commentCount = [tempDictionary objectForKey:#"comment_count"];
NSString *commentButtonText =[NSString stringWithFormat:#"Comments ( %# )",commentCount];
[Cell.commentButton setTitle:commentButtonText forState: UIControlStateNormal];
NSString *bumpCount = [tempDictionary objectForKey:#"bump_count"];
NSString *bumpButtonText =[NSString stringWithFormat:#"Bumps ( %# )",bumpCount];
[Cell.bumpButton setTitle:bumpButtonText forState: UIControlStateNormal];
//[Cell.StatusLabel sizeToFit];
NSString *created_string=[tempDictionary objectForKey:#"created"];
double created_double = created_string.doubleValue;
NSDate *date = [[NSDate alloc] initWithTimeIntervalSince1970:created_double];
NSString *ago = [date timeAgo];
Cell.timeLabel.text=ago;
//Cell.DefaultImg.image = [UIImage imageNamed:#"buhz_mini_logo.png"];
[Cell.DefaultImg setImageWithURL:[NSURL URLWithString:thumb_url]
placeholderImage:[UIImage imageNamed:#".png"]];
return Cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Ideally you should do lazy loading so that instead of creating a new textView each time, you just reuse the same one.
UITextView *temp = [[UITextView alloc] initWithFrame:CGRectMake(82, 26, self.FeedTable.frame.size.width, 18)]; //This initial size doesn't matter
NSDictionary *tempDictionary= [self->feedArray objectAtIndex:indexPath.row];
NSString *status = [tempDictionary objectForKey:#"message"];
temp.font =[UIFont fontWithName:#"System" size:12];
temp.text = status;
[temp isHidden];
CGFloat textViewWidth = 218;
CGRect tempFrame = CGRectMake(82,26,textViewWidth,18); //The height of this frame doesn't matter.
CGSize tvsize = [temp sizeThatFits:CGSizeMake(tempFrame.size.width, tempFrame.size.height)]; //This calculates the necessary size so that all the text fits in the necessary width.
//Add the height of the other UI elements inside your cell
return tvsize.height + 70;
}
#end
CUSTOM FeedItemCell.M
#import "FeedItemCell.h"
#import "WYPopoverController/WYPopoverController.h"
#import "WYPopoverController/WYStoryboardPopoverSegue.h"
#import "CommentsViewController.h"
#import "NSDate+TimeAgo.h"
#interface FeedItemCell() <WYPopoverControllerDelegate>
{
WYPopoverController* commentsPopoverController;
}
- (IBAction)open:(id)sender;
- (void)close:(id)sender;
#end
#implementation FeedItemCell
#synthesize commentButton;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
-(IBAction)bump:(id)sender{
[self expand];
}
- (IBAction)open:(id)sender
{
[self showpopover:sender];
}
- (void)close:(id)sender
{
[commentsPopoverController dismissPopoverAnimated:YES];
commentsPopoverController.delegate = nil;
commentsPopoverController = nil;
}
-(void)expand
{
CGRect oldFrame = [self frame];
[self setFrame:CGRectMake( oldFrame.origin.x,
oldFrame.origin.y,
oldFrame.size.width,
oldFrame.size.height * 2)];
}
-(void)contract
{
CGRect oldFrame = [self frame];
[self setFrame:CGRectMake( oldFrame.origin.x,
oldFrame.origin.y,
oldFrame.size.width,
oldFrame.size.height / 2)];
}
- (IBAction)showpopover:(id)sender
{
if (commentsPopoverController == nil)
{
UIView *btn = (UIView*)sender;
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
CommentsViewController *commentsViewController = [storyboard instantiateViewControllerWithIdentifier:#"Comments"];
commentsViewController.msg_id=_msg_id;
if ([commentsViewController respondsToSelector:#selector(setPreferredContentSize:)]) {
commentsViewController.preferredContentSize = CGSizeMake(300, 500); // iOS 7
}
else {
commentsViewController.contentSizeForViewInPopover = CGSizeMake(300, 500); // iOS < 7
}
commentsViewController.title = #"Comments";
[commentsViewController.navigationItem setRightBarButtonItem:[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(close:)]];
commentsViewController.modalInPopover = NO;
UINavigationController* contentViewController = [[UINavigationController alloc] initWithRootViewController:commentsViewController];
commentsPopoverController = [[WYPopoverController alloc] initWithContentViewController:contentViewController];
commentsPopoverController.delegate = self;
commentsPopoverController.passthroughViews = #[btn];
commentsPopoverController.popoverLayoutMargins = UIEdgeInsetsMake(10, 10, 10, 10);
commentsPopoverController.wantsDefaultContentAppearance = NO;
[commentsPopoverController presentPopoverFromRect:btn.bounds
inView:btn
permittedArrowDirections:WYPopoverArrowDirectionNone
animated:YES
options:WYPopoverAnimationOptionFadeWithScale];
}
else
{
[self close:nil];
}
}
- (BOOL)popoverControllerShouldDismissPopover:(WYPopoverController *)controller
{
return YES;
}
- (void)popoverControllerDidDismissPopover:(WYPopoverController *)controller
{
if (controller == commentsPopoverController)
{
commentsPopoverController.delegate = nil;
commentsPopoverController = nil;
}
}
#end
I'm trying to use the MWFeedParser library in my app. On my homescreen, I have a view controller named NewsViewController.
In the MWFeedParser library, the root view controller is called RootViewController. I've tried to copy all the code from the RootViewController into the NewsViewController .H + .M and in IB I've linked the tableview to "dataSource" and "delegate". But when my app starts the tableview is empty.
Here's how to code looks like:
.H:
#import <UIKit/UIKit.h>
#import "MWFeedItem.h"
#import "MWFeedParser.h"
#interface NewsViewController : UITableViewController <MWFeedParserDelegate, UITableViewDelegate, UITableViewDataSource> {
// Parsing
MWFeedParser *feedParser;
NSMutableArray *parsedItems;
// Displaying
NSArray *itemsToDisplay;
NSDateFormatter *formatter;
IBOutlet UITableView *tableView;
}
// Properties
#property (nonatomic, retain) NSArray *itemsToDisplay;
#property (nonatomic, retain) IBOutlet UITableView *tableView;
-(IBAction)goHome;
#end
.M:
#import "NSString+HTML.h"
#import "MWFeedParser.h"
#import "DetailTableViewController.h"
#implementation NewsViewController
#synthesize itemsToDisplay, tableView;
#pragma mark -
#pragma mark View lifecycle
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"News", #"News");
self.tabBarItem.image = [UIImage imageNamed:#"icon_news"]; }
return self;
}
- (void)viewDidLoad
{
label.shadowOffset = CGSizeMake(0.0f, 1.0f);
label.textColor = [UIColor colorWithRed:0xB3/249.0 green:0xB3/252.0 blue:0xB3/253.0 alpha:1];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// Date
// Setup
formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterShortStyle];
[formatter setTimeStyle:NSDateFormatterShortStyle];
parsedItems = [[NSMutableArray alloc] init];
self.itemsToDisplay = [NSArray array];
// Refresh button
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
target:self
action:#selector(refresh)];
// Parse
NSURL *feedURL = [NSURL URLWithString:#"http://www.mywebsite.com/feed/"];
feedParser = [[MWFeedParser alloc] initWithFeedURL:feedURL];
feedParser.delegate = self;
feedParser.feedParseType = ParseTypeFull; // Parse feed info and all items
feedParser.connectionType = ConnectionTypeAsynchronously;
[feedParser parse];
UIImage *someImage = [UIImage imageNamed:#"back_active1#2x.png"];
[button setBackgroundImage:someImage forState:UIControlStateHighlighted];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidAppear:(BOOL)animated {
static BOOL first = YES;
if (first) {
UIViewController *popup = [[Home1ViewController alloc] initWithNibName:#"Home1ViewController" bundle:nil];
[self presentViewController:popup animated:NO completion:nil];
first = NO;
}
}
#pragma mark -
#pragma mark Parsing
// Reset and reparse
- (void)refresh {
self.title = #"Refreshing...";
[parsedItems removeAllObjects];
[feedParser stopParsing];
[feedParser parse];
self.tableView.userInteractionEnabled = NO;
self.tableView.alpha = 0.3;
}
- (void)updateTableWithParsedItems {
self.itemsToDisplay = [parsedItems sortedArrayUsingDescriptors:
[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:#"date"
ascending:NO]]];
self.tableView.userInteractionEnabled = YES;
self.tableView.alpha = 1;
[self.tableView reloadData];
}
#pragma mark -
#pragma mark MWFeedParserDelegate
- (void)feedParserDidStart:(MWFeedParser *)parser {
NSLog(#"Started Parsing: %#", parser.url);
}
- (void)feedParser:(MWFeedParser *)parser didParseFeedInfo:(MWFeedInfo *)info {
NSLog(#"Parsed Feed Info: “%#”", info.title);
self.title = info.title;
}
- (void)feedParser:(MWFeedParser *)parser didParseFeedItem:(MWFeedItem *)item {
NSLog(#"Parsed Feed Item: “%#”", item.title);
if (item) [parsedItems addObject:item];
}
- (void)feedParserDidFinish:(MWFeedParser *)parser {
NSLog(#"Finished Parsing%#", (parser.stopped ? #" (Stopped)" : #""));
[self updateTableWithParsedItems];
}
- (void)feedParser:(MWFeedParser *)parser didFailWithError:(NSError *)error {
NSLog(#"Finished Parsing With Error: %#", error);
if (parsedItems.count == 0) {
self.title = #"Failed"; // Show failed message in title
} else {
// Failed but some items parsed, so show and inform of error
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Parsing Incomplete"
message:#"There was an error during the parsing of this feed. Not all of the feed items could parsed."
delegate:nil
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alert show];
}
[self updateTableWithParsedItems];
}
#pragma mark -
#pragma mark Table view data source
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return itemsToDisplay.count;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Configure the cell.
MWFeedItem *item = [itemsToDisplay objectAtIndex:indexPath.row];
if (item) {
// Process
NSString *itemTitle = item.title ? [item.title stringByConvertingHTMLToPlainText] : #"[No Title]";
NSString *itemSummary = item.summary ? [item.summary stringByConvertingHTMLToPlainText] : #"[No Summary]";
// Set
cell.textLabel.font = [UIFont boldSystemFontOfSize:15];
cell.textLabel.text = itemTitle;
NSMutableString *subtitle = [NSMutableString string];
if (item.date) [subtitle appendFormat:#"%#: ", [formatter stringFromDate:item.date]];
[subtitle appendString:itemSummary];
cell.detailTextLabel.text = subtitle;
}
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Show detail
DetailTableViewController *detail = [[DetailTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
detail.item = (MWFeedItem *)[itemsToDisplay objectAtIndex:indexPath.row];
[self.navigationController pushViewController:detail animated:YES];
// Deselect
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#end
Please help me fix this!
you can follow the following link to use MWFeedParser : https://0club1.blogspot.in/
If your Table view is empty, you could have got the following error:
NSURLSession/NSURLConnection HTTP load failed
To allow HTTP, we need to allow arbitrary loads in App Transport Security Settings. Select info.plist in your project, in Information property list add new list as App Transport Security Settings.
Within that add Allow Arbitary Loads and mark it as YES.
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.