I am trying to load a youtube channel's feed into a uitableview with an rss feed. I need this to display the thumbnail from the individual videos. I can only find this using the gdata client which does not work. Here is the code I have so far:
Videos.h:
#import <UIKit/UIKit.h>
#interface Videos : UITableViewController<NSXMLParserelegate>{
IBOutlet UITableView * newsTable;
UIActivityIndicatorView * activityIndicator;
CGSize cellSize;
NSXMLParser * rssParser;
NSMutableArray * stories;
// a temporary item; added to the "stories" array one at a time, and cleared for the next one
NSMutableDictionary * item;
// it parses through the document, from top to bottom...
// we collect and cache each sub-element value, and then save each item to our array.
// we use these to track each current item, until it's ready to be added to the "stories" array
NSString * currentElement;
NSMutableString * currentTitle, * currentDate, * currentSummary, * currentLink;
}
- (void)parseXMLFileAtURL:(NSString *)URL;
#end
Videos.m
#import "Videos.h"
#import "AppDelegate.h"
#implementation Videos
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.tabBarItem.image = [UIImage imageNamed:#"clapboard#2x.png"];
}
return self;
}
- (void)viewDidLoad {
// Add the following line if you want the list to be editable
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [stories count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
// Set up the cell
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
cell.textLabel.text=[[stories objectAtIndex: storyIndex] objectForKey: #"title"];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
NSString * storyLink = [[stories objectAtIndex: storyIndex] objectForKey: #"link"];
NSLog(#"%#",stories );
// clean up the link - get rid of spaces, returns, and tabs...
storyLink = [storyLink stringByReplacingOccurrencesOfString:#" " withString:#""];
storyLink = [storyLink stringByReplacingOccurrencesOfString:#"\n" withString:#""];
storyLink = [storyLink stringByReplacingOccurrencesOfString:#" " withString:#""];
// open in Safari
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:storyLink]];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([stories count] == 0) {
NSString * path = #"https://youtube.com/rss/user/TheGruenebergBrother/videos.rss";
[self parseXMLFileAtURL:path];
}
cellSize = CGSizeMake([newsTable bounds].size.width, 60);
}
- (void)viewWillDisappear:(BOOL)animated {
}
- (void)viewDidDisappear:(BOOL)animated {
}
#pragma mark - parseing_Delegate_methods
- (void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(#"found file and started parsing");
}
- (void)parseXMLFileAtURL:(NSString *)URL
{
stories = [[NSMutableArray alloc] init];
//you must then convert the path to a proper NSURL or it won't work
NSURL *xmlURL = [NSURL URLWithString:URL];
// here, for some reason you have to use NSClassFromString when trying to alloc NSXMLParser, otherwise you will get an object not found error
// this may be necessary only for the toolchain
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[rssParser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser parse];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:#"Unable to download story feed from web site (Error code %i )", [parseError code]];
NSLog(#"error parsing XML: %#", errorString);
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:#"Error loading content" message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
//NSLog(#"found this element: %#", elementName);
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
// clear out our story item caches...
item = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
currentSummary = [[NSMutableString alloc] init];
currentLink = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
//NSLog(#"ended element: %#", elementName);
if ([elementName isEqualToString:#"item"]) {
// save values to an item, then store that item into the array...
[item setObject:currentTitle forKey:#"title"];
[item setObject:currentLink forKey:#"link"];
[item setObject:currentSummary forKey:#"summary"];
[item setObject:currentDate forKey:#"date"];
[stories addObject:[item copy]];
NSLog(#"adding story: %#", currentTitle);
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
//NSLog(#"found characters: %#", string);
// save the characters for the current item...
if ([currentElement isEqualToString:#"title"]) {
[currentTitle appendString:string];
} else if ([currentElement isEqualToString:#"link"]) {
[currentLink appendString:string];
} else if ([currentElement isEqualToString:#"description"]) {
[currentSummary appendString:string];
} else if ([currentElement isEqualToString:#"pubDate"]) {
[currentDate appendString:string];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
NSLog(#"all done!");
NSLog(#"stories array has %d items", [stories count]);
[newsTable reloadData];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[currentElement release];
[rssParser release];
[stories release];
[item release];
[currentTitle release];
[currentDate release];
[currentSummary release];
[currentLink release];
[super dealloc];
}
#end
Also where should the code go, and what is the code?
Thanks
there is a thumbnail url,
http://img.youtube.com/vi/VIDEOIDHERE/0.jpg
http://img.youtube.com/vi/VIDEOIDHERE/1.jpg
http://img.youtube.com/vi/VIDEOIDHERE/2.jpg
http://img.youtube.com/vi/VIDEOIDHERE/3.jpg
will fetch you the thumbnail for any video. The number represents the quality, zero being the highest. E.g:
http://img.youtube.com/vi/F2Jko4Ipdrs/0.jpg
Related
i have a huge huge problem, i feel like i will never get rid of it. i got a xml file coming from my server, parsing it well. but i got duplicates in that xml file. The thing is i need to get rid of the duplicates in my NSMutableArray, in order to know what i have in my NSMutable array and display a little menu with wich section is avaible and wich is not. But i can't manage to copy only the good stuff into another array since the NSMutableArray is as a matter of fact a Array of dictionaries. And i don't know how to manage them, i tried sevral things the past two days with no results, so please if someone can help me it would very appreciated.
here is my entire code (hope it doesn't burn you eyes :3) :
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonHMAC.h>
#import "SizingDataViewController.h"
#import "LCYDataBackedTableView.h"
#implementation SizingDataViewController
- (void)viewDidLoad {
//Add the following line if you want the list to be editable
self.title = #"Last choice";
}
-(BOOL)ifStringExists:(NSString *)stringSentToCheck{
for (int i = 0; i < ([stories count]); i++) {
NSLog(#"i = %i", i);
NSMutableString *stringToCheck = (NSMutableString *)[[stories objectAtIndex: i] objectForKey: #"type"];
if ([stringToCheck isEqualToString:stringSentToCheck] == YES) {
NSLog(#"%#", #"okay its OKAY ");
return YES;
}
}
return NO;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
CGRect frame = CGRectMake(0, 0, 160, 50);
UILabel *lbl1 = [[UILabel alloc] initWithFrame:frame];
lbl1.textAlignment = NSTextAlignmentRight;
[lbl1 setFont:[UIFont fontWithName:#"Helvetica" size:12.0]];
[lbl1 setTextColor:[UIColor grayColor]];
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
lbl1.text = [[stories objectAtIndex: storyIndex] objectForKey: #"creation_date"];//Modifier pour changer le texte affiché
[cell.contentView addSubview:lbl1];
[lbl1 release];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [stories count];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
- (void)parseXMLFileAtURL:(NSString *)URL
{
stories = [[NSMutableArray alloc] init];
NSData *xmlData = [URL dataUsingEncoding:NSUTF8StringEncoding];
rssParser = [[[NSXMLParser alloc] initWithData:xmlData]autorelease];
[rssParser setDelegate:self];
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser parse];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:#"Unable to download story feed from web site (Error code %i )", [parseError code]];
NSLog(#"error parsing XML: %#", errorString);
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:#"Error loading content" message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"data"]) {
item = [[NSMutableDictionary alloc] init];
currentData_id = [[NSMutableString alloc] init];
currentSizing_id = [[NSMutableString alloc] init];
currentName = [[NSMutableString alloc] init];
currentSize = [[NSMutableString alloc] init];
currentType = [[NSMutableString alloc]init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"data"]) {
[item setObject:currentData_id forKey:#"data_id"];
[item setObject:currentSizing_id forKey:#"sizing_id"];
[item setObject:currentName forKey:#"name"];
[item setObject:currentSize forKey:#"size"];
[item setObject:currentType forKey:#"type"]
// here is where the magic happens
--> if (([self ifStringExists:currentType] == NO)){
[stories addObject:[item copy]];
}
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
NSLog(#"found characters in found characters: %#", string);
if ([currentElement isEqualToString:#"data_id"]) {
[currentData_id appendString:string];
} else if ([currentElement isEqualToString:#"sizing_id"]) {
[currentSizing_id appendString:string];
} else if ([currentElement isEqualToString:#"name"]) {
[currentName appendString:string];
} else if ([currentElement isEqualToString:#"size"]) {
[currentSize appendString:string];
}else if ([currentElement isEqualToString:#"type"]) {
[currentType appendString:string];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
NSLog(#"all done!");
NSLog(#"stories array has %d items", [stories count]);
[newsTable reloadData];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)dealloc {
[currentElement release];
[rssParser release];
[stories release];
[item release];
[currentData_id release];
[currentSize release];
[currentSizing_id release];
[currentName release];
[currentType release];
[super dealloc];
}
#end
my .h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "LCYDataBackedTableView.h"
#interface SizingDataViewController : LCYDataBackedTableView {
IBOutlet UITableView * newsTable;
UIActivityIndicatorView * activityIndicator;
CGSize cellSize;
NSXMLParser * rssParser;
NSMutableArray * stories;
NSMutableArray * newStories;
NSMutableArray *filteredStories;
NSString *l_uri;
NSString *myId;
NSString *idName;
BOOL *uploaded;
BOOL isFiltered;
NSIndexPath *indexPaath;
NSMutableDictionary * item;
NSString * currentElement;
NSMutableString * currentData_id, * currentSizing_id, * currentType, * currentName, * currentSize;
NSDictionary *data, *data1, *data2, *data3, *data4;
}
- (void)parseXMLFileAtURL:(NSString *)URL;
-(void)lastChance;
-(BOOL)ifStringExists:(NSString *)stringSentToCheck;
-(void)lastChance;
#end
Where you have the code [stories addObject:[item copy]]; in your parser delegate callback, don't just blindly add the new item - check if it exists already:
if (![stories containsObject:item]) {
[stories addObject:item copy];
}
Then you won't have duplicates at all.
You don't strictly need to copy item because you're creating a new one each time you start a new element. Also, if you don't define 'duplicate' as a full match to the entire dictionary then you will need to write your own contains type method which iterates the array and check the parts of each dictionary that you're interested in.
can someone please tell me how to add the search function to an array that contains the titles of rss news links that i have gathered using the xml parser in Xcode 4.6.2 here in the following code? All i basically want is to be able to go to the search bar in my viewcontroller whilst in the simulator or app, and be able to search through the different rss headlines which fill the tableview cell in the ns url array i have here in the code.
//
#import "SocialMasterViewController.h"
#import "SocialDetailViewController.h"
#interface SocialMasterViewController () {
NSXMLParser *parser;
NSMutableArray *feeds;
NSMutableDictionary *item;
NSMutableString *title;
NSMutableString *link;
NSString *element;
NSMutableArray *totalStrings;
NSMutableArray *filteredStrings;
BOOL isFiltered;
}
#end
#implementation SocialMasterViewController
-(void)gotosharing {
UIStoryboard *sharingStoryboard = [UIStoryboard storyboardWithName:#"Sharing" bundle:nil];
UIViewController *initialSharingVC = [sharingStoryboard instantiateInitialViewController];
initialSharingVC.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentViewController:initialSharingVC animated:YES completion:nil];
}
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.mySearchBar.delegate = self;
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
feeds = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:#"http://rssmix.com/u/3747019/rss.xml"
];
parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[parser setDelegate:self];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table View
// table view and my data source's and delegate methods......
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = [[feeds objectAtIndex:indexPath.row] objectForKey: #"title"];
return cell;
static NSString *CellIdentifier =#"Cell";
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
element = elementName;
if ([element isEqualToString:#"item"]) {
item = [[NSMutableDictionary alloc] init];
title = [[NSMutableString alloc] init];
link = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"item"]) {
[item setObject:title forKey:#"title"];
[item setObject:link forKey:#"link"];
[feeds addObject:[item copy]];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if ([element isEqualToString:#"title"]) {
[title appendString:string];
} else if ([element isEqualToString:#"link"]) {
[link appendString:string];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[self.tableView reloadData];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSString *string = [feeds[indexPath.row] objectForKey: #"link"];
[[segue destinationViewController] setUrl:string];
}
}
#end
btw this is my masterviewcontroller.m file code
Looking forward to ur response guys :)
Anytime your search bar changes you want to update the contents of the table view.
So, first you filter the url strings then update tableView by reloading the data.
1) Make the calls when search bar changes:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[self filterURLsWithSearchBar:searchText];
[self.myTableView reloadData];
}
2) filter the strings you want to show
- (void)filterURLsWithSearchBar:(NSString *)searchText
{
[filteredStrings removeAllObjects];
for (NSString *rssUrl in totalStrings)
{
NSComparisonResult result = [rssUrl compare:searchText
options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch)
range:[rssUrl rangeOfString:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch)]];
if (result == NSOrderedSame) {
[self.filteredStrings addObject:rssUrl];
}
}
}
3) reload table data (needs to change the # of rows and the datasource)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([self.mySerchBar.text isEqualToString:#""] || self.mySearchBar.text == NULL) {
return totalStrings.count;
}
else {
return filteredStrings.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"UITableViewCell"];
}
if ([self.mySearchBar.text isEqualToString:#""]|| self.mySearchBar.text == NULL)
{
cell.textLabel.text = [totalStrings objectAtIndex:[indexPath row]];
}
else {
cell.textLabel.text = [filteredStrings objectAtIndex:[indexPath row]];
}
return cell;
}
I'm pretty new iPhone development and i have a problem.
After several hours of searching and digging in the code I could understand the source of the problem (hope) but do not know how to solve it.
the problem is that the tableView cells loads before the parser is finish so the "[array count]" is nil.
When I set the 'number of rows in section' manually, the table does appear, but only after a few sec and scrolling up and down.
i have 2 classes, one for XMLParser and one for show the tableView.
According to what I read before I opened this question is that i need to reload the tavleview data but becuase i have 2 various classes i dont know how to do that.
any ideas?
Thanks in davance!
here is my XMLParser code:
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
self.titles = [[NSMutableArray alloc]init];
self.descriptions = [[NSMutableArray alloc]init];
self.links = [[NSMutableArray alloc]init];
self.pubDate = [[NSMutableArray alloc]init];
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
}
BOOL isItem = NO;
BOOL isTitle = NO;
BOOL isDesription = NO;
BOOL isImg = NO;
BOOL isPubDate = NO;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"item"]) {
isItem = YES;
}
if ([elementName isEqualToString:#"title"]) {
isTitle=YES;
self.titlesString = [[NSMutableString alloc]init];
}
if ([elementName isEqualToString:#"description"]) {
isDesription = YES;
self.descriptionString = [[NSMutableString alloc]init];;
self.data = [NSMutableData data];
}
if ([elementName isEqualToString:#"pubDate"]) {
isPubDate = YES;
self.pubDateString = [[NSMutableString alloc]init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if(isItem && isTitle){
[self.titlesString appendString:string];
}
if (isItem && isDesription) {
[self.descriptionString appendString:string];
}
if (isItem && isPubDate) {
[self.pubDateString appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock
{
if (self.data)
[self.data appendData:CDATABlock];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"item"]) {
isItem = NO;
[self.titles addObject:self.titlesString];
[self.descriptions addObject:self.descriptionString];
[self.pubDate addObject:self.pubDateString];
NSLog(#"%#,%#,%#,",self.titlesString,self.descriptionString,self.pubDate);
}
if ([elementName isEqualToString:#"title"]) {
isTitle=NO;
}
if ([elementName isEqualToString:#"description"]) {
isDesription = NO;
if ([self.data length] > 0)
{
NSString *htmlSnippet = [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding];
NSString *imageSrc = [self firstImgUrlString:htmlSnippet];
[self.links addObject:imageSrc];
}
self.data = nil;
}
if([elementName isEqualToString:#"pubDate"])
isPubDate = NO;
}
- (NSString *)firstImgUrlString:(NSString *)string
{
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(<img\\s[\\s\\S]*?src\\s*?=\\s*?['\"](.*?)['\"][\\s\\S]*?>)+?"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSTextCheckingResult *result = [regex firstMatchInString:string
options:0
range:NSMakeRange(0, [string length])];
if (result)
return [string substringWithRange:[result rangeAtIndex:2]];
return nil;
}
and here's my show tableView code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"menuButton.png"] style:UIBarButtonItemStyleBordered target:self.viewDeckController action:#selector(toggleLeftView)];
NSURL *url;
if (!self.isGetLink)
url = [NSURL URLWithString:#"http://www.ynet.co.il/Integration/StoryRss2.xml"];
else
url = [NSURL URLWithString:self.linkForParsingString];
if (!self.xmlParser) {
self.xmlParser = [[XMLparser alloc]init];
[self.xmlParser LoadXMLWithURl:url];
}
self.isGetLink = NO;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier
{
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.xmlParser.titles count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomCell";
CustomCell *cell = (CustomCell*) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.lblTitle.text = [self.xmlParser.titles objectAtIndex:indexPath.row];
cell.lblTitle.font = [UIFont boldSystemFontOfSize:[UIFont systemFontSize]];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[self.xmlParser.links objectAtIndex:indexPath.row]]];
cell.imgView.image = [UIImage imageWithData:data];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 82;
}
#pragma mark - Table view delegate
NSString *curnentDes;
NSString *currentTitle;
NSString *currentPubDate;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ReadViewController *rvc = [[ReadViewController alloc]initWithNibName:#"ReadViewController" bundle:nil];
curnentDes = [self.xmlParser.descriptions objectAtIndex:indexPath.row];
currentTitle = [self.xmlParser.titles objectAtIndex:indexPath.row];
currentPubDate = [self.xmlParser.pubDate objectAtIndex:indexPath.row];
NSDateFormatter *inputFormatter = [[NSDateFormatter alloc]init];
[inputFormatter setDateFormat:#"EEE, dd MMM yyyy HH:mm:ss Z"];
NSDate *inputDate = [inputFormatter dateFromString:currentPubDate];
NSDateFormatter *outputFormatter = [[NSDateFormatter alloc]init];
[outputFormatter setDateFormat:#"dd/MM/yyyy HH:mm"];
NSString *outputDate = [outputFormatter stringFromDate:inputDate];
rvc.description = curnentDes;
rvc.stitle = currentTitle;
rvc.pubDate = outputDate;
[self.navigationController pushViewController:rvc animated:YES];
}
Pretty simple reload the table after your parsing is done, you are good to go. The reason your data comes after scrolling is your array is updated at a later point and then cellForRowAtIndex is called and array has content so you see rows.
Note: cellForRow is called for every row (hope you know that).
Using notification:
In your parser class
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
[[NSNotificationCenter defaultCenter ]postNotificationName:#"parsingComplete" object:nil];
}
In ViewDidLoad of your table view class add this code and add the other functions anywhere in ur class:
your viewDidLoad(edited)
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(parsingComplete) name:#"parsingComplete" object:nil];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"menuButton.png"] style:UIBarButtonItemStyleBordered target:self.viewDeckController action:#selector(toggleLeftView)];
NSURL *url;
if (!self.isGetLink)
url = [NSURL URLWithString:#"http://www.ynet.co.il/Integration/StoryRss2.xml"];
else
url = [NSURL URLWithString:self.linkForParsingString];
if (!self.xmlParser) {
self.xmlParser = [[XMLparser alloc]init];
[self.xmlParser LoadXMLWithURl:url];
}
self.isGetLink = NO;
}
//Function to handle notification:
-(void)parsingComplete
{
NSLog(#"parsing results: %#",self.xmlParser.titles);
//change your tableViewName
[yourTableView reloadData];
}
//removing notification:
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Reload the table once parsing is completed. Use delegate methods to reload the table view from didEndElement method. Try some think like this.
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"Final Element"])
{
// Reload your table View using delegates
}
}
i have a big Problem on XCode 4/Objective C and my NSXMLParser:
I got a Tabbar App with a NSXMLParser on one Tab, so on the first call,
...when the Tab becomes clicked/touched, the NSXMLParser parses me Data
into a tableview. But, if the tab gets clicked/touched a second time,
the Parser/app creates a big Memory leak, and if thats not enough, it
crashes on the iphone (not simulator).
Maybe any of you have any ideas. I heard about a workaround but i have
no idea how to realize that. Any sample code etc. is welcome.
I hope you can help me.
thanks
XMLController.h
#import "XMLController.h"
#import "Tabelle.h"
#implementation XMLController
#synthesize table;
-(id) loadXMLbyURL:(NSString *)urlString{
table = [[NSMutableArray alloc] init];
NSURL *url= [NSURL URLWithString:urlString];
NSData *xmlData = [NSData dataWithContentsOfURL:url];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
parser.delegate = self;
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
[parser release];
return self;
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)
namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"team"])
{
[currentNodeContent setString:nil];
currentTabelle = [[Tabelle alloc] init];
currentNodeContent = [[NSMutableString alloc]init];
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)
namespaceURI qualifiedName:(NSString *)qName
{
NSString *theContent = [currentNodeContent stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; //
[currentNodeContent setString:#""];
if([elementName isEqualToString:#"pos"])
{
currentTabelle.place = theContent;
}
if([elementName isEqualToString:#"name"])
{
currentTabelle.Name = theContent;
}
if([elementName isEqualToString:#"w"])
{
currentTabelle.wins = theContent;
}
if([elementName isEqualToString:#"l"])
{
currentTabelle.losses = theContent;
}
if([elementName isEqualToString:#"pct"])
{
currentTabelle.winpct = theContent;
}
if([elementName isEqualToString:#"gb"])
{
currentTabelle.GamesBehind = theContent;
}
if([elementName isEqualToString:#"team"])
{
[table addObject:currentTabelle];
[currentTabelle release];
currentTabelle = nil;
[currentNodeContent release];
currentNodeContent = nil;
}
}
-(void) parserDidEndDocument:(NSXMLParser *)nparser
{
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
//currentNodeContent = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[currentNodeContent appendString:string];
}
#end
#
And the class where i call it:
#import "TabellenController.h"
#import "Tabelle.h"
#import "Baseball_DeutschlandAppDelegate.h"
#import "CustomCell.h"
#implementation TabellenController
#synthesize xmlcont, ergebnisTabelle;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
//Abfragen nach der Liga (mit Dateiladen)
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//make a file name to write the data to using the documents directory:
NSString *fileName = [NSString stringWithFormat:#"%#/CellContent.txt",
documentsDirectory];
NSString *content = [[NSString alloc] initWithContentsOfFile:fileName
usedEncoding:nil
error:nil];
self.xmlcont = [[XMLController alloc] init];
if ([content isEqualToString:#"1. Bundesliga"]){
if(pageNumber == 0){
[self.xmlcont loadXMLbyURL:#"http://results.baseball-softball.de/extern/standing.php?l=12&xml"];
}
if(pageNumber == 1){
[self.xmlcont loadXMLbyURL:#"http://results.baseball-softball.de/extern/standing.php?l=11&xml"];
}
}if ([content isEqualToString:#"2. Bundesliga"]){
if(pageNumber == 0){
[self.xmlcont loadXMLbyURL:#"http://results.baseball-softball.de/extern/standing.php?l=22&xml"];
}
if(pageNumber == 1){
[self.xmlcont loadXMLbyURL:#"http://results.baseball-softball.de/extern/standing.php?l=21&xml"];
}
}if ([content isEqualToString:#"Regionalligen"]){
if(pageNumber == 0){
[self.xmlcont loadXMLbyURL:#"http://results.baseball-softball.de/extern/standing.php?l=31&xml"];
}
if(pageNumber == 1){
[self.xmlcont loadXMLbyURL:#"http://results.baseball-softball.de/extern/standing.php?l=32&xml"];
}
if(pageNumber == 2){
[self.xmlcont loadXMLbyURL:#"http://results.baseball-softball.de/extern/standing.php?l=33&xml"];
}
if(pageNumber == 3){
[self.xmlcont loadXMLbyURL:#"http://results.baseball-softball.de/extern/standing.php?l=34&xml"];
}
}
[content release];
[super viewDidLoad];
}
- (id)initWithPageNumber:(int)page{
pageNumber = page;
return self;
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
/*NSMutableArray *ar = [xmlcont table];
NSInteger *sections;
for (int i = 0; i < [ar count]; i++){
for (int j = (i+1); j < [ar count]; j++){
}
}*/
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
NSMutableArray *ar = [xmlcont table];
return [ar count];
}
- (NSString *)tableview:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [NSString stringWithFormat:#"# Team W L AVG GB"];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CustomCell";
CustomCell *cell = (CustomCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
for (id currentObject in topLevelObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (CustomCell *) currentObject;
break;
}
}
}
// Configure the cell...
NSMutableArray *cont = [xmlcont table];
Tabelle *current = [cont objectAtIndex:indexPath.row];
cell.place.text = [NSString stringWithFormat:#"%#.", [current place]];
cell.team.text = [NSString stringWithFormat:#"%#", [current Name]];
cell.wins.text = [NSString stringWithFormat:#"%#", [current wins]];
cell.losses.text = [NSString stringWithFormat:#"%#", [current losses]];
cell.winpct.text = [NSString stringWithFormat:#"%#", [current winpct]];
cell.gb.text = [NSString stringWithFormat:#"%#", [current GamesBehind]];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 35.0f;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
/*<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];*/
}
#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 {
NSLog(#"View verschwindet1");
}
- (void)dealloc {
[self.xmlcont release];
[super dealloc];
}
#end
I think you are missing a dealloc method in your XMLController.
Also, self.xmlcont = [[XMLController alloc] init]; might be a memory leak if xmlcont is a retain or copy property. In dealloc, you should prefer to do [xmlcont release]; without dot syntax.
There might be more problems, but these bits could make a good start.
I'm trying to learn IOS development, I've created a project from a sample RSS feed app that loads data from a XML feed file on my server. This works fine but I want it to refresh if the home button is pressed (ios multi-tasking). I've tried [table reloaddata] and put it in all the viewdidload / viewdiddisappear sections but its not working and its not stopping on the breakpoints I've put on them all.
#implementation RootViewController
- (void)viewDidLoad {
// Add the following line if you want the list to be editable
// self.navigationItem.leftBarButtonItem = self.editButtonItem;
//[newsTable reloaddata];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [stories count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
}
// Set up the cell
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
[cell setText:[[stories objectAtIndex: storyIndex] objectForKey: #"title"]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
NSString * storyLink = [[stories objectAtIndex: storyIndex] objectForKey: #"link"];
// clean up the link - get rid of spaces, returns, and tabs...
storyLink = [storyLink stringByReplacingOccurrencesOfString:#" " withString:#""];
storyLink = [storyLink stringByReplacingOccurrencesOfString:#"\n" withString:#""];
storyLink = [storyLink stringByReplacingOccurrencesOfString:#" " withString:#""];
NSLog(#"link: %#", storyLink);
// open in Safari
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:storyLink]];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[newsTable reloadData];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([stories count] == 0) {
[newsTable reloadData];
NSString * path = #"http://www.myserver.co.uk/test.xml";
[self parseXMLFileAtURL:path];
//[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(reloadData) userInfo:nil repeats:FALSE];
}
cellSize = CGSizeMake([newsTable bounds].size.width, 60);
}
-(void)viewWillDisappear:(BOOL)animated {
[newsTable reloadData];
}
- (void)viewDidDisappear:(BOOL)animated {
[newsTable reloadData];}
- (void)applicationDidBecomeActive:(UIApplication *)application {
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
[newsTable reloadData];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
/*
Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
*/
[newsTable reloadData];
}
- (void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(#"found file and started parsing");
}
- (void)parseXMLFileAtURL:(NSString *)URL
{
stories = [[NSMutableArray alloc] init];
//you must then convert the path to a proper NSURL or it won't work
NSURL *xmlURL = [NSURL URLWithString:URL];
// here, for some reason you have to use NSClassFromString when trying to alloc NSXMLParser, otherwise you will get an object not found error
// this may be necessary only for the toolchain
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[rssParser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser parse];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:#"Unable to download story feed from web site (Error code %i )", [parseError code]];
NSLog(#"error parsing XML: %#", errorString);
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:#"Error loading content" message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
//NSLog(#"found this element: %#", elementName);
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
// clear out our story item caches...
item = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
currentSummary = [[NSMutableString alloc] init];
currentLink = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
//NSLog(#"ended element: %#", elementName);
if ([elementName isEqualToString:#"item"]) {
// save values to an item, then store that item into the array...
[item setObject:currentTitle forKey:#"title"];
[item setObject:currentLink forKey:#"link"];
[item setObject:currentSummary forKey:#"summary"];
[item setObject:currentDate forKey:#"date"];
[stories addObject:[item copy]];
NSLog(#"adding story: %#", currentTitle);
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
//NSLog(#"found characters: %#", string);
// save the characters for the current item...
if ([currentElement isEqualToString:#"title"]) {
[currentTitle appendString:string];
} else if ([currentElement isEqualToString:#"link"]) {
[currentLink appendString:string];
} else if ([currentElement isEqualToString:#"description"]) {
[currentSummary appendString:string];
} else if ([currentElement isEqualToString:#"pubDate"]) {
[currentDate appendString:string];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
NSLog(#"all done!");
NSLog(#"stories array has %d items", [stories count]);
[newsTable reloadData];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[currentElement release];
[rssParser release];
[stories release];
[item release];
[currentTitle release];
[currentDate release];
[currentSummary release];
[currentLink release];
[super dealloc];
}
#end
I like to use notifications to handle this situation. Add this line of code to your 'viewDidLoad' method
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateRSSFeed)
name:UIApplicationWillEnterForegroundNotification
object:nil];
The 'updateRSSFeed' method will do something like this
- (void) updateRSSFeed
{
NSLog( #"Feed Me!!!");
// Code to restart loading the data from the RSS feed and ultimately reloading
// the table view.
}
And don't forget to remove the observer so add this to the 'dealloc' method
[[NSNotificationCenter defaultCenter] removeObserver: self];
In a complex app with multiple view controllers a view controller can be deallocated without the app quitting and a notification may be sent to a non-existent instance and you get a crash.
Notifications are very useful so dig into the documentation and other sources to figure out how to use them.
Finally, the methods 'applicationDidBecomeActive:' and 'applicationWillEnterForeground:' will never be called in the root view controller since they are part of the application delegate protocol which your appDelegate adheres to.
Hey, if you want to react to the app going into background, you should implement
- (void)applicationDidEnterBackground:(UIApplication *)application
on your appDelegate, and trigger the data reload from there. viewWillAppear doesn't get called when the app is going to background (i.e. when you "minimize" it, press home button, or switch to other app.
Cheers
I'm not really clear where you are calling [tableView reloadData] from. Based on your description, you must be calling it during the applicationWiilResignActive:, but this will most likely not run unless you have indicated to the OS that you want to download in the background.
However I believe that you have to have already started that download before this method is called, which will explain why you see neither the table reload or the breakpoints called.
It's possible I misunderstood how you are doing the call, but I think you have a fundamental design problem since you can't start that reload at the moment of deactivation.