Its complicated to put in words but lemme give it a try. I have a MenuViewController that has an array with category names, tapping on the category rows in tableview instantiate a different view controller using the Storyboard ID.
Now if i use different classes for each view controller, that would be a lot of redundant code and classes. What i want to do is to use one class for all these view controllers lets call it PrimaryViewController and upon selecting different categories in the MenuViewController, it calls different methods or blocks in the PrimaryViewController.
Here is the method in the PrimaryViewController:
- (void) fetchData:(NSInteger )pageNumber
{
channel = [[TheFeedStore sharedStore] fetchWebService:pageNumber withCompletion:^(RSSChannel *obj, NSError *err){
if (!err) {
int currentItemCount = [[channel items] count];
channel = obj;
int newItemCount = [[channel items] count];
int itemDelta = newItemCount - currentItemCount;
if (itemDelta > 0) {
NSMutableArray *rows = [NSMutableArray array];
for (int i = 0; i < itemDelta; i++) {
NSIndexPath *ip = [NSIndexPath indexPathForRow:i inSection:0];
[rows addObject:ip];
}
[[self tableView] insertRowsAtIndexPaths:rows withRowAnimation:UITableViewRowAnimationBottom];
}
}
}];
}
The above code has the ability to load one category. Notice the first line "channel = [[TheFeedStore sharedStore] fetchWebService", the other categories are named "fetchWebServiceCat2", "fetchWebServiceCat3" and "fetchWebServiceCat4" in another class named TheFeedStore.
What i want is when a different view controller is instantiated from the MenuViewController, it should use PrimaryViewController's fetchData method to call a different category method of TheFeedStore.
Thanks!
[store fetchWebService:webService withCompletion:completion];
is equivalent to:
[store performSelector:#selector(fetchWebService:withCompletion:)
withObject:webService
withObject:completion];
So you can do this:
SEL sel = nil;
if (...) sel = #selector(fetchWebService:withCompletion:);
if (...) sel = #selector(fetchWebServiceCat2:withCompletion:);
...
[store performSelector:sel withObject:webService withObject:^{}];
Or even this:
SEL sel = NSSelectorFromString([NSString stringWithFormat:#"fetchWebService%#:withCompletion:", #"Cat2"]);
...
Related
I have a main view controller(SecondViewController) with a UITable and a navigation controller. When a navigation bar button is pressed, a menu drops down from the navigation bar on top of the table. This menu is created by adding a view controller as a subview like so:
//SecondViewController.m
self = sortMenu.secondVC;
[self addChildViewController:sortMenu];
[self.view addSubview:sortMenu.view];
[sortMenu didMoveToParentViewController:self];
sortMenu contains a button that changes the order the cells are displayed in by calling a class method of the main view controller.
//SortMenuViewController.m
- (IBAction)sortButtonPressed:(id)sender {
[_secondVC sortButtonPressed:[sender tag]];
}
In sortButtonPressed, it calls a method to make a fetch request with updated sort filter value.
//SecondViewController.m
-(void)sortButtonPressed:(NSInteger)sortDescriptor{
_sortDescriptor = sortDescriptor;
currentPredicate = [NSPredicate predicateWithFormat:#"dataset & %d > 0", 4];
[self fetchResultsUsingSegmentedControlIndex];
}
The fetch request is performed and returns the data in a new order.
//SecondViewController.m
- (IBAction)fetchResultsUsingSegmentedControlIndex
{
NSString* sectionNameKeyPath = nil;
NSArray* sortDescriptors = nil;
NSSortDescriptor *scientificNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"scientificName" ascending:YES];
NSSortDescriptor *commonNameFirstDescriptor = [[NSSortDescriptor alloc] initWithKey:#"commonNameFirst" ascending:YES];
NSSortDescriptor *commonNameLastDescriptor = [[NSSortDescriptor alloc]
initWithKey:#"commonNameLast"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
if (_sortDescriptor == kSortByCommonNameFirst )
{
sortDescriptors = [[NSArray alloc] initWithObjects:commonNameFirstDescriptor, commonNameLastDescriptor, scientificNameDescriptor, nil];
sectionNameKeyPath = #"commonNameFirst";
}
else if (_sortDescriptor == kSortByCommonNameLast )
{
sortDescriptors = [[NSArray alloc] initWithObjects:commonNameLastDescriptor, commonNameFirstDescriptor, scientificNameDescriptor, nil];
sectionNameKeyPath = #"commonNameLast";
}
else if (_sortDescriptor == kSortByScientificName )
{
sortDescriptors = [[NSArray alloc] initWithObjects:scientificNameDescriptor, commonNameFirstDescriptor, commonNameLastDescriptor, nil];
sectionNameKeyPath = #"scientificName";
}
NSError *error;
NSLog(#"current predicate: %#", currentPredicate);
[[self fetchedResultsControllerWithsectionNameKeyPath:sectionNameKeyPath sortDescriptors:sortDescriptors predicate:currentPredicate] performFetch:&error];
[scientificNameDescriptor release];
[commonNameLastDescriptor release];
[commonNameFirstDescriptor release];
[sortDescriptors release];
NSUInteger sectionsCt = [[speciesFetchedResultsController sections] count];
int sum = 0;
for (int i=1; i < sectionsCt; i++){
id <NSFetchedResultsSectionInfo> sectionInfo = [[speciesFetchedResultsController sections] objectAtIndex:i];
NSUInteger numOfObj = [sectionInfo numberOfObjects];
NSLog(#" in section %d number of objects is %lu ", i, (unsigned long)numOfObj);
sum = sum + numOfObj;
}
[_table performSelectorOnMainThread:#selector(reloadData)
withObject:nil
waitUntilDone:NO];
}
When I call fetchResultsUsingSegmentedControlIndex from the main view controller (before dropping down the sort menu), it works correctly. However, when called from sortMenu, numberOfRowsInSection, numberOfSectionsInTableView, and cellForRowAtIndexPath are not called. I have tried to call reloadData on the main thread with performSelectorOnMainThread and also dispatching it to the main queue, but neither works.
I originally created a sort menu by adding a pickerview to the main view controller on pressing the navigation bar button, and my table reloaded correctly. Since creating a separate view controller for the menu (to have greater design control), it doesn't work.
Ended up using delegation.
// SortMenuViewController.h
#import <UIKit/UIKit.h>
#protocol SortMenuViewControllerDelegate <NSObject>
-(void)sortButtonPressed:(NSInteger)sortDescriptor;
-(void)viewButtonPressed:(NSInteger)viewDescriptor;
#end
#interface SortMenuViewController : UIViewController{
}
//SortMenuViewController.m
- (IBAction)changeSort:(id)sender {
[_delegate sortButtonPressed:[sender tag]];
}
//SecondViewController.h
#interface SecondViewController : UIViewController <NSFetchedResultsControllerDelegate, SortMenuViewControllerDelegate>{
}
-(void)sortButtonPressed:(NSInteger)sortDescriptor{
_sortDescriptor = sortDescriptor;
currentPredicate = [NSPredicate predicateWithFormat:#"dataset & %d > 0", dataset];
[self fetchResultsUsingSegmentedControlIndex];
}
When animating the deletion of all rows and hence the deletion of all sections that included all rows of a UITableView I am running into this error:
CRASH: attempt to delete row 2 from section 0, but there are only 0 sections before the update
In particular I have a singleton manager class that serves as the table view data source and delegate. I post an NSNotification to tell the table view to delete rows that should be deleted, and that NSNotification triggers the following method:
dispatch_async(dispatch_get_main_queue(), ^{
if ([[[Manager sharedManager] justDeletedIndices] count] > 0) {
[mainTableView beginUpdates];
NSMutableArray <NSIndexPath *> *pathsToDelete = [[Manager sharedManager] justDeletedIndices];
[mainTableView deleteRowsAtIndexPaths:pathsToDelete withRowAnimation:UITableViewRowAnimationFade];
[[Manager sharedManager] setJustDeletedIndices:[NSMutableArray new]];
[mainTableView endUpdates];
} else {
[mainTableView reloadData];
}
});
The code for the method is in turn triggered by a method in Manager like so:
- (void) deleteMessagesForNotificationObjects: (NSArray <Object *> *) objects {
// this is where the property that includes the NSIndexPath
[self p_updatePathsToDeleteForDeletedObjects:objects];
// this is the notification that should trigger the code above
[[NSNotificationCenter defaultCenter] postNotificationName:#"RefreshTableView" object:self];
// this is where I modify the underlying data structures that power
// the table view's data source
NSMutableArray *localObjects = [objects mutableCopy];
for (Object *obj in localObjects) {
[self deleteMessageWithToken:obj.token andUniqueID:nil andFireDate:obj.displayDate];
}
NSArray *allKeys = [self.displayDict allKeys];
NSMutableArray <NSString *> *keysToDelete = [NSMutableArray new];
for (NSString *key in allKeys) {
NSMutableArray <Object *> *currentArr = self.displayDict[key];
NSMutableArray <Object *> *objsToDelete = [NSMutableArray new];
for (int i = 0; i < [localObjects count]; i ++) {
if ([currentArr containsObject:localObjects[i]]) {
[objsToDelete addObject:localObjects[i]];
}
}
[currentArr removeObjectsInArray:objsToDelete];
[localObjects removeObjectsInArray:objsToDelete];
if ([currentArr count] < 1) {
[keysToDelete addObject:key];
}
}
[self.displayDict removeObjectsForKeys:keysToDelete];
self.keyOrder = [[[self class] orderedKeysFromDict:self.displayDict] mutableCopy];
}
I am unclear as to what has to happen in what order. How do the commands indicating to a table view that it has to delete certain rows in an animated fashion (discussed here: Add/Delete UITableViewCell with animation?) relate to the ordering of actually modifying the underlying data source? In what order do I (1) animate row deletion and section deletion and (2) actually delete those rows and sections?
The answer is that the data source has to be modified inside beginUpdates and endUpdates, not in another method or elsewhere in the code.
I have created a array in my controller. Based on that JSON array my cards[tinderswipecards/cwRichard] are created . Now I want if I remove a card I want to get the data of card Shown to display somewhere in my viewController something like this
My cards coming from DraggableBackgroundView Class and i m implementing this in my own viewController
_peopleNearBypeopleList = [NSMutableArray new];
[dataArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
{
PeopleNearByIndividualModel * model = [PeopleNearByIndividualModel new];
NSDictionary *dicData = obj;
model.senderId = [[dicData objectForKey:USER_ID] integerValue];
model.displayName = [[dicData objectForKey:DISPLAY_NAME] uppercaseString];
model.userImage = [dicData objectForKey:#"image"];
[self.peopleNearBypeopleList addObject:model];
NSLog(#"%#",_peopleNearBypeopleList);
if (idx == [dataArray count] - 1) {
[self createInvitationViews];
}
and the strings to be created like
int i;
for (i = 0; i < [_peopleNearBypeopleList count]; i++) {
PeopleNearByIndividualModel * model = [_peopleNearBypeopleList objectAtIndex:i];
NSString *nameString = #"ARE YOU SURE YOU WANT TO INVITE ";
nameString = [nameString stringByAppendingString:model.displayName];
NSLog(#"%#", nameString);
_textView.textColor = [UIColor whiteColor];
_textView.font = [UIFont fontWithName:#"Roboto-Regular" size:17.0f];
_textView.text = nameString;
}
again and it is calling the first Object.how can i acheive this
this is my draggableViewBackground Class
//%%% loads all the cards and puts the first x in the "loaded cards" array
-(void)loadCards:(NSArray*)array
{
exampleCardLabels = array;
if([exampleCardLabels count] > 0) {
NSInteger numLoadedCardsCap =(([exampleCardLabels count] > MAX_BUFFER_SIZE)?MAX_BUFFER_SIZE:[exampleCardLabels count]);
//%%% if the buffer size is greater than the data size, there will be an array error, so this makes sure that doesn't happen
//%%% loops through the exampleCardsLabels array to create a card for each label. This should be customized by removing "exampleCardLabels" with your own array of data
for (int i = 0; i<[exampleCardLabels count]; i++) {
MG_DraggableVIew* newCard = [self createDraggableViewWithDataAtIndex:i];
[allCards addObject:newCard];
if (i<numLoadedCardsCap) {
//%%% adds a small number of cards to be loaded
[loadedCards addObject:newCard];
}
}
//%%% displays the small number of loaded cards dictated by MAX_BUFFER_SIZE so that not all the cards
// are showing at once and clogging a ton of data
for (int i = 0; i<[loadedCards count]; i++) {
if (i>0) {
[self insertSubview:[loadedCards objectAtIndex:i] belowSubview: [loadedCards objectAtIndex:i-1]];
} else {
[self addSubview:[loadedCards objectAtIndex:i]];
}
cardsLoadedIndex++; //%%% we loaded a card into loaded cards, so we have to increment
}
}
// }
}
#warning include own action here!
//%%% action called when the card goes to the left.
// This should be customized with your own action
-(void)cardSwipedLeft:(UIView *)card;
{
//do whatever you want with the card that was swiped
// DraggableView *c = (DraggableView *)card;
[loadedCards removeObjectAtIndex:0]; //%%% card was swiped, so it's no longer a "loaded card"
if (cardsLoadedIndex < [allCards count]) { //%%% if we haven't reached the end of all cards, put another into the loaded cards
[loadedCards addObject:[allCards objectAtIndex:cardsLoadedIndex]];
cardsLoadedIndex++;//%%% loaded a card, so have to increment count
[self insertSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-1)] belowSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-2)]];
}
}
#warning include own action here!
//%%% action called when the card goes to the right.
// This should be customized with your own action
-(void)cardSwipedRight:(UIView *)card
{
//do whatever you want with the card that was swiped
// DraggableView *c = (DraggableView *)card;
[loadedCards removeObjectAtIndex:0]; //%%% card was swiped, so it's no longer a "loaded card"
if (cardsLoadedIndex < [allCards count]) { //%%% if we haven't reached the end of all cards, put another into the loaded cards
[loadedCards addObject:[allCards objectAtIndex:cardsLoadedIndex]];
cardsLoadedIndex++;//%%% loaded a card, so have to increment count
[self insertSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-1)] belowSubview:[loadedCards objectAtIndex:(MAX_BUFFER_SIZE-2)]];
}
}
//%%% when you hit the right button, this is called and substitutes the swipe
-(void)swipeRight
{
MG_DraggableVIew *dragView = [loadedCards firstObject];
dragView.overlayView.mode = GGOverlayViewModeRight;
[UIView animateWithDuration:0.2 animations:^{
dragView.overlayView.alpha = 1;
}];
[dragView rightClickAction];
}
//%%% when you hit the left button, this is called and substitutes the swipe
-(void)swipeLeft
{
MG_DraggableVIew *dragView = [loadedCards firstObject];
dragView.overlayView.mode = GGOverlayViewModeLeft;
[UIView animateWithDuration:0.2 animations:^{
dragView.overlayView.alpha = 1;
}];
[dragView leftClickAction];
}
you can post a NSNotification inside -(void)cardSwipedLeft:(UIView *)card & -(void)cardSwipedRight:(UIView *)card method in DraggableViewBackground.
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:index] forKey:#"someKey"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TestNotification" object:self userInfo:userInfo];
Here index is your card index. To set the index, you can declare a int property named index. Set initial value value of index like index=0; in initwithframe method.
then, increase the value of index when you swipe a card.
set index++; inside -(void)cardSwipedLeft:(UIView *)card & -(void)cardSwipedRight:(UIView *)card method in DraggableViewBackground.
Listen for this notification in your view controller. To achieve that add in the view controller's method in viewDidLoad by this line:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveTestNotification:)
name:#"TestNotification"
object:nil];
implement this selector method in your viewcontroller-
- (void) receiveTestNotification:(NSNotification *) notification
{
if ([[notification name] isEqualToString:#"TestNotification"]){
NSLog (#"Successfully received the test notification!");
NSDictionary *userInfo = notification.userInfo;
int myObject = [[userInfo objectForKey:#"someKey"]integerValue];
NSLog(#"index: %d", myObject);
}
}
on receiveTestNotification method you get card index. then you can get your data from your array by index.
In my opinion, it will be better for you if you maintain a dictionary for cards and their data.
You can create a dictionary with keys as card ids/card numbers and values as their data (description.)
So whenever you are removing card, simply fetch its data from dictionary.
Lets say cardsDictionary is your dictionary like this :
cardsDictionary = #{#"card1_id":#"card1_data"......#"cardn_id":#"cardn_data"};
Then on removing a card you can do like this:
card_data = cardsDictionary[#"card_id"];
I have a program where a user enter strings into an NSMutableArray (myArray) via a Text Field. This array is passed into the next view controller where there is a label (myLabel) and two buttons. Printed to the label is a random string from myArray. ButtonA displays a different random string from the array when pressed and ButtonB removes the current string that is printed to the label and then displays a random string from the array to the label.
This is my current solution:
- (void)viewDidLoad {
self.myLabel.text = [self.myArray objectAtIndex:arc4random() % [myArray count]];
-(IBAction)ButtonA:(id)sender {
self.myLabel.text = [self.myArray objectAtIndex:arc4random() % [myArray count]];
}
-(IBAction)ButtonB:(id)sender {
NSInteger index = [myArray indexOfObject: //what goes here?];
[self.myArray removeObjectAtIndex:index];
self.myLabel.text = [self.myArray objectAtIndex:arc4random() % [myArray count]];
}
Is there a way to get the index of the random string displayed and then remove it from the array? I want this to continue doing this until all items from the array have been removed. Thank you
The // what goes here? should simply be self.myLabel.text.
Though it might be better to add an instance variable that saves off the last random index. Then all array index references should be made with that instance variable.
You also have the same line of code to calculate a random number and set a label repeated 3 times. Create a new method for that and call that function from the three places you have it now.
There are two ways you can do this:
The first way is to store the string you got from your random method. You can declare a global variable for this in your class. And I suggest that you always place a block of similar code in another method.
NSString *generatedString;
- (NSString *)generateRandomString
{
generatedString = [self.myArray objectAtIndex:arc4random() % [myArray count]];
return generatedString;
}
Then in your implementation:
- (void)viewDidLoad
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
[self.myArray removeObject:generatedString];
self.myLabel.text = [self generateRandomString];
}
Another way is to store the index of the string generated:
NSInteger generatedStringIndex;
- (NSString *)generateRandomString
{
generatedStringIndex = arc4random() % [myArray count];
NSString generatedString = [self.myArray objectAtIndex:generatedStringIndex];
return generatedString;
}
Then in your implementation:
- (void)viewDidLoad
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
self.myLabel.text = [self generateRandomString];
}
- (IBAction)buttonA:(id)sender
{
[self.myArray removeObject:generatedStringIndex];
self.myLabel.text = [self generateRandomString];
}
I have dropped my code into a situation where I need to call UITableView data source methods written in some UIViewController class before a particular view is presented so that the cells get prepopulated and I can set a BOOL that the data in the not present viewController class is valid or not. I may explain it in more detail if required, but I wanted to know if its possible to do that. If yes, then how to do it? .. as a particular set of my code written after [tableView reloadData] is dependent on running the dataSource methods of UITableView. Please throw some light on this, if needs to be handled in a specific thread?
Following is the case where I call reloadData. Note: This is happening in another class when basicFactsViewController's viewWillAppear method has not been called yet:
- (BOOL) isComplete {
dispatch_async(dispatch_get_main_queue(), ^{
[basicFactsViewController.tableView reloadData];
});
return basicFactsViewController.isComplete && selectedVehicleId && selectedMakeId && selectedModelId && selectedYearId && selectedTrimId;
}
Now basicFactsViewController.isComplete is checked in this method:
- (BOOL) isComplete {
[self collectKeyHighlights];
return _isComplete;
}
Now the dictionary "tableCells" in the method below uses the cells population to check whether all features have been completed or not:
- (NSDictionary *) collectKeyHighlights {
NSMutableDictionary *key_highlights_update = [NSMutableDictionary new];
NSMutableDictionary *cell_highlight_update = [NSMutableDictionary new];
if(visible_key_highlights.count == 0) _isComplete = YES;
_isComplete = YES;
__block NSMutableArray *reloadCellAtIndexPathSet = [[NSMutableArray alloc] init];
[visible_key_highlights enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *feature = (NSDictionary *)obj;
UITableViewCell *cell = [self.tableCells objectForKey:[NSIndexPath indexPathForRow:idx inSection:0]];
if(cell) {
if([cell isKindOfClass:[DRColorSelectionTableViewCell class]]) {
NSInteger selectedIndex = ((DRColorSelectionTableViewCell *)cell).selectedIndex;
NSInteger numberOfSegments = ((DRColorSelectionTableViewCell *)cell).numberOfSegments;
if(selectedIndex > -1 ) {
NSArray *dataValues = [[visible_key_highlights objectAtIndex:idx] objectForKey:#"data_values"];
NSDictionary *colorData;
BOOL reloadCellForIndexPath = NO;
if (numberOfSegments == selectedIndex) {
colorData = #{ #"normalized" : #"user_defined", #"isother" : #YES, #"hexcode":#"#FFFFFF", #"actual":((DRColorSelectionTableViewCell *)cell).otherColorTextField.text};
reloadCellForIndexPath = YES;
}
else{
colorData = [dataValues objectAtIndex:selectedIndex];
}
[key_highlights_update setObject:colorData forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:colorData forKey:[feature objectForKey:#"name"]];
if (![colorData isEqual:[prevSelections objectForKey:[feature objectForKey:#"name"]]]) {
[reloadCellAtIndexPathSet addObject:((DRColorSelectionTableViewCell *)cell).indexPath];
}
//if (reloadCellForIndexPath) {
//}
} else {
_isComplete = NO;
}
} else if([cell isKindOfClass:[DRInputTableViewCell class]]) {
NSString *textInput = ((DRInputTableViewCell *)cell).inputTextField.text;
if([textInput length]) {
[key_highlights_update setObject:[NSString toSnakeCase:textInput] forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:textInput forKey:[feature objectForKey:#"name"]];
}else {
_isComplete = NO;
}
} else if([cell isKindOfClass:[DRPickerTableViewCell class]]) {
NSString *textInput = ((DRPickerTableViewCell *)cell).inputField.text;
if([textInput length]) {
[key_highlights_update setObject:[NSString toSnakeCase:textInput] forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:textInput forKey:[feature objectForKey:#"name"]];
} else {
_isComplete = NO;
}
} else if([cell isKindOfClass:[DRSwitchTableViewCell class]]) {
// send this everytime for now
BOOL isSelected = ((DRSwitchTableViewCell *)cell).toggleButton.selected;
[key_highlights_update setObject:[NSNumber numberWithBool:isSelected] forKey:[feature objectForKey:#"name"]];
[cell_highlight_update setObject:[NSNumber numberWithBool:isSelected] forKey:[feature objectForKey:#"name"]];
}
}
else{
_isComplete = NO;
}
}];
prevSelections = cell_highlight_update;
if ([reloadCellAtIndexPathSet count]) {
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:reloadCellAtIndexPathSet withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
return key_highlights_update;
}
Now here since
[tableView reloadData]
is not calling cellForRowAtIndePath:, hence, tableCells is not getting populated, hence, I am always getting _isComplete = NO.
If I understand correctly, there is processing being done when the tableview loads (calls it's dataSource methods) and you want to trigger that early to use its results. Calling [basicFactsViewController.tableView reloadData]; early won't work if the basicFactsViewController hasn't been displayed yet. If basicFactsViewController is a UIViewController and has the default view and the tableView property is a subview of that standard view, then (if I remember correctly) the tableView property will be nil until the basicFactsViewController has been displayed. A shortcut around that is to access the viewController's view property and cause it to initialize (viewDidLoad and all that). You can do that by simply messaging the viewController: [basicFactsViewController view].
If I've been right so far I'm fairly confident that will initialize the tableView property. But I'm not sure if it will cause the table view to load its data. And even if it does work, it's definitely not the best solution to the piece of code you're trying to architect. Apple's design for UIKit has been focused on the model/view/controller pattern and it's easier to go with the flow and do the same. I imagine that you could move the processing that is in the data source methods for the tableView out into another class (or maybe even the same class), and call that method to get everything ready for both the tableView and any other checks that you have, storing the data in dictionaries and arrays in such a way that you can easily load them by index into the tableView when cellForIndex is called.