I have a table based off Sam's Teach Yourself iOS Development's FlowerViewController, that, under didSelectRowAtIndesPath it goes to a website in a new nib (I tweaked part of the passing data).
MY QUESTION: I would like to update this to, instead of going to a nib, to segue within a storyboard. I know that instead of using didSelectRow... I use prepareForSegue...but I can't figure out the details...
my I have ViewController.m with the following:
- (void)viewDidLoad {
[self movieData];
[super viewDidLoad];
self.title = #"Movies";
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [movieSections count];
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[movieData objectAtIndex:section] 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];
}
// Configure the cell.
[[cell textLabel]
setText:[[[movieData
objectAtIndex:indexPath.section]
objectAtIndex: indexPath.row]
objectForKey:#"name"]];
[[cell imageView]
setImage:[UIImage imageNamed:[[[movieData
objectAtIndex:indexPath.section]
objectAtIndex: indexPath.row]
objectForKey:#"picture"]]];
[[cell detailTextLabel]
setText:[[[movieData
objectAtIndex:indexPath.section]
objectAtIndex: indexPath.row]
objectForKey:#"detail"]];
cell.detailTextLabel.numberOfLines = 0;
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
// Override to support row selection in the table view.
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
WebViewController *webViewController =
[[WebViewController alloc] initWithNibName:
#"WebViewController" bundle:nil];
webViewController.detailURL=
[[NSURL alloc] initWithString:
[[[movieData objectAtIndex:indexPath.section] objectAtIndex:
indexPath.row] objectForKey:#"url"]];
webViewController.title=
[[[movieData objectAtIndex:indexPath.section] objectAtIndex:
indexPath.row] objectForKey:#"name"];
[self.navigationController pushViewController:
webViewController animated:YES];
}
#pragma mark -
#pragma mark Table view delegate
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)movieData {
NSMutableArray *myMovies;
movieSections=[[NSMutableArray alloc] initWithObjects:
#"Movies",nil];
myMovies=[[NSMutableArray alloc] init];
[myMovies addObject:[[NSMutableDictionary alloc]
initWithObjectsAndKeys:#"Movie1",#"name",
#"1.png",#"picture",
#"http://www.url1.com",#"url",#"Some information",#"detail",nil]];
[myMovies addObject:[[NSMutableDictionary alloc]
initWithObjectsAndKeys:#"Movie2",#"name",
#"2.png",#"picture",
#"http://www.url2.com",#"url",#"Some information 2",#"detail",nil]];
[myMovies addObject:[[NSMutableDictionary alloc]
initWithObjectsAndKeys:#"Movie3",#"name",
#"3.png",#"picture",
#"http://www.url3.com",#"url",#"Some information 3",#"detail",nil]];
[myMovies addObject:[[NSMutableDictionary alloc]
initWithObjectsAndKeys:#"Movie4",#"name",
#"4.png",#"picture",
#"http://www.url4.com",#"url",#"Some information 4",#"detail",nil]];
movieData=[[NSMutableArray alloc] initWithObjects:
myMovies,nil];
}
I attempted to comment out the didSelectRowAtIndexPath and add the following for the segue, but the cell highlights and nothing happens (thankfully it doesn't freeze/crash, but there's nothing positive)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"movieSegue"]) {
NSIndexPath *selectedRowIndex = [self.tableView indexPathForSelectedRow];
WebViewSegue *_webViewSegue = [segue destinationViewController];
_webViewSegue.detailURL =
[[NSURL alloc] initWithString:[[[movieData objectAtIndex:selectedRowIndex.section] objectAtIndex:
selectedRowIndex.row] objectForKey:#"url"]];
}
}
Then I want it to pass to WebViewSegue
WebViewSegue.h:
#interface WebViewSegue : UIViewController {
IBOutlet UIWebView *detailWebView;
NSURL *detailURL;
IBOutlet UIActivityIndicatorView *activity;
NSTimer *timer;
}
#property (nonatomic, weak) NSURL *detailURL;
#property (nonatomic, weak) UIWebView *detailWebView;
#property (nonatomic, weak) UIActivityIndicatorView *activity;
#end
WebViewSegue.m:
#synthesize detailWebView =_detailWebView;
#synthesize detailURL = _detailURL;
#synthesize activity =_activity;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
[detailWebView loadRequest:[NSURLRequest requestWithURL:detailURL]];
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0)
target:self
selector:#selector(tick)
userInfo:nil
repeats:YES];
}
-(void)tick {
if (!detailWebView.loading)
[activity stopAnimating];
else
[activity startAnimating];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(void)wevView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Cannot connect"
message:#"Please check your connection and try again"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
#end
I've answered your question in another post on the site. See my answer here.
Specifically on how to pass data from a table to the next storyboard segue, first create a property for the data in the next storyboard segue (i.e. the destination view controller). Then set that property in the prepareForSegue method of the table (the source view controller).
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// needed if you have multiple segues
if ([[segue identifier] isEqualToString:#"changeNameAndDate"])
{
[[segue destinationViewController] setDataProperty:self.tableData];
// where dataProperty is the property in the designation view controller
// and tableData is the data your are passing from the source
{
}
This is a lot of code to digest; you should try to simplify. But if I've read it correctly, your basic approach seems correct.
First, put a breakpoint on prepareForSegue:sender:, and make sure it's being called, and that the identifier is what you expect it to be.
Then put a breakpoint on viewDidLoad and make sure it's called when you think it should be.
I would pull the loadRequest: out into its own method and call it both in viewDidLoad and in setDetailURL:. It's likely that setDetailURL: is being called after viewDidLoad if all of this is in a single storyboard.
EDIT What I'm saying is that prepareForSegue:sender: is likely correct. You're problem is in the presented view controller.
- (void)reloadWebView { // Pull the loadRequest: out...
[self.detailWebView loadRequest:[NSURLRequest requestWithURL:self.detailURL]];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self reloadWebView]; // ...and call it both in viewDidLoad...
...
}
- (void)setDetailURL:(NSURL *)URL {
[URL retain];
[detailURL release];
detailURL = URL;
[self reloadWebView]; // ...and in setDetailURL:
}
Also note that there is no reason for your timer. Just turn on your progress indicator in reloadWebView and turn it off in webViewDidFinishLoad and webView:didFailLoadWithError:. Your current approach makes it impossible to deallocate this view controller because the timer retains it forever.
Related
I have few images on my server whose names are stored in the phpmysql table. The table contains two fields: id and images. I have prepared a php to fetch the images in json encoded formatted as mentioned:
jsonFetch.php
<?php
$dbhost = "localhost";
$dbname = "userauth";
$dbuser = "root";
//$DB_Pass = "root";
$dbtable = "images";
#mysql_connect($dbhost, $dbuser);
$db = mysql_select_db($dbname);
$sql = "SELECT * FROM $dbtable";
$query = mysql_query($sql);
while($row = mysql_fetch_array($query))
{
$rows[] = array(
//"id" => $row[0],
"image" => $row[1]
//"description" => $row['description']);
);
}
$json = json_encode($rows);
$callback = $_GET['images'];
echo $callback.$json ;
//print_r($json);
?>
Now, when i hit the url, i am getting following response:
[{"image":"./95462"},{"image":"./8838upload_image.jpg"}{"image":"./43185upload_image.jpg"},{"image":"/17426upload_image.jpg"}]
I am getting json array as above.
The next step is to display the above array in multithreaded manner in UITableView.
I am getting the images from url when i hardcode them but when it comes to json parse, i am a noob. I have tried every possible manner in which json can be parsed so for you reference, i am posting the .m file. :
#import "json.h"
#interface profilePhotos(Private)
- (void) initialize;
- (void) loadImage:(id)arg;
- (void) updateTableView:(id)arg;
- (void) addImagesToQueue:(NSArray *)images;
- (void) addImagesToQueue:(NSArray *)arrayImages;
- (void) addImagesToQueue:(NSArray *)arrayDataFromServer;
- (void) showcommentView;
- (void) hidecommentView;
#end
#implementation profilePhotos
#synthesize photosTable;
#synthesize addPhotos;
#synthesize deletePhotos;
#synthesize back;
#synthesize imageQueue, loadedImages, imageLoaderOpQueue, commentView;
//#synthesize photosView;
-(void)initializeWith:(int)buttonTag{
tag = buttonTag;
NSLog(#"tag = %d", tag);
}
- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if (!(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
return self;
}
[self initialize];
return self;
}
- (void) awakeFromNib
{
NSLog(#"AsyncImageLoadingViewController::awakeFromNib called");
[super awakeFromNib];
[self initialize];
}
- (void) viewDidLoad
{
NSLog(#"AsyncImageLoadingViewController::viewDidLoad called");
[super viewDidLoad];
}
- (void) viewDidAppear:(BOOL)animated
{
NSLog(#"AsyncImageLoadingViewController::viewDidAppear called");
[super viewDidAppear:animated];
NSArray *images = [NSArray arrayWithObjects:
#"http://dl.dropbox.com/u/9234555/avatars/ava01.gif",
#"http://dl.dropbox.com/u/9234555/avatars/ava02.gif",
#"http://dl.dropbox.com/u/9234555/avatars/ava03.gif",
#"http://dl.dropbox.com/u/9234555/avatars/ava04.gif",
#"http://dl.dropbox.com/u/9234555/avatars/ava05.gif", nil];
[self addImagesToQueue:images];
NSLog(#"addImagesToQueue: %#",self);
}
#pragma mark -
#pragma mark Private Methods
/*!
#method
#abstract initializes class variables
*/
- (void) initialize
{
NSLog(#"AsyncImageLoadingViewController::initialize called");
NSMutableArray *a = [[NSMutableArray alloc] init];
self.imageQueue = a;
//[a release];
a = [[NSMutableArray alloc] init];
self.loadedImages = a;
//[a release];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
self.imageLoaderOpQueue = queue;
//[queue release];
}
/*!
#method
#abstract updates tableview for the newly downloaded image and scrolls the tableview to bottom
*/
- (void) updateTableView:(id)arg
{
NSLog(#"AsyncImageLoadingViewController::updateTableView called");
if ((arg == nil) || ([arg isKindOfClass:[UIImage class]] == NO)) {
return;
}
// store the newly downloaded image
[self.loadedImages addObject:arg];
//[arg release];
// refresh tableview
[self.photosTable reloadData];
// scroll to the last cell of the tableview
NSIndexPath *lastRow = [NSIndexPath indexPathForRow:([self.loadedImages count] - 1) inSection:0];
[self.photosTable scrollToRowAtIndexPath:lastRow
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
}
/*!
#method
#abstract downloads images, this is the method that dispatches tasks in the operation q ueue
*/
- (void) loadImage:(id)arg
{
NSLog(#"AsyncImageLoadingViewController::loadImage called");
if ((arg == nil) || ([arg isKindOfClass:[NSString class]] == NO)) {
return;
}
// create a local autorelease pool since this code runs not on main thread
//NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// fetch the image
NSLog(#"AsyncImageLoadingViewController::loadImage - will download image: %#", arg);
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:arg]];
UIImage *image = [UIImage imageWithData:data];
NSLog(#"image: %#",image);
// update tableview with the downloaded image on main thread
[self performSelectorOnMainThread:#selector(updateTableView:) withObject:image waitUntilDone:NO];
//[pool release];
}
/*!
#method
#abstract adds images to the queue and starts the operation queue to download them
*/
- (void) addImagesToQueue:(NSArray *)images
{
NSLog(#"AsyncImageLoadingViewController::addImagesToQueue called");
[self.imageQueue addObjectsFromArray:images];
NSLog(#"addImagesToQueue Array: %#", self);
// suspend the operation queue
[self.imageLoaderOpQueue setSuspended:YES];
// add tasks to the operation queue
for (NSString *imageUrl in self.imageQueue) {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(loadImage:) object:imageUrl];
[self.imageLoaderOpQueue addOperation:op];
// [op release];
}
// clear items in the queue and resume the operation queue to start downloading images
[self.imageQueue removeAllObjects];
[self.imageLoaderOpQueue setSuspended:NO];
}
#pragma mark -
#pragma mark UITableViewDataSource Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [self.loadedImages count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CellIdentifier";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
//cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStyleGrouped reuseIdentifier:CellIdentifier] autorelease];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewStyleGrouped reuseIdentifier:[NSString stringWithFormat:#"cellID%d",indexPath.row]];
cell.accessoryType =UITableViewCellAccessoryNone;
//cell.accessoryType =UITableViewCellAccessoryDisclosureIndicator;
}
for(UIView *subviews in cell.subviews)
[subviews removeFromSuperview];
UIImageView *photo;
photo=[[UIImageView alloc] init];
[photo setImage:[self.loadedImages objectAtIndex:indexPath.row]];
[photo setFrame:CGRectMake(0, 5, 150, 120)];
[cell addSubview:photo];
return cell;
}
-(void)aMethod:(UIButton *)sender{
//[sender tag];
NSIndexPath *indexPath = [photosTable indexPathForCell: (UITableViewCell*)[[sender superview]superview]];
NSLog(#"[sender tag] is %d",[sender tag]);
if([sender tag]==indexPath.row){
textField = (UITextField*)[cell viewWithTag:[sender tag]];
textField.hidden=NO;
}
//}
}
#pragma mark -
#pragma mark UITableViewDelegate Methods
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload{
// [self setPhotosView:nil];
[self setPhotosTable:nil];
[self setAddPhotos:nil];
[self setDeletePhotos:nil];
[self setBack:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:( UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
I believe that something is needed to be done in viewDidAppear method but what is it i don't understand.
Kindly, help me out. I have tried every possible json method . May be i am making some errors in that but i am all the way frustrated. Please help me please.
Dude..use following reference and you will get tutorial as working demo..
images in UITableView using multithreading
See this reference if you are new in iOS. It's simple
Hope, this will help you...
i suggest you to use SDWebImage
Web Image
This library provides a category for UIImageVIew with support for remote images coming from the web.
It provides:
An UIImageView category adding web image and cache management to the Cocoa Touch framework
An asynchronous image downloader
An asynchronous memory + disk image caching with automatic cache expiration handling
A guarantee that the same URL won't be downloaded several times
A guarantee that bogus URLs won't be retried again and again
Performances!
Try it - https://github.com/rs/SDWebImage
I am a total newbie on iOS development. After numerous tries; with lots of sample codes I managed to parse a json string from my server and able to display the results in a dynamic tableview. My problem is I cannot make the cells clickable so they would pass the id and the label to another view where another json parse will be performed to display the details of the row.
Below is my code:
#import "jsonviewcontroller.h"
#import "CJSONDeserializer.h"
#import "Otel_ItemViewController.h"
#implementation jsonviewcontroller
#synthesize tableview;
#synthesize rows;
- (void)dealloc {
[rows release];
[tableview release];
[super dealloc];
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [rows 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] autorelease];
}
// Configure the cell.
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
cell.textLabel.text = [dict objectForKey:#"C_NAME"];
cell.detailTextLabel.text = [dict objectForKey:#"CAT_ID"];
return cell;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:#"http://zskript.net/categories.php"];
NSString *jsonreturn = [[NSString alloc] initWithContentsOfURL:url];
NSLog(jsonreturn); // Look at the console and you can see what the restults are
NSData *jsonData = [jsonreturn dataUsingEncoding:NSUTF32BigEndianStringEncoding];
NSError *error = nil;
// In "real" code you should surround this with try and catch
NSDictionary * dict = [[[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:&error] retain];
if (dict)
{
rows = [dict objectForKey:#"users"];
}
NSLog(#"Array: %#",rows);
[jsonreturn release];
}
// Do some customisation of our new view when a table item has been selected
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure we're referring to the correct segue
if ([[segue identifier] isEqualToString:#"ShowSelectedMovie"]) {
// Get reference to the destination view controller
Otel_ItemViewController *vc = [segue destinationViewController];
// get the selected index
NSInteger selectedIndex = [[self.tableview indexPathForSelectedRow] row];
// Pass the name and index of our film
[vc setSelectedItem:[NSString stringWithFormat:#"%#", [rows objectAtIndex:selectedIndex]]];
[vc setSelectedIndex:selectedIndex];
}
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
#end
And below is the view that will display the detail:
#import "Otel_ItemViewController.h"
#implementation Otel_ItemViewController
#synthesize selectedIndex, selectedItem;
- (void)viewDidLoad
{
[super viewDidLoad];
[outputLabel setText:selectedItem];
[outputText setText:selectedItem];
[outputImage setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%d.jpg", selectedIndex]]];
}
#end
Currently, when I click the cells in the table, Although I have set it to push to the next view, nothing happens. Would someone please advise?
Here is the updated code.
My root view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
DetailViewController *controller = [[DetailViewController alloc] init];
controller.CATNAME = [dict objectForKey:#"C_NAME"];
controller.CATNUMBER = [dict objectForKey:#"CAT_ID"];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
And here is DetailViewController.h:
#interface DetailViewController : UIViewController {
NSString *CATNAME;
NSInteger CATNUMBER;
IBOutlet UILabel *labelId;
IBOutlet UILabel *LabelName;
}
#property (nonatomic) NSInteger CATNUMBER;
#property (nonatomic, retain) NSString *CATNAME;
#end
And DetailViewController.m:
#import "DetailViewController.h"
#implementation DetailViewController
#synthesize CATNAME, CATNUMBER;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void) viewDidLoad
{
LabelName.Text = CATNAME;
labelId = CATNUMBER;
// [LabelName setText:CATNAME];
// [labelId setText:CATNUMBER];
}
You have to implement this method
EDIT: Loading a new view
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
MyNewViewController *controller = [[MyNewViewController alloc] init];
controller.C_NAME = [dict objectForKey:#"C_NAME"];
controller.CAT_ID = [dict objectForKey:#"CAT_ID"];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
In the code above I assume you are using a navigation controller (which is the easiest way to do what you want to be doing). Also I am assuming you have a class that inherits from UIViewController that you want to have displayed. I am also assuming that this class which I called MyNewViewController in my example has two members and properties called C_NAME and CAT_ID respectively.
- (void) viewDidLoad
{
labelName.Text = C_NAME;
labelId = CAT_ID
}
The above my be incorrect as I am doing it out of memory. But the principal stays the same, if you passed the variables correctly it should work, you can have a look at my blog it still needs some work done on it, but it has a nice beginners post and shows how to edit the text of the label. In the code above I am assuming your view contains two labels labelName and labelId respectfully.
In there you have access to what cell was selected and you can then define what needs to happen.
I'm fairly new to iOS development, but have yet to find a fix for this after extensive searching.
The issue is that when a table cell is clicked, it's title information (built from an array at index) is sent to a UILabel on the Detail view controller. Then after clicking back to the MasterViewController TableViewCells, and selecting a different cell, it's title is not passed to the UILabel, and the previously or first item clicked information is retained.
This may be a release/retain issue, or a table reloadData issue but no matter where I try to add these in for my objects or table it won't reset the "currentNodeTitle" on DetailViewController to be the newly clicked cell before sending it to the UILabel.
Here is my setup:
I have a table with cells created from an NSMutableArray called nodeTitles.
The NSLog output of the array data is
nodeTitles: (
Issue,
"Issue 2",
"Issue 3",
"Issue 4",
"Issue 5",
"Issue 6",
"Issue 7"
)
In MasterViewController.h:
#class DetailViewController;
#interface MasterViewController : UITableViewController {
NSMutableArray * nodeTitles;
}
#property (strong, nonatomic) DetailViewController *detailViewController;
#property (nonatomic, retain) NSMutableArray *nodeTitles;
#end
In MasterViewController.m:
From the array each cell is created with the contents of each indexed row.
nodeTitles = [[NSMutableArray alloc]initWithObjects:#"Issue", #"Issue 2",#"Issue 3", #"Issue 4", #"Issue 5", #"Issue 6", #"Issue 7", nil];
cellForRowAtIndexPath:
- (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] autorelease];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
}
//*only thing added*
cell.textLabel.text = [self.nodeTitles objectAtIndex: [indexPath row]];
//*end only thing added*
return cell;}
Then for the didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
if (!self.detailViewController) {
self.detailViewController = [[[DetailViewController alloc] initWithNibName:#"DetailViewController_iPhone" bundle:nil] autorelease];
self.detailViewController.currentNodeTitle = [nodeTitles objectAtIndex:indexPath.row];
//[contentTitleArray release];
}
[self.navigationController pushViewController:self.detailViewController animated:YES];
}
}
Everything is working at this point. I've got a list of table view cells, with the content from my array and when clicked, I've linked these to a UILabel.
My Dealloc, viewDidUnload, viewWillAppear, viewDidAppear and viewWillDissappear in MasterViewController:
- (void)dealloc
{
[self.nodeTitles release];
[_detailViewController release];
[super dealloc];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
self.nodeTitles = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.tableView reloadData];
}
My DetailViewController.h:
#interface DetailViewController : UIViewController <UISplitViewControllerDelegate> {
IBOutlet UILabel *detailDescriptionLabel;
NSMutableString *currentNodeTitle;
}
#property (strong, nonatomic) id detailItem;
#property (nonatomic, retain) IBOutlet UILabel *detailDescriptionLabel;
#property (nonatomic, retain)NSMutableString *currentNodeTitle;
#end
DetailViewController.m:
#interface DetailViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
#end
#implementation DetailViewController
#synthesize detailItem = _detailItem;
#synthesize detailDescriptionLabel = _detailDescriptionLabel;
#synthesize masterPopoverController = _masterPopoverController;
#synthesize currentNodeTitle;
- (void)dealloc
{
currentNodeTitle = nil;
[_detailItem release];
[_detailDescriptionLabel release];
[_masterPopoverController release];
[super dealloc];
}
In the viewDidLoad on DetailViewController.m, I'm setting the UILabel item with:
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = currentNodeTitle;
_detailDescriptionLabel.text = currentNodeTitle;
[self configureView];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[currentNodeTitle release];
}
After clicking on an item, and the clicking back to the master controller, how can I get a newly selected cell title to show in the UILabel on the DetailViewController?
thanks~
if (!self.detailViewController) {
self.detailViewController.currentNodeTitle = [nodeTitles objectAtIndex:indexPath.row];
}
I think this IF statement is not executed again since detailViewController is valid.
Move the currentNodeTitle assignment out of the IF block.
EDIT
You are retaining the detailViewController, so it looks like viewDidLoad is being called only once and that is the only place where you use the currentNodeTitle.
Try moving the
self.title = currentNodeTitle;
_detailDescriptionLabel.text = currentNodeTitle;
to viewWillAppear: since it will be called every time you push the detailViewController.
When you are making your new label then just reload the table. One think also you should follow that, make a label in the if(cell == nil) { [cell.contentView addSubview:label]; } then make tag of this label then after this condition review the label label = (UILabel*)[cell.contentView viewWithTag:labeltag]
may be then it will help your problem.
Greetings, people.
I'm diving into Objective-C for pretty much the first time. It's kicking my C# butt, but (heh) I think I'm finally starting to "get" some of the concepts.
I'm writing an iPhone app that connects to a web service, pulls back data in XML format, parses the data, and renders it as a UITableView. Clicking a cell in the tableview loads a detail view, and so on.
I'm running into some conceptual problems. In C#, most controls have a display label and a value. Which makes it easy to attach events (another thing I am addicted to) to a cell or entry and key off the value for further look ups.
The problem I'm having is that the UITableView, whilst built for a detail view, doesn't seem to have a value property or anything I can intelligently key off of to pass an identifier to the detail view and populate it.
Currently I am handling this by parsing my XML feed into an NSMutableArray that holds all of the data about a particular entry. I populate the UITableView based on one particular field in that array. And then I use objectAtIndex:indexPath.row to pass any identifiers or the like to the detail view.
I could proceed like this and get it working, but the whole thing feels like a big mess and like it's going to be a monster to maintain/scale out. So, with that being said...please help me! I'm going to post a lot of my code so you can see just how poorly I'm approaching this :)
The Table View controller I'm horribly overtasking:
#import "InvoicesTableViewController.h"
#import "Wrapper.h"
#import "InvoiceDetailViewController.h"
#implementation InvoicesTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
[listOfItems release];
[restAPI release];
[parameters release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - Wrapper
- (void)wrapper:(Wrapper *)wrapper didRetrieveData:(NSData *)data
{
NSData *result = [[restAPI responseAsText] dataUsingEncoding:NSUTF8StringEncoding];
if (result != nil)
{
NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:result] autorelease];
parser.delegate = self;
[parser parse];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
#pragma mark - Parser
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
if(OneInvoice == nil)
{
OneInvoice = [[NSMutableArray alloc] init];
}
if ([elementName isEqualToString:#"invoice"])
{
[OneInvoice addObject:[attributeDict valueForKey:#"uri"]];
[OneInvoice addObject:[attributeDict valueForKey:#"total"]];
[OneInvoice addObject:[attributeDict valueForKey:#"total_due"]];
[OneInvoice addObject:[attributeDict valueForKey:#"status"]];
}
if ([elementName isEqualToString:#"client"])
{
[OneInvoice addObject:[attributeDict valueForKey:#"name"]];
[listOfItems addObject:[[NSMutableArray alloc] initWithArray:OneInvoice copyItems:YES]];
[OneInvoice release];
OneInvoice = nil;
[self.tableView reloadData];
}
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
listOfItems = [[NSMutableArray alloc] init];
//Set the title
self.navigationItem.title = #"Invoices";
if(restAPI == nil) {
restAPI = [[Wrapper alloc] init];
}
restAPI.delegate = self;
parameters = nil;
restAPI.mimeType = #"application/vnd.site+xml";
url = [NSURL URLWithString: #"https://user:pass#site.com/invoices/?status=all"];
[restAPI sendRequestTo:url usingVerb: #"GET" withParameters: parameters];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [listOfItems count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Setup the cell
NSString *cellValue = [NSString stringWithFormat:#"%# - %#", [[listOfItems objectAtIndex:indexPath.row] objectAtIndex:4],[[listOfItems objectAtIndex:indexPath.row] objectAtIndex:1]];
cell.textLabel.text = cellValue;
return cell;
}
- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath {
//return UITableViewCellAccessoryDetailDisclosureButton;
return UITableViewCellAccessoryDetailDisclosureButton;
//return UITableViewCellAccessoryDisclosureIndicator;
}
/*
// 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;
}
*/
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *invoiceURL = [[listOfItems objectAtIndex:indexPath.row] objectAtIndex:0];
InvoiceDetailViewController *dvController = [[InvoiceDetailViewController alloc] initWithNibName:#"InvoiceDetailViewController" bundle:[NSBundle mainBundle]];
dvController.invoiceURL = invoiceURL;
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
dvController = nil;
}
#end
Please do not hesitate to mock my form. I am very inexperienced with C/C++, and particularly Objective-C. I hobbled this together last night to just get something working that I can build upon.
Thanks a million,
Clifton
Cocoa uses the model-view-controller paradigm. In your case the model is the NSMutableArray, the controller the table view controller and the view your UITableView.
Table cells do not hold any data. The controller will get notified when a cell is clicked and will figure out the corresponding data (like you did) and will then call another controller, update the model or update the views.
You code looks correct. I don't see what the "mess" is you're worried about. You have a data source, the data source is displayed in the table. The the user selects a row from your table, you pass the data reference to your detail view. If you want super-data-modeling-kung-fu you can look in to Core Data, you can hook up a TableView to automatically display from a Core Data entity.
Ok, Here is the code in my AppDelegate that loads the Schedule nib.
Schedule *newestVideoController = [[Schedule alloc] initWithNibName:#"Schedule" bundle:nil];
newestVideoController.tabBarItem = [[UITabBarItem alloc] initWithTitle:#"Schedule" image:[UIImage imageNamed:#"app-icon_2_newest.png"] tag:2];
NSArray *controllers = [NSArray arrayWithObjects:navController, newestVideoController, nil];
mainController.viewControllers = controllers;
Now here is my header file for schedule.
#import <UIKit/UIKit.h>
#import "AnotherViewController.h"
#interface Schedule : UITableViewController
<UITableViewDelegate, UITableViewDataSource> {
NSArray *mySchedule;
}
#property (nonatomic,retain) NSArray *mySchedule;
#end
Lastly here again is my implementaion file for "Schedule".
#import "Schedule.h"
#import "AnotherViewController.h"
#implementation Schedule
#synthesize mySchedule;
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return mySchedule.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//create a cell
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"];
//fill it with contents
cell.textLabel.text = [mySchedule objectAtIndex:indexPath.row];
//return it
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:#"AnotherViewController" bundle:nil];
[self.navigationController pushViewController:anotherViewController animated:YES];
[anotherViewController release];
}
/*
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSString *myFile = [[NSBundle mainBundle] pathForResource:#"exercise" ofType:#"plist"];
mySchedule = [[NSArray alloc] initWithContentsOfFile:myFile];
[super viewDidLoad];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
Need more code/info. It looks like you implemented tableView:didSelectRowAtIndexPath: and pushed another View Controller onto the stack correctly, so it's hard to say. But here's a hit list:
Are you certain tableView:didSelectRowAtIndexPath: is getting called? Did you set a breakpoint? If not, you may have forgotten to set your Schedule class as the delegate either programatically or in Interface Builder.
Is schedule's navigation controller definitely set? That is, was it pushed onto a Navigation Controller stack?
Are you sure the nib file for AnotherViewController exists?