I have a tableView controller that loads my parsed XML data. It takes some time to do.
- (void)viewDidLoad
{
[super viewDidLoad];
CustomStringParser *customStringParser = [[CustomStringParser alloc] init];
// Set MORE logo in navigation bar
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Navigation"]];
// Download and parse XML data
RXMLElement *rxml = [RXMLElement elementFromXMLData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.morecobalt.co.uk/rss/?t=events"]]];
// Create an array to store each feed
eventsFeeds = [[NSMutableArray alloc] init];
// Loop through XML data
[rxml iterate:#"channel" usingBlock:^(RXMLElement *supportElement) {
[supportElement iterate:#"item" usingBlock:^(RXMLElement *repElement) {
// Assign element to string
NSString *title = [repElement child:#"title"].text;
NSString *subtitle = [repElement child:#"tagline"].text;
NSString *description = [repElement child:#"description"].text;
NSString *imageurl = [repElement child:#"image"].text;
NSString *startDate = [repElement child:#"start_date"].text;
NSString *endDate = [repElement child:#"end_date"].text;
// Assign element value to MoreCobalt.h propertys
currentFeed = [MoreCobaltEvents alloc];
currentFeed.title = title;
currentFeed.subtitle = subtitle;
currentFeed.imageurl = imageurl;
// DESCRIPTION FORMATTING
description = [customStringParser parseHTML:description];
description = [customStringParser parseLinesMultiple:description];
description = [customStringParser removeSocialSignifiers:description];
currentFeed.description = description;
// DATE FORMATTING
currentFeed.startDate = [customStringParser formatDate:startDate];
currentFeed.endDate = [customStringParser formatDate:endDate];
// Add a new object to the feeds array
[eventsFeeds addObject:currentFeed];
}];
}];
}
While the data is been parsed I want to add a loading screen with a activity indicator on. Im guessing this is going to be a sub view. How can I do this?
NOTE: Im using storyboards. Image below
Just drag an activity indicator component on your xib.
Declare a property in your .h file. Hook it up.
In your .m file in viewDidLoad call.
[self.activityIndicator starAnimating];
[self.activityIndicator.hidesWhenStopped = YES;
And then after your table loads call:
[self.activityIndicator stopAnimating];
No need for subviews.
Write below code in **AppDelegate.h**
UIActivityIndicatorView *progressIndicator;
UIView *progressIndicatorView;
Write below code in **AppDelegate.m**
-(void) addProgressIndicator:(UIView *)parentView
{
[self.progressIndicatorView removeFromSuperview];
self.progressIndicatorView = [[UIView alloc] initWithFrame:CGRectMake(0,0, parentView.frame.size.width,parentView.frame.size.height)];
self.progressIndicatorView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
self.progressIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake((parentView.frame.size.width - 70) / 2,(parentView.frame.size.height - 70) / 2,70,70)];
[self.progressIndicatorView addSubview:self.progressIndicator];
[parentView addSubview:self.progressIndicatorView];
[parentView bringSubviewToFront:self.progressIndicatorView];
[self.progressIndicatorView bringSubviewToFront:self.progressIndicator];
[self.progressIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
[self.progressIndicator setHidesWhenStopped:YES];
[self.progressIndicator stopAnimating];
[self.progressIndicatorView setHidden:YES];
}
-(void) startProgressIndicator
{
[NSThread detachNewThreadSelector:#selector(showProgressIndicator) toTarget:self withObject:nil];
}
-(void) showProgressIndicator
{
[self.progressIndicatorView setHidden:NO];
[self.progressIndicator startAnimating];
}
-(void) stopProgressIndicator
{
[self.progressIndicator stopAnimating];
[self.progressIndicatorView setHidden:YES];
}
Call this methods where you want to show Progress Indicator,in viewDidLoad/ViewWillAppear .
Good Luck !!
Related
How can I use activity indicator in second view controller to let data to be complete load
first VC
- (IBAction)btnOpenHTML:(id)sender {
RecievVC *obj = [self.storyboard instantiateViewControllerWithIdentifier:#"RecievVC"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:lblName.text forKey:#"lblname"];
//........Lots of Data........(then in the end of 1st VC)........//
obj.dicData = dict;
[self.navigationController pushViewController:obj animated:true];
}
#end
the 2nd VC Code after viewdidload
- (void)viewDidLoad {
[super viewDidLoad];
[self->webView.scrollView setScrollEnabled:true];
[self->webView loadHTMLString:[self getHTMLStirng] baseURL:[NSURL URLWithString:#""]];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
-(NSString *)getHTMLStirng {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"resumeT" ofType:#"html"];
NSString *strHTML = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
if (self.dicData) {
strHTML = [strHTML stringByReplacingOccurrencesOfString:#"#NAME" withString:[_dicData valueForKey:#"lblname"]];
//...............Lots of Data......Then at the end of 2nd VC............//
}return strHTML;
}
what I main by lots of data, it's around 150 of keys and value have to be send from 1st VC to 2nd VC.
any idea?
You could try GCD:
// instantiate the activity indicator
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleGray];
activity.center = self.view.center;
// start your activity indicator
[activity startAnimating]
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// load data on background thread
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:lblName.text forKey:#"lblname"];
//........Lots of Data........(then in the end of 1st VC)........//
// update UI on main thread
dispatch_async(dispatch_get_main_queue(), ^{
// stop your activity indicator
[activity stopAnimating]
RecievVC *obj = [self.storyboard instantiateViewControllerWithIdentifier:#"RecievVC"];
obj.dicData = dict;
[self.navigationController pushViewController:obj animated:true];
});
});
How do we handle showing a http posted username to a navigation bar title? Here is a method which successfully shows a username in a jpeg represented photo taken by people in a UIScrollView*listview;
-(id)initWithIndex:(int)i andData:(NSDictionary*)data {
self = [super init];
if (self !=nil) {
//initialize
self.tag = [[data objectForKey:#"IdPhoto"] intValue];
int row = i/3;
int col = i % 3;
self.frame = CGRectMake(1.5*kPadding+col*(kThumbSide+kPadding), 1.5*kPadding+row*(kThumbSide+kPadding), kThumbSide, kThumbSide);
self.backgroundColor = [UIColor grayColor];
//add the photo caption
UILabel* caption = [[UILabel alloc] initWithFrame:CGRectMake(0, kThumbSide-16, kThumbSide, 16)];
caption.backgroundColor = [UIColor blackColor];
caption.textColor = [UIColor whiteColor];
caption.textAlignment = UITextAlignmentCenter;
caption.font = [UIFont systemFontOfSize:12];
caption.text = [NSString stringWithFormat:#"#%#",[data objectForKey:#"username"]];
[self addSubview: caption];
//add touch event
[self addTarget:delegate action:#selector(didSelectPhoto:) forControlEvents:UIControlEventTouchUpInside];
//load the image
API* api = [API sharedInstance];
int IdPhoto = [[data objectForKey:#"IdPhoto"] intValue];
NSURL* imageURL = [api urlForImageWithId:[NSNumber numberWithInt: IdPhoto] isThumb:YES];
AFImageRequestOperation* imageOperation = [AFImageRequestOperation imageRequestOperationWithRequest: [NSURLRequest requestWithURL:imageURL] success:^(UIImage *image) {
//create an image view, add it to the view
UIImageView* thumbView = [[UIImageView alloc] initWithImage: image];
thumbView.frame = CGRectMake(0,0,90,90);
thumbView.contentMode = UIViewContentModeScaleAspectFit;
[self insertSubview: thumbView belowSubview: caption];
}];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[queue addOperation:imageOperation];
}
return self;
}
Place the self.navigationItem.title = label in the same method as the -(void)initWithIndex:(int)i andData:(NSDictionary*)data method, after the label receives information.
-(void)initWithIndex:(int)i andData:(NSDictionary*)data {
//remove the "#" from before the %# that you have below
label = [NSString stringWithFormat:#"#%#",[data objectForKey:#"username"]];
self.navigationItem.title = label;
}
Depending on whether it needs to load immediately, you should call the -(void)initWithIndex:(int)i andData:(NSDictionary*)data in viewDidLoad or viewWillAppear methods.
UPDATE
Try using this:
[self.navigationItem setTitle: label];
For some reason this works better. I tried both in a test and this one worked for me over the other.
UPDATE 2
Initialise your label with a value/object.
-(void)viewDidLoad{
label = #"";//initialises the value to be a blank string enabling use
//call your method that assigns the title now.
}
Now when the method -(void)initWithIndex:(int)i andData:(NSDictionary*)data is called, the title should update as label has been initialised and will now update it's value.
UPDATE 3 - AS PER ASKERS REQUEST
At the top of .m file
#interface YourClassName ()
{
NSString * label;
}
#end
#implementation YourClassName
-(void)viewDidLoad{
label = #"";//initialises the value to be a blank string enabling use
}
-(void)initWithIndex:(int)i andData:(NSDictionary*)data {
//remove the "#" from before the %# that you have below
label = [NSString stringWithFormat:#"#%#",[data objectForKey:#"username"]];
//the label now has a new value so use it for title
self.navigationItem.title = label;
//test to see that there is a value available in log
NSLog(#"What is the name: %#", label);
}
I have a loading view that I insert over the top of a view while it is parsing data from the internet using a separate thread, in this case its on top of a UICollectionView.
For some reason that I cannot understand the loading overlay disappears on its own whist the parsing is still taking place, presenting a blank screen for a second or two. It doesn't seem to happen on UITableViews just on UIcollectionviews. Any help would be appreciated.
Activity overlay.m:
#import "ActivityOverlayController.h
#interface ActivityOverlayController ()
#end
#implementation ActivityOverlayController
-(id)initWithFrame:(CGRect)theFrame {
if (self = [super init]) {
frame = theFrame;
self.view.frame = theFrame;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#", #"ActivityOverlayController called");
container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 110, 30)];
activityLabel = [[UILabel alloc] init];
activityLabel.text = NSLocalizedString(#"Loading", #"string1");
activityLabel.textColor = [UIColor lightGrayColor];
activityLabel.font = [UIFont boldSystemFontOfSize:17];
[container addSubview:activityLabel];
activityLabel.frame = CGRectMake(0, 3, 70, 25);
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicator.color = [UIColor blackColor];
activityIndicator.hidesWhenStopped = TRUE;
[activityIndicator startAnimating];
activityIndicator.frame = CGRectMake(80, 0, 30, 30);
[container addSubview:activityIndicator];
[self.view addSubview:container];
container.center = CGPointMake(frame.size.width/2, frame.size.height/2);
self.view.backgroundColor = [UIColor colorWithRed:255 green:255 blue:255 alpha:0.7];
}
-(void)viewWillAppear:(BOOL) animated {
[super viewWillAppear:animated];
}
-(void)viewWillDisappear:(BOOL) animated {
[super viewWillDisappear:animated];
}
-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[activityIndicator stopAnimating];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
Parsing part of collectionViewController.m:
-(void)ParseCollectionView{
[self performSelectorOnMainThread:#selector(showActivityView) withObject:nil waitUntilDone:YES];
CollectionViewImages = [[NSMutableArray alloc]init];
NSData *xmlData = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:GalleryTitle]];
cvTBXML = [[TBXML alloc]initWithXMLData:xmlData];
// Obtain root element
TBXMLElement * root = cvTBXML.rootXMLElement;
TBXMLElement * channel = [TBXML childElementNamed:#"channel" parentElement:root];
if (root)
{
TBXMLElement * item = [TBXML childElementNamed:#"item" parentElement:channel];
while (item !=nil)
{
//create new instance of news object
NSObject *Imagetoparse = [[GalleryImage alloc] init];
TBXMLElement * link = [TBXML childElementNamed:#"link" parentElement:item];
NSString *linktext= [TBXML textForElement:link];
[Imagetoparse setValue:linktext forKey:#"link"];
TBXMLElement * thumbnail = [TBXML childElementNamed:#"description" parentElement:item];
NSString *Thumbnailtext= [TBXML textForElement:thumbnail];
Thumbnailtext = [Thumbnailtext substringFromIndex:21];
NSInteger *slength = Thumbnailtext.length -4;
Thumbnailtext = [Thumbnailtext substringToIndex:slength];
Thumbnailtext = [Thumbnailtext stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
Thumbnailtext = [Thumbnailtext stringByReplacingOccurrencesOfString:#"thumb" withString:#"main"];
NSLog(#"Thumnail1 : %#", Thumbnailtext);
NSData *Thumbnailimage = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: Thumbnailtext]];
[Imagetoparse setValue:Thumbnailimage forKey:#"thumbnail"];
[CollectionViewImages addObject:Imagetoparse];
item = [TBXML nextSiblingNamed:#"item" searchFromElement:item];
}
}
[self.collectionView reloadData];
NSLog(#"%#", #"Finished Parse Collection View");
[self performSelectorOnMainThread:#selector(hideActivityView) withObject:nil waitUntilDone:NO];
}
-(void)showActivityView {
if (overlayController == nil) {
overlayController = [[ActivityOverlayController alloc] initWithFrame:(self.view.superview.bounds)];
}
[self.view.superview insertSubview:overlayController.view aboveSubview:self.view];
}
-(void)hideActivityView {
[overlayController.view removeFromSuperview];
}
I've built an iOS 7 app using storyboards. Within my offersViewController I have a UIView and a UITableView. The UIView acts as a subview that displays a loading message while my feed is been parsed. Once complete the subview is removed and my parsed data is presented in my UITableView.
#interface OffersViewController ()
#end
#implementation OffersViewController
#synthesize loadingView;
MoreCobaltOffers *currentFeed;
AppDelegate *appDelegate;
- (void)viewDidAppear:(BOOL)animated
{
[self.tableView addSubview:loadingView];
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Navigation"]];
CustomStringParser *customStringParser = [[CustomStringParser alloc] init];
// Download and parse XML data
RXMLElement *rxml = [RXMLElement elementFromXMLData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.myrssfeed.com"]]];
// Create an reference to AppDelegate
appDelegate = [[UIApplication sharedApplication] delegate];
// Create an array to store each feed
appDelegate.offersFeeds = [[NSMutableArray alloc] init];
// Loop Through XML Data
[rxml iterate:#"channel" usingBlock:^(RXMLElement *supportElement) {
[supportElement iterate:#"item" usingBlock:^(RXMLElement *repElement) {
// Assign element to string
NSString *title = [repElement child:#"title"].text;
NSString *subtitle = [repElement child:#"tagline"].text;
NSString *description = [repElement child:#"description"].text;
NSString *imageurl = [repElement child:#"image"].text;
NSString *address = [repElement child:#"address"].text;
// Assign element value to MoreCobalt.h propertys
currentFeed = [MoreCobaltOffers alloc];
currentFeed.title = title;
currentFeed.imageurl = imageurl;
currentFeed.addressline = address;
// DESCRIPTION FORMATTING
description = [customStringParser parseHTML:description];
description = [customStringParser parseLinesMultiple:description];
description = [customStringParser removeSocialSignifiers:description];
description = [customStringParser appendTermsOfUse:description];
currentFeed.description = description;
// SUBTITLE FORMATTING
subtitle = [customStringParser parseHTML:subtitle];
subtitle = [customStringParser parseLinesSingle:subtitle];
subtitle = [customStringParser removeSocialSignifiers:subtitle];
currentFeed.subtitle = subtitle;
// Add a new object to the feeds array
[[appDelegate offersFeeds] addObject:currentFeed];
}];
//Remove the loading screen
[loadingView removeFromSuperview];
//Show table data, if this is not here the table is empty.
[self.tableView reloadData];
}];
}
When I run the app the loading screen appears and then the table is displayed with data. If I navigate away from this view controller to another tab and then navigate back the table will flash. Not very good user experience.
The line of code responsible is [self.tableView reloadData];. I need this in or table becomes empty. What am I doing wrong?
ViewWillAppear is called any time the view appears. In order for the table not to reload each time, move the code inside viewDidAppear.
For showing the loading view only one time move the parsing to another method like:
- (void)parseFeed {
[self.loadingIndicator startAnimating];
self.loadingIndicator.hidesWhenStopped = YES;
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Navigation"]];
CustomStringParser *customStringParser = [[CustomStringParser alloc] init];
// Download and parse XML data
RXMLElement *rxml = [RXMLElement elementFromXMLData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.morecobalt.co.uk/rss/?t=offers"]]];
// Create an reference to AppDelegate
appDelegate = [[UIApplication sharedApplication] delegate];
// Create an array to store each feed
appDelegate.offersFeeds = [[NSMutableArray alloc] init];
// Loop Through XML Data
[rxml iterate:#"channel" usingBlock:^(RXMLElement *supportElement) {
[supportElement iterate:#"item" usingBlock:^(RXMLElement *repElement) {
// Assign element to string
NSString *title = [repElement child:#"title"].text;
NSString *subtitle = [repElement child:#"tagline"].text;
NSString *description = [repElement child:#"description"].text;
NSString *imageurl = [repElement child:#"image"].text;
NSString *address = [repElement child:#"address"].text;
// Assign element value to MoreCobalt.h propertys
currentFeed = [MoreCobaltOffers alloc];
currentFeed.title = title;
currentFeed.imageurl = imageurl;
currentFeed.addressline = address;
// DESCRIPTION FORMATTING
description = [customStringParser parseHTML:description];
description = [customStringParser parseLinesMultiple:description];
description = [customStringParser removeSocialSignifiers:description];
description = [customStringParser appendTermsOfUse:description];
currentFeed.description = description;
// SUBTITLE FORMATTING
subtitle = [customStringParser parseHTML:subtitle];
subtitle = [customStringParser parseLinesSingle:subtitle];
subtitle = [customStringParser removeSocialSignifiers:subtitle];
currentFeed.subtitle = subtitle;
// Add a new object to the feeds array
[[appDelegate offersFeeds] addObject:currentFeed];
}];
[loadingView removeFromSuperview];
[self.loadingIndicator stopAnimating];
[self.tableView reloadData];
}];
[loadingView removeFromSuperview];
[self.loadingIndicator stopAnimating];
isFirstLoad = NO;
}
Declare a BOOL to check if it is first load and do this check:
-(void)viewDidAppear:(BOOL)animated {
if (isFirstLoad){
[self parseFeed];
}
[super viewDidAppear:animated];
}
Few things worth nothing:
You should extract all of that code for the parsing of your XML into another class. You should not be parsing data from XML inside of a view controller.
Right now you're running your network calls and parsing the XML data every time the view appears. What you might consider is only doing it when the view loads, thus running your code in viewDidLoad
Whenever you end up moving the code you might consider having a temporary local array and filling that with the XML return values, then using isEqualToArray to compare it to your property of values to see if its different. If it is, reload the table and set the property anew, if not, don't.
On my UIViewController I have a UIView and a UITableView. The UIView acts as a loading screen while my XML Parser (RaptureXML) is parsing table data. When its finished it shows the data in the UITableView.
However when I run the app and navigate to a different view controller and then back, the UITableView flashes due to [self.tableView reloadData]; If I take this out it does not display the data in the table. Can I move this somewhere else?
#interface OffersViewController ()
#end
#implementation OffersViewController
#synthesize loadingView;
MoreCobaltOffers *currentFeed;
AppDelegate *appDelegate;
- (void)viewDidAppear:(BOOL)animated
{
[self.tableView addSubview:loadingView];
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Navigation"]];
CustomStringParser *customStringParser = [[CustomStringParser alloc] init];
// Download and parse XML data
RXMLElement *rxml = [RXMLElement elementFromXMLData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.morecobalt.co.uk/rss/?t=offers"]]];
// Create an reference to AppDelegate
appDelegate = [[UIApplication sharedApplication] delegate];
// Create an array to store each feed
appDelegate.offersFeeds = [[NSMutableArray alloc] init];
// Loop Through XML Data
[rxml iterate:#"channel" usingBlock:^(RXMLElement *supportElement) {
[supportElement iterate:#"item" usingBlock:^(RXMLElement *repElement) {
// Assign element to string
NSString *title = [repElement child:#"title"].text;
NSString *subtitle = [repElement child:#"tagline"].text;
NSString *description = [repElement child:#"description"].text;
NSString *imageurl = [repElement child:#"image"].text;
NSString *address = [repElement child:#"address"].text;
// Assign element value to MoreCobalt.h propertys
currentFeed = [MoreCobaltOffers alloc];
currentFeed.title = title;
currentFeed.imageurl = imageurl;
currentFeed.addressline = address;
// DESCRIPTION FORMATTING
description = [customStringParser parseHTML:description];
description = [customStringParser parseLinesMultiple:description];
description = [customStringParser removeSocialSignifiers:description];
description = [customStringParser appendTermsOfUse:description];
currentFeed.description = description;
// SUBTITLE FORMATTING
subtitle = [customStringParser parseHTML:subtitle];
subtitle = [customStringParser parseLinesSingle:subtitle];
subtitle = [customStringParser removeSocialSignifiers:subtitle];
currentFeed.subtitle = subtitle;
// Add a new object to the feeds array
[[appDelegate offersFeeds] addObject:currentFeed];
}];
[loadingView removeFromSuperview];
[self.tableView reloadData];
}];
}
You are adding the view before the TableView. Just change the order in the interface builder.
Here is you view hierarchy:
View
View
GrayActivity Indicator
Label - Loading
Table View
TableViewCell
Here is how is suppose to be:
View
Table View
TableViewCell
View
GrayActivity Indicator
Label - Loading
EDIT
Please remove this line `[loadingView setHidden:YES]; update your code to the following:
[rxml iterate:#"channel" usingBlock:^(RXMLElement *supportElement) {
[supportElement iterate:#"item" usingBlock:^(RXMLElement *repElement) {
.
.
.
[[appDelegate offersFeeds] addObject:currentFeed];
}];
dispatch_sync(dispatch_get_main_queue(), ^{
[loadingView setHidden:YES];
[self.table reloadData];
});
}];
restructure your code like this and i think it should work
// Loop Through XML Data
[rxml iterate:#"channel" usingBlock:^(RXMLElement *supportElement) {
[supportElement iterate:#"item" usingBlock:^(RXMLElement *repElement) {
[loadingView setHidden:No];
...
...
...
...
...
...
[loadingView setHidden:YES];
}];
}];