TableView is reloaded when you navigate away then back - ios

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.

Related

Research Kit - Objective-C error: "Visual consent step has no visible scenes"

edit: I finally solved this, it was a combination of a caching problem and a missing line of code. I never actually added the task to the view controller, somehow I missed that step. But also I had strange errors just running the github demo project and had to reboot and sudo delete the cache directory in order to get Xcode to function as it should.
I am trying to implement Research Kit in Objective-C. There are no tutorials and very little documentation to turn to. I am getting a crash "Visual consent step has no visible scenes". I have one view controller and on that view controller I have a button that triggers the IBAction "consentTapped". I have attempted to adapt the Ray Wenderlich tutorial http://www.raywenderlich.com/104575/researchkit-tutorial-with-swift and this GitHub project: https://github.com/weberbry/ResearchKitConsentDemo
In an attempt to troubleshoot this myself I have put all the code in viewDidAppear, taking it out of encapsulated methods because I thought I made a mistake that way but there is still a problem:
Here is my code:
#import "ViewController.h"
#import <ResearchKit/ResearchKit.h>
#interface ViewController ()<ORKTaskViewControllerDelegate>
#property (strong, nonatomic) ORKConsentDocument *consentDocument;
#property (strong, nonatomic) ORKOrderedTask *orderedTask;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSString *resource = [[NSBundle mainBundle] pathForResource:#"ConsentText" ofType:#"json"];
NSData *consentData = [NSData dataWithContentsOfFile:resource];
NSDictionary *parsedConsentData = [NSJSONSerialization JSONObjectWithData:consentData options:NSJSONReadingMutableContainers error:nil];
NSArray *sectionDataParsedFromInputFile = [parsedConsentData objectForKey:#"sections"];
NSMutableArray *consentSections = [NSMutableArray new];
for (NSDictionary *sectionDictionary in sectionDataParsedFromInputFile) {
ORKConsentSectionType sectionType = [[sectionDictionary objectForKey:#"sectionType"] integerValue];
NSString *title = [sectionDictionary objectForKey:#"sectionTitle"];
NSString *summary = [sectionDictionary objectForKey:#"sectionSummary"];
NSString *detail = [sectionDictionary objectForKey:#"sectionDetail"];
ORKConsentSection *section = [[ORKConsentSection alloc] initWithType:sectionType];
section.title = title;
section.summary = summary;
section.htmlContent = detail;
ORKConsentSection *consentSection = section;
[consentSections addObject:consentSection];
}
ORKConsentSection *introSection = [[ORKConsentSection alloc] initWithType:ORKConsentSectionTypeOnlyInDocument];
introSection.title = #"Intro Language";
introSection.content = #"This will only be shown in the consent document because this sectionType is map to ORKConsentSectionTypeOnlyInDocument. A consent document can include many sections with type ORKConsentSectionTypeOnlyInDocument. In this document there is a ORKConsentSectionTypeOnlyInDocument section as an intro and one as a closing section";
[consentSections insertObject:introSection atIndex:0];
ORKConsentSection *closingSection = [[ORKConsentSection alloc] initWithType:ORKConsentSectionTypeOnlyInDocument];
closingSection.title = #"Additional Terms";
closingSection.htmlContent = #"Adding a ORKConsentSectionTypeOnlyInDocument at the end of a consent can be helpful to include any additional legal or related information.";
[consentSections addObject:closingSection];
self.consentDocument = [ORKConsentDocument new];
self.consentDocument.title = #"Demo Consent";
self.consentDocument.sections = consentSections;
ORKConsentSignature *signature = [ORKConsentSignature new];
self.consentDocument.signatures = [NSArray arrayWithObject:signature];
ORKVisualConsentStep *visualConsentStep = [[ORKVisualConsentStep alloc] initWithIdentifier:#"visualConsentStep" document:self.consentDocument];
ORKConsentReviewStep *consentReviewStep = [[ORKConsentReviewStep alloc] initWithIdentifier:#"consentReviewStep" signature:self.consentDocument.signatures.firstObject inDocument:self.consentDocument];
consentReviewStep.text = #"Review Consent!";
consentReviewStep.reasonForConsent = #"I confirm that I consent to join this study";
self.orderedTask = [[ORKOrderedTask alloc] initWithIdentifier:#"consent" steps:#[visualConsentStep, consentReviewStep]];
}
- (IBAction)consentTapped:(id)sender {
ORKTaskViewController *taskViewController = [[ORKTaskViewController alloc]initWithTask:self.orderedTask taskRunUUID:nil];
taskViewController.delegate = self;
[self presentViewController:taskViewController animated:YES completion:nil];
}
- (void)taskViewController:(ORKTaskViewController *)taskViewController
didFinishWithReason:(ORKTaskViewControllerFinishReason)reason
error:(NSError *)error {
ORKTaskResult *taskResult = [taskViewController result];
[self dismissViewControllerAnimated:YES completion:nil];
}
Due to the wording of the error I feel like there should be another view controller scene but I didn't see one on the Ray Wenderlich tutorial unless I missed it. I don't know swift at all yet so if you do and you see that I missed something please let me know.
The crash happens as you leave viewDidAppear.
Also this is my very first post here so if I have not followed community guidelines please let me know and I will modify my post at once.
edit: here is the working code. And remember, sudo delete your DerivedData folder, and reboot if you have strange errors in addition to the original error I posted. The missing line was "taskViewController.task = self.orderedTask;"
#import "ViewController.h"
#import <ResearchKit/ResearchKit.h>
#interface ViewController ()<ORKTaskViewControllerDelegate>
#property (strong, nonatomic) ORKConsentDocument *consentDocument;
#property (strong, nonatomic) ORKOrderedTask *orderedTask;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSString *resource = [[NSBundle mainBundle] pathForResource:#"ConsentText" ofType:#"json"];
NSData *consentData = [NSData dataWithContentsOfFile:resource];
NSDictionary *parsedConsentData = [NSJSONSerialization JSONObjectWithData:consentData options:NSJSONReadingMutableContainers error:nil];
NSArray *sectionDataParsedFromInputFile = [parsedConsentData objectForKey:#"sections"];
NSMutableArray *consentSections = [NSMutableArray new];
for (NSDictionary *sectionDictionary in sectionDataParsedFromInputFile) {
ORKConsentSectionType sectionType = [[sectionDictionary objectForKey:#"sectionType"] integerValue];
NSString *title = [sectionDictionary objectForKey:#"sectionTitle"];
NSString *summary = [sectionDictionary objectForKey:#"sectionSummary"];
NSString *detail = [sectionDictionary objectForKey:#"sectionDetail"];
ORKConsentSection *section = [[ORKConsentSection alloc] initWithType:sectionType];
section.title = title;
section.summary = summary;
section.htmlContent = detail;
ORKConsentSection *consentSection = section;
[consentSections addObject:consentSection];
}
ORKConsentSection *introSection = [[ORKConsentSection alloc] initWithType:ORKConsentSectionTypeOnlyInDocument];
introSection.title = #"Intro Language";
introSection.htmlContent = #"This will only be shown in the consent document because this sectionType is map to ORKConsentSectionTypeOnlyInDocument. A consent document can include many sections with type ORKConsentSectionTypeOnlyInDocument. In this document there is a ORKConsentSectionTypeOnlyInDocument section as an intro and one as a closing section";
[consentSections insertObject:introSection atIndex:0];
ORKConsentSection *closingSection = [[ORKConsentSection alloc] initWithType:ORKConsentSectionTypeOnlyInDocument];
closingSection.title = #"Additional Terms";
closingSection.htmlContent = #"Adding a ORKConsentSectionTypeOnlyInDocument at the end of a consent can be helpful to include any additional legal or related information.";
[consentSections addObject:closingSection];
NSArray *sections = consentSections;
self.consentDocument = [ORKConsentDocument new];
self.consentDocument.title = #"Demo Consent";
self.consentDocument.sections = consentSections;
ORKConsentSignature *signature = [ORKConsentSignature new];
self.consentDocument.signatures = [NSArray arrayWithObject:signature];
ORKVisualConsentStep *visualConsentStep = [[ORKVisualConsentStep alloc] initWithIdentifier:#"visualConsentStep" document:self.consentDocument];
ORKConsentReviewStep *consentReviewStep = [[ORKConsentReviewStep alloc] initWithIdentifier:#"consentReviewStep" signature:self.consentDocument.signatures.firstObject inDocument:self.consentDocument];
consentReviewStep.text = #"Review Consent!";
consentReviewStep.reasonForConsent = #"I confirm that I consent to join this study";
self.orderedTask = [[ORKOrderedTask alloc] initWithIdentifier:#"consent" steps:#[visualConsentStep, consentReviewStep]];
}
- (IBAction)consentTapped:(id)sender {
ORKTaskViewController *taskViewController = [[ORKTaskViewController alloc] init];
taskViewController.task = self.orderedTask;
taskViewController.delegate = self;
[self presentViewController:taskViewController animated:YES completion:nil];
}
- (void)taskViewController:(ORKTaskViewController *)taskViewController
didFinishWithReason:(ORKTaskViewControllerFinishReason)reason
error:(NSError *)error {
ORKTaskResult *taskResult = [taskViewController result];
[self dismissViewControllerAnimated:YES completion:nil];
}
Answer by Abbey Jackson:
Here is the working code. And remember, sudo delete your DerivedData folder, and reboot if you have strange errors in addition to the original error I posted. The missing line was taskViewController.task = self.orderedTask;
#import "ViewController.h"
#import <ResearchKit/ResearchKit.h>
#interface ViewController ()<ORKTaskViewControllerDelegate>
#property (strong, nonatomic) ORKConsentDocument *consentDocument;
#property (strong, nonatomic) ORKOrderedTask *orderedTask;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSString *resource = [[NSBundle mainBundle] pathForResource:#"ConsentText" ofType:#"json"];
NSData *consentData = [NSData dataWithContentsOfFile:resource];
NSDictionary *parsedConsentData = [NSJSONSerialization JSONObjectWithData:consentData options:NSJSONReadingMutableContainers error:nil];
NSArray *sectionDataParsedFromInputFile = [parsedConsentData objectForKey:#"sections"];
NSMutableArray *consentSections = [NSMutableArray new];
for (NSDictionary *sectionDictionary in sectionDataParsedFromInputFile) {
ORKConsentSectionType sectionType = [[sectionDictionary objectForKey:#"sectionType"] integerValue];
NSString *title = [sectionDictionary objectForKey:#"sectionTitle"];
NSString *summary = [sectionDictionary objectForKey:#"sectionSummary"];
NSString *detail = [sectionDictionary objectForKey:#"sectionDetail"];
ORKConsentSection *section = [[ORKConsentSection alloc] initWithType:sectionType];
section.title = title;
section.summary = summary;
section.htmlContent = detail;
ORKConsentSection *consentSection = section;
[consentSections addObject:consentSection];
}
ORKConsentSection *introSection = [[ORKConsentSection alloc] initWithType:ORKConsentSectionTypeOnlyInDocument];
introSection.title = #"Intro Language";
introSection.htmlContent = #"This will only be shown in the consent document because this sectionType is map to ORKConsentSectionTypeOnlyInDocument. A consent document can include many sections with type ORKConsentSectionTypeOnlyInDocument. In this document there is a ORKConsentSectionTypeOnlyInDocument section as an intro and one as a closing section";
[consentSections insertObject:introSection atIndex:0];
ORKConsentSection *closingSection = [[ORKConsentSection alloc] initWithType:ORKConsentSectionTypeOnlyInDocument];
closingSection.title = #"Additional Terms";
closingSection.htmlContent = #"Adding a ORKConsentSectionTypeOnlyInDocument at the end of a consent can be helpful to include any additional legal or related information.";
[consentSections addObject:closingSection];
NSArray *sections = consentSections;
self.consentDocument = [ORKConsentDocument new];
self.consentDocument.title = #"Demo Consent";
self.consentDocument.sections = consentSections;
ORKConsentSignature *signature = [ORKConsentSignature new];
self.consentDocument.signatures = [NSArray arrayWithObject:signature];
ORKVisualConsentStep *visualConsentStep = [[ORKVisualConsentStep alloc] initWithIdentifier:#"visualConsentStep" document:self.consentDocument];
ORKConsentReviewStep *consentReviewStep = [[ORKConsentReviewStep alloc] initWithIdentifier:#"consentReviewStep" signature:self.consentDocument.signatures.firstObject inDocument:self.consentDocument];
consentReviewStep.text = #"Review Consent!";
consentReviewStep.reasonForConsent = #"I confirm that I consent to join this study";
self.orderedTask = [[ORKOrderedTask alloc] initWithIdentifier:#"consent" steps:#[visualConsentStep, consentReviewStep]];
}
- (IBAction)consentTapped:(id)sender {
ORKTaskViewController *taskViewController = [[ORKTaskViewController alloc] init];
taskViewController.task = self.orderedTask;
taskViewController.delegate = self;
[self presentViewController:taskViewController animated:YES completion:nil];
}
- (void)taskViewController:(ORKTaskViewController *)taskViewController
didFinishWithReason:(ORKTaskViewControllerFinishReason)reason
error:(NSError *)error {
ORKTaskResult *taskResult = [taskViewController result];
[self dismissViewControllerAnimated:YES completion:nil];
}

Load photos to DetailViewController from NSMutableArray

I'm new to iOS development and am currently studying from video tutorials. I came across a problem with quite a simple app I'm experimenting on.
The problem is with loading pictures to UIImage in a split view control iPad app. The app is based on the XCode master-detail application template. I created an array with NSObject variables and two properties.
#interface Burger : NSObject
#property (nonatomic) NSString *name;
#property (nonatomic) NSString *filename;
#end
- (void)viewDidLoad {
[super viewDidLoad];
photos = [[NSMutableArray alloc]init];
Burger *pic = [[Burger alloc]init];
pic.name = #"Bacon";
pic.filename = #"burger-bacon";
[photos addObject:pic];
pic = [[Burger alloc]init];
pic.name = #"Cheese";
pic.filename = #"burger-cheddar";
[photos addObject:pic];
pic = [[Burger alloc]init];
pic.name = #"Hawaii";
pic.filename = #"burger-hawaii";
[photos addObject:pic];
pic = [[Burger alloc]init];
pic.name = #"Koko z jajkiej kurzym";
pic.filename = #"burger-jajo";
[photos addObject:pic];
pic = [[Burger alloc]init];
pic.name = #"Kozi ser";
pic.filename = #"burger-kozi";
[photos addObject:pic];
pic = [[Burger alloc]init];
pic.name = #"Łosoś";
pic.filename = #"burger-losos";
[photos addObject:pic];
pic = [[Burger alloc]init];
pic.name = #"Vege (Soczewica)";
pic.filename = #"burger-soczewica";
[photos addObject:pic];
}
I want the UImageView nested in DetailView to display a picture of the object chosen in the MasterView controller. I got it to display the pictures in the MasterView table
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Burger *current = [photos objectAtIndex:indexPath.row];
UIImage *image = [UIImage imageNamed:[current filename]];
[cell.imageView setImage:image];
self.title = self.currentPhoto.name;
cell.textLabel.text = [current name];
cell.detailTextLabel.text = [current price];
return cell;
}
and it also works when I point to a specific file name:
- (void)configureView {
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem notes];
UIImage *image = [UIImage imageNamed: #"burger-bacon"];
[self.currentImage setImage:image];
self.title = self.currentPhoto.name;
}
}
but when I try to show the picture of the chosen element like this:
- (void)configureView {
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem notes];
UIImage *image = [UIImage imageNamed: self.currentPhoto.filename];
[self.currentImage setImage:image];
self.title = self.currentPhoto.name;
}
}
I get these few lines in the debug area:
2015-06-23 12:13:25.964 SlowFoodiPad[22177:744765] CUICatalog: Invalid asset name supplied: (null)
which means the segues work and the photo files seem to be fine. There seem to be a problem with pointing to the right file to display. Any ideas how to fix it?
OK, I managed to solve the problem partially. I edited prepareForSegue method of MasterViewController:
DetailViewController *controller = (DetailViewController *)[[segue destinationViewController] topViewController];
Burger *c = photos [indexPath.row];
[controller setDetailItem:c];
//this line helped
[controller setCurrentPhoto:c];
The appropriate photos now show, although the debug area keeps showing me this message:
CUICatalog: Invalid asset name supplied: (null)
two identical lines each time I tap on an element in the MasterView table.
Right now it's unclear when "-(void)configureView" is being called. My guess, based on your issue, is that it's actually called before prepareForSegue. Or at least, the very first time, it's being called before you actually have an image. You are checking for "if (self.detailItem)" but you are not checking for if (self.currentPhoto).
So probably the first time configure view is called, there is no self.currentPhoto so it can't be set. Then when the segue is actually prepared (prepareForSegue:) you set the current photo, so then when configure view is called again, it's there.
If you paste more of your view controller code that shows what triggers the segue (probably it's just the storyboard but unclear from what you have above) and when configureView, then we'd know for sure if my guess is correct.
OK, there's some progress. Now I only get two lines of this message and only at initial start of the app:
CUICatalog: Invalid asset name supplied: (null)
I commented out few lines here. Can you see anything else that is causing problems?
- (void)setDetailItem:(id)newDetailItem {
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
}
- (void)configureView {
// Update the user interface for the detail item.
// if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem notes];
// UIImage *image = [UIImage imageNamed:self.currentPhoto.filename];
// [self.currentImage setImage:image];
}
//}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIImage *image = [UIImage imageNamed:self.currentPhoto.filename];
[self.currentImage setImage:image];
self.title = self.currentPhoto.name;
self.detailDescriptionLabel.text = [self.detailItem notes];
// [self configureView];
}
Here's the prepareForSegue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
DetailViewController *controller = (DetailViewController *)[[segue destinationViewController] topViewController];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Burger *c = photos [indexPath.row];
[controller setDetailItem:c];
//adding this line helped
[controller setCurrentPhoto:c];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
}
}

iOS nav bar with username

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);
}

Add loading view controller before tableview shows

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 !!

UITableView flashes when reloaded

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];
}];
}];

Resources