I'm building an iOS app that parses JSON data when the first ViewController loads. In another ViewController, I have a UIPicker, which includes an array objects that pass their own corresponding urls, and a UIButton which calls the segue back to the first ViewController. However, when I call the segue, the View Controller still parses the same data from the initial nsurl, instead of the data called to replace it. How do I fix this so that when I select an object from the UIPicker and pass the data to the next controller, the first view controller parses that data accordingly?
Here is my code:
MainVC.m
-(void)viewDidLoad{
[super viewDidLoad];
_bburl = [NSURL URLWithString:#"http://www.example.com/api/sites?count=75"];
NSData *jsonData = [NSData dataWithContentsOfURL:_bburl];
NSURLSession *session = [NSURLSession sharedSession];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:_bburl];
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:_bburl completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error){
NSLog(#"%#", response);
NSData *data = [[NSData alloc]initWithContentsOfURL:location];
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
. . . . .
}
MenuVC.m
-(void)viewDidLoad{
[super viewDidLoad];
self->catArray = [[NSArray alloc] initWithObjects:#"All",#"4-Letter", #"Animal", #"Auction", #"Blog", #"Business", #"Celluar", #"Children",#"Community", #"Consulting", #"Dating", #"Design", #"Download", nil];
self->URLArray = [[NSMutableArray alloc]init];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=4-letter"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=animal"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=auction"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=blog"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=business"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=cellular"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=children"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=community"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=consulting"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=dating"];
[URLArray addObject: #"https://www.exapmle.com/api/sites/?count=75&category=design"];
[URLArray addObject: #"https://www.example.com/api/sites/?count=75&category=download"];
self->picker = catArray;
}
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
// This method returns the number of components we want in our Picker.
// The components are the colums.
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
// This returns the number of rows in each component. We use the count of our array to determine the number of rows.
return [self->catArray count];
return 0;
}
#pragma mark Picker Delegate Methods
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return [self->catArray objectAtIndex:row];
return 0;
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
selectedRow = [pickerView selectedRowInComponent:0];
NSString *catURLString = [URLArray objectAtIndex:selectedRow];
catURL = [NSURL URLWithString:catURLString];
MainViewController *mainVC = [[MainViewController alloc]init];
mainVC.bburl = catURL;
NSLog(#"%d, %#", selectedRow, catURL);
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"backFromMenuSegue"]) {
NSString *catURLString = [URLArray objectAtIndex:[self->picker selectedRowInComponent:0]];
catURL = [NSURL URLWithString:catURLString];
MainViewController *mainVC = segue.destinationViewController;
mainVC.bburl = catURL;
NSLog(#"Selected url: %#",catURL);
}
}
When you call the segue back to the first ViewController the first viewController has already loaded. So method viewDidLoad will not be called. The quickest and dirty way to solve this is move all the code in viewDidLoad to viewWillAppear
Related
I have a table on my ios app that shows last tweets, however after updating my app to 64bit architecture I'm getting four times the following error: multiple methods named 'objectAtIndex:' found with mismatched result, parameter type or attributes error.
This is the relevant part of the code:
-(void)tableView:(UITableView*)tableView didHoldRowAtIndexPath:(NSIndexPath*)indexPath {
if ([NXCatchall isiPad]) {
NSDictionary *tweet = [twitterHandler getTweetForArrayIndex:indexPath.row];
NSDictionary *urls = [[tweet objectForKey:#"entities"] objectForKey:#"urls"];
urlDict = [NSMutableDictionary dictionary];
for (int i=0; i<urls.count; i++) {
NSString *displayName = [[urls valueForKey:#"display_url"] objectAtIndex:i];
NSURL *url = [NSURL URLWithString:[[urls valueForKey:#"expanded_url"]objectAtIndex:i]];
[urlDict setValue:url forKey:displayName];
}
if (urlDict.count != 0) {
UIActionSheet *actionSheet = [[UIActionSheet alloc] init];
[actionSheet setTitle:#"Open in Safari"];
[actionSheet setDelegate:self];
for (int i=0; i<urlDict.count; i++) {
// NSString *key = [[dict allKeys] objectAtIndex:i];
//Yes it's ugly, it works at runtime. Deal with it.
[actionSheet addButtonWithTitle:[[urlDict valueForKey:(NSString*)
[[urlDict allKeys]objectAtIndex:i]]absoluteString]];
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"heldTweetWithInfo" object:self userInfo:[NSDictionary dictionaryWithObject:actionSheet forKey:#"actionSheet"]];
}
}
}
#pragma mark - Table view delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *tweet = [twitterHandler getTweetForArrayIndex:indexPath.row];
if ([NXCatchall isiPad]) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"selectedTweetNotification" object:self userInfo:tweet];
}
NSDictionary *urls = [[tweet objectForKey:#"entities"] objectForKey:#"urls"];
urlDict = [NSMutableDictionary dictionary];
for (int i=0; i<urls.count; i++) {
NSString *displayName = [[urls valueForKey:#"display_url"] objectAtIndex:i];
NSURL *url = [NSURL URLWithString:[[urls valueForKey:#"expanded_url"]objectAtIndex:i]];
[urlDict setValue:url forKey:displayName];
}
Affected lines start with NSString & NSURL
Try casting it into NSArray. My guess is that some other class declares objectAtIndex.
NSString *displayName = [(NSArray *)[urls valueForKey:#"display_url"] objectAtIndex:i];
NSURL *url = [NSURL URLWithString:[(NSArray *)[urls valueForKey:#"expanded_url"]objectAtIndex:i]];
I'm still new to using NSURL to get data and seem to have issues whenever trying to use this. In this case I use debug to check all the date coming in in ViewDidload and all the correct data comes in and is split into the arrays I then want to use to build my table view controller. However when we reach the NumberOfRows in section method, all of the arrays seem to have been reset to nil.
I've tried using various combinations of NSURL solutions but none seem to get any further than the one I am using right now (which at least shows some data arrriving). Can anyone please let me know if I am making an obvious mistake, or if not give me a reliable piece of code which I should use to perform a simple GET like this.
Thank you very much.
Here below my code:
#implementation MyLessonsTableViewController
NSArray *pastarr = nil;
NSArray *todoarr = nil;
NSArray *comingarr = nil;
NSArray *jsonless = nil;
- (void)viewDidLoad {
[super viewDidLoad];
// GET MY LESSONS FROM DATABASE
jsonless = [[NSArray alloc] init];
pastarr = [[NSArray alloc] init];
todoarr = [[NSArray alloc] init];
comingarr = [[NSArray alloc] init];
NSString *token = #"5cfd28bed3f5f5bd63143c81a50d434a";
NSString *urlString = [NSString stringWithFormat:#"http://soon.nextdoorteacher.com/apps/api/nextdoorteacher/student-lessons?t=%#", token];
NSURL *urlcc = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:urlcc];
NSError *error;
NSMutableDictionary *jsonLess = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
NSLog(#"My Lessons Json == %#", jsonLess);
// SPLIT ARRAY
NSArray *pastarr = [jsonLess valueForKeyPath:#"past"];
NSArray *todoarr = [jsonLess valueForKeyPath:#"todo"];
NSArray *comingarr = [jsonLess valueForKeyPath:#"upcoming"];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
NSUInteger lessonRowCount = 0;
switch (section) {
case 0:
lessonRowCount = todoarr.count;
break;
case 1:
lessonRowCount = comingarr.count;
break;
case 2:
lessonRowCount = pastarr.count;
break;
default:
break;
}
return lessonRowCount;
}
Several issues.
You call reloadData needlessly in dispatch_async.
You call reloadData before you process jsonLess.
You never assign anything to your array ivars.
You don't actually have ivars for your arrays. You have global variables.
Here's your posted code all fixed up:
#implementation MyLessonsTableViewController {
NSArray *pastarr = nil;
NSArray *todoarr = nil;
NSArray *comingarr = nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
// GET MY LESSONS FROM DATABASE
NSString *token = #"5cfd28bed3f5f5bd63143c81a50d434a";
NSString *urlString = [NSString stringWithFormat:#"http://soon.nextdoorteacher.com/apps/api/nextdoorteacher/student-lessons?t=%#", token];
NSURL *urlcc = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:urlcc];
NSError *error;
NSMutableDictionary *jsonLess = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions
error:&error];
NSLog(#"My Lessons Json == %#", jsonLess);
// SPLIT ARRAY
pastarr = [jsonLess valueForKeyPath:#"past"];
todoarr = [jsonLess valueForKeyPath:#"todo"];
comingarr = [jsonLess valueForKeyPath:#"upcoming"];
[self.tableView reloadData];
}
Now this still suffers from one big problem. You are doing Internet access on the main thread. That's bad. You really should do it this way:
- (void)viewDidLoad {
[super viewDidLoad];
// GET MY LESSONS FROM DATABASE
NSString *token = #"5cfd28bed3f5f5bd63143c81a50d434a";
NSString *urlString = [NSString stringWithFormat:#"http://soon.nextdoorteacher.com/apps/api/nextdoorteacher/student-lessons?t=%#", token];
NSURL *urlcc = [NSURL URLWithString:urlString];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:urlcc];
NSError *error;
NSMutableDictionary *jsonLess = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions
error:&error];
NSLog(#"My Lessons Json == %#", jsonLess);
// SPLIT ARRAY
pastarr = [jsonLess valueForKeyPath:#"past"];
todoarr = [jsonLess valueForKeyPath:#"todo"];
comingarr = [jsonLess valueForKeyPath:#"upcoming"];
// Now this must be done on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}};
}
I’m building an article reading app.I’m using AMSliderMenu(https://github.com/SocialObjects-Software/AMSlideMenu)
library for menu list.When i click on any cell in AMSlideMenu it load into another table view which contain list of articles.
I’m fetching article data in uitableview with JSON.
The issue is when i click on list of menu in AMSlideMenu it takes time to open another view.How can i resolve this problem.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
BOOL myBool = [self isNetworkAvailable];
if (myBool)
{
#try {
_Title1 = [[NSMutableArray alloc] init];
_Author1 = [[NSMutableArray alloc] init];
_Images1 = [[NSMutableArray alloc] init];
NSData* data = [NSData dataWithContentsOfURL:ysURL];
NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if(ys_avatars){
for (int j=0;j<ys_avatars.count;j++)
{
[_Title1 addObject:ys_avatars[j][#"title"]];
[_Author1 addObject: ys_avatars[j][#"author"]];
[_Images1 addObject: ys_avatars[j][#"featured_img"]];
}
}
else
{
NSLog(#"asd");
}
}
#catch (NSException *exception) {
}
[self LoadMore];
[self LoadMore];
});
});
}
}
-(void)LoadMore
{ BOOL myBool = [self isNetworkAvailable];
if (myBool)
{
#try {
// for table cell seperator line color
self.tableView.separatorColor = [UIColor colorWithRed:190/255.0 green:190/255.0 blue:190/255.0 alpha:1.0];
// for displaying the previous screen lable with back button in details view controller
UIBarButtonItem *backbutton1 = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStyleBordered target:nil action:nil];
[[self navigationItem] setBackBarButtonItem:backbutton1];
NSString *urlString=[NSString stringWithFormat:#"www.example.com&page=%d",x]; NSURL *newUrl=[NSURL URLWithString:urlString];
NSData* data = [NSData dataWithContentsOfURL:newUrl];
NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
x++;
if(ys_avatars){
for (int j=0;j<_Title1.count ;j++)
{
[_Title1 addObject:ys_avatars[j][#"title"]];
[_Author1 addObject: ys_avatars[j][#"author"]];
[_Images1 addObject: ys_avatars[j][#"featured_img"]];
} }
else
{ NSLog(#"asd"); }
[self.tableView reloadData];}
#catch (NSException *exception) {
}} }
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _Title1.count;
}
Its because you have done many many works in viewDidLoad method that's executing synchronously, and that view controller will not be shown until the code in viewDidLoad will not executed completely.
So, load your data asynchronously or do it in viewDidAppear method, but I strongly suggest you to do it asynchronously.
i am using the method to get venue from foursquare. and when i click to search bar to load the venue, it shows me the table minus one iteration, (for example if i search for "SAMSUNG" it reloads the tableview for "SAMSUN") but i can see from the nslog that that data is retrieved from four square and stored in nsarray, but it does not show in table.
- (void)getFourSquare:(NSString *)SearchString
{
NSString *CLIENT_ID = #"CLIENT_ID";
NSString *CLIENT_SECRET = #"CLIENT_SECRET";
NSString *searchString = SearchString;
NSString *requestString =[NSString stringWithFormat:#"https://api.foursquare.com/v2/venues/search?near=perth&intent=browse&radius=20000&limit=25&query=%#&client_id=%#&client_secret=%#&v=20140710",searchString,CLIENT_ID, CLIENT_SECRET];
NSURL *urlString = [NSURL URLWithString:requestString];
[NSURLConnection sendAsynchronousRequest:[[NSURLRequest alloc] initWithURL:urlString] queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
NSArray *latestFSquares = [self fetchData:data];
fsquares = [NSMutableArray arrayWithCapacity:10];
if (latestFSquares) {
for (NSDictionary *fSquareDic in latestFSquares) {
foursquare *fsquare = [[foursquare alloc] init];
fsquare.name = [fSquareDic objectForKey:#"name"];
NSLog(#"name : %# ",[fSquareDic objectForKey:#"name"]);
[fsquares addObject:fsquare];
};
}
}
[self.tableView reloadData];
}];
}
the method i am using to update the search text is below, i tried using the tableView reload in the below function and still shows me minus one iteration.
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
[self getFourSquare:searchText];
}
The problem is the line below
fsquares = [NSMutableArray arrayWithCapacity:10];
You are creating new reference of NSMutableArray everytime, thats wrong. To fix this initialize fsquares once and change the values like below
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
if(!fsquares)
fsquares = [NSMutableArray arrayWithCapacity:10];
[self getFourSquare:searchText];
}
And remove initialization inside the function getFourSquare.
Update
Move [fsquares removeAllObjects] to the following line in the below block
if (latestFSquares) {
[fsquares removeAllObjects];
}
It should fix.
Cheers.
New to iOS and am stuck on one issue in regards to adding objects in NSMutable Array and displaying the array on another view within the App. The data displays fine on other view in TableView, but when I add another item to the Array (using code below), it just replaces what was there, not adding to the array.
- (void) postArray {
tableData = [[NSMutableArray alloc] initWithCapacity:10];
tableData = [[NSMutableArray alloc] initWithObjects: nil];
[tableData addObject:favShot]; }
-(NSString *) saveFilePath {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, TRUE);
return [[path objectAtIndex:0] stringByAppendingPathComponent:#"savefile.plist"]; }
- (void) viewWillDisappear:(BOOL)animated: (UIApplication *) application {
NSArray *values = [[NSArray alloc] initWithObjects:tableData, nil];
[values writeToFile:[self saveFilePath] atomically: TRUE]; }
- (void)viewDidLoad {
tableData = [[NSMutableArray alloc] initWithObjects: nil];
NSString *myPath = [self saveFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists)
{
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
tableData = [values mutable copy];
}
UIApplication *myApp = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:myApp];
[super viewDidLoad]; }
Thank you.
Every time you call postArray you're creating an instance of NSMutableArray, then throwing it away (leaking it if you aren't using ARC). Then you're creating another instance of NSMutableArray. Then you're adding an object (favShot) to that second instance.
Next time you call postArray it's going to throw away your old array and create 2 new ones.
What you want to do is create the tableData instance when you create the controller instance, or when the view loads. Then, don't set tableData = ... as that will discard the old instance. Just add and remove objects.
edit
- (void) postArray {
[tableData addObject:favShot];
}
- (NSString *)saveFilePath {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, TRUE);
return [[path objectAtIndex:0] stringByAppendingPathComponent:#"savefile.plist"];
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[tableData writeToFile:[self saveFilePath] atomically: TRUE];
}
- (void)viewDidLoad {
NSString *myPath = [self saveFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists)
{
tableData = [[NSMutableArray alloc] initWithContentsOfFile:myPath];
} else {
tableData = [[NSMutableArray alloc] initWithObjects: nil];
}
UIApplication *myApp = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:myApp];
[super viewDidLoad];
}
Try
- (void) viewWillDisappear:(BOOL)animated: (UIApplication *) application
{
NSString *filePath = [self saveFilePath];
NSMutableArray *savedArray = [NSMutableArray arrayWithContentsOfFile:filePath];
if (!savedArray) savedArray = [NSMutableArray array];
[savedArray addObject:tableData];
[savedArray writeToFile:filePath atomically:YES];
}