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
Related
So i am trying to make an application that displays names of people in a tableview and on tap moves to the next view controller that shows an image of the person.
However when i add the search bar on the table view; i dont seem to have it right.
What am i doing wrong here?
The code compiles and displays on the simulator but when i click on any of the buttons, it gives me the errors i hate the most (Thread 1 : signal SIGABRT)
Here is my code for the Table View Controller
#import "PhotoTableViewController.h"
#import "Photo.h"
#import "DisplayViewController.h"
#interface PhotoTableViewController ()
#end
#implementation PhotoTableViewController
#synthesize photoSearchBar, showPhotos, filteredPhotos;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
showPhotos = [NSArray arrayWithObjects:
[Photo photoofname:#"Main" filename:#"photo1.jpg" notes:#"Amazing Road Bike"],
[Photo photoofname:#"Back" filename:#"photo3.jpg" notes:#"this is the back"], nil];
[self.tableView reloadData];
self.filteredPhotos = [NSMutableArray arrayWithCapacity:[showPhotos count]];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [filteredPhotos count];
} else {
return [showPhotos count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"PCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Photo *photo = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
photo = [filteredPhotos objectAtIndex:indexPath.row];
}else
{
photo = [showPhotos objectAtIndex:indexPath.row];
}
cell.textLabel.text = photo.name;
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
}
#pragma mark Content Filtering
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[self.filteredPhotos removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#",searchText];
filteredPhotos = [NSMutableArray arrayWithArray:[showPhotos filteredArrayUsingPredicate:predicate]];
}
#pragma mark - UISearchDisplayController Delegate Methods
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
// Tells the table data source to reload when text changes
[self filterContentForSearchText:searchString scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
// Tells the table data source to reload when scope bar selection changes
[self filterContentForSearchText:self.searchDisplayController.searchBar.text scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
#pragma mark - TableView Delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Perform segue to candy detail
[self performSegueWithIdentifier:#"candyDetail" sender:tableView];
}
#pragma mark - Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"photoDetail"]) {
UIViewController *candyDetailViewController = [segue destinationViewController];
// In order to manipulate the destination view controller, another check on which table (search or normal) is displayed is needed
if(sender == self.searchDisplayController.searchResultsTableView) {
NSIndexPath *indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
NSString *destinationTitle = [[filteredPhotos objectAtIndex:[indexPath row]] name];
[candyDetailViewController setTitle:destinationTitle];
}
else {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSString *destinationTitle = [[showPhotos objectAtIndex:[indexPath row]] name];
[candyDetailViewController setTitle:destinationTitle];
}
}
}
Also this is the code for my Objective C Class called Photo
#import "Photo.h"
#implementation Photo
#synthesize name,filename,notes;
+(id) photoofname: (NSString*)name filename:(NSString*)filename notes:(NSString*)notes{
Photo *newPhoto = [[Photo alloc]init];
newPhoto.name = name;
newPhoto.filename = filename;
newPhoto.notes = notes;
return newPhoto;
}
#end
Well, just by looking at the code what I can suggest you is, first remove that call to prepareForSegue method called in UITableView's delegate method, didSelectForRowAtIndexPath.
You are overriding prepareForSegue, so in your storyboard you should have a prototype cell from where you have to ctrl-drag to the destination controller and segue it accordingly. That's a basic concept. Still having problem? Let us see your console messages when it crashes.
I created an app which will fetch info from web service.So far i got it by displaying the contents using NSLog but when i tried to load it in UITableViewCell its not displayed.Here is my code for that
#import "TableViewController.h"
#import "JSON.h"
#import "SBJsonParser.h"
#interface TableViewController ()
#end
#implementation TableViewController
#synthesize jsonurl,jsondata,jsonarray;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
}
jsonurl=[NSURL URLWithString:#"http://minorasarees.com/category.php"];
jsondata=[[NSString alloc]initWithContentsOfURL:jsonurl];
self.jsonarray=[jsondata JSONValue];
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#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 [jsonarray 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];
}
NSLog(#"1");
NSLog(#"%#",jsonarray);
cell.textLabel.text=[jsonarray objectAtIndex:indexPath.row];
NSLog(#"2");
return cell;
}
-(void)dealloc
{
[super dealloc];
[jsonarray release];
[jsondata release];
[jsonurl release];
}
#end
i've inserted tableviewcontroller by adding file with UITableViewController..will that be a problem..Help please..
Your JSON contains an array of dictionaries, so you're setting the text in your table view cell to an dictionary which cannot work since a string is expected. This actually should crash.
To solve that set your text to the category property of that dictionary:
cell.textLabel.text=[[jsonarray objectAtIndex:indexPath.row] valueForKey: #"category"];
besides this there are other things wrong with your code: [super dealloc] needs to be the last thing you call in your dealloc method. And you really should be using asynchronous networking code, blocking the main thread with networking is not acceptable.
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.
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.
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.