Cant refresh UITableView inside UIViewController - objective-c

Newbie :(
Tweaking my way through tutorials but 36 hours can't figure this one
Example 1.h
#interface RootViewController : UITableViewController {
NSArray *userList; // the current list of users
}
#property (nonatomic, retain) NSArray *userList;
- (void) updateTable:(NSArray *)data;
- (void) fetchData;
#end
Example 1.m
- (void) updateTable:(NSArray *)data {
self.userList = data;
[self.tableView reloadData];
}
This works, the asynchronous XML object returns an array with the selector updateTable and the table updates.
But when I make the view a UIViewController the table doesn't update
Example 2.h
#class xmlGetStories;
#interface FirstViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
UITableView *tableView;
NSArray *userList;
}
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic, retain) NSArray *userList;
- (void) updateTable:(NSArray *)data;
- (void) fetchData;
#end
Example 2.m
- (void) updateTable:(NSArray *)data {
self.userList = data;
NSLog(#"updateTable sees this data: %#", userList);
//[self.tableView reloadData];
[tableView reloadData];
}
updateTable executes and the data is passed across alright but the table doesn't refresh. I realise that it's something to do with the way I'm addressing the table as it's part of a UIViewController rather than a direct UITableViewController. But I thought that
UIViewController <UITableViewDataSource, UITableViewDelegate>
wired in the table. I have another testbed example where example two works if I hardcode the array contents into the AppDelegate.
I have tried
[FirstViewController reloadData];
[FirstViewController.tableView reloadData];
Neither work.
Brain dead looking at code. Hopefully it's something simple.
TIA

The most likely cause is that tableView is nil. Make sure you have wired it in IB.

Related

Obj-C / Using MapKitView and TableView together with delegation

I am a very beginner of objective c and I wanted to create a scene about using 2 child view controller in a master view controller which includes MapKit and TableView. I ve searched it on internet for 1 day and but I couldn't make any of solutions since I failed in different steps of each suggestions. I know that there are a lot of way to pass data between View Controllers and I thought the best way is using delegation logic in this situation. (let me know if I am wrong please). By the way, I can update or move the cursor onto specific location WHEN I set a dummy button on MapKitViewController, so MapKit part is not where I am failing at. I am pretty sure that the problem is about communication between 2 View Controllers which are active at the same time.
Problem:
Updating MapView by clicking a table row of TableView which includes location coordinate detail. Both children view are set on a MasterViewController by 2 container views.
Tried so far:
Created a method "goToLocation:(Location*) location" in MapViewController and sent parameters to this method from TableViewController. =>(Lat and Long parameters received by goToLocation() but MapKitView doesn't get updated)
Tried to create a delegate logic between MapView and TableView.=>(Probably I couldn't create it properly. Please see below)
What I want :
I want to know what part I am doing wrong. Is there a easy or proper way to achieve that ?
TableViewController.h
#class TableViewController; //define class
#protocol TableViewDelegate <NSObject> //define delegate protocol
//define delegate method to be implemented within another class
- (void) locationSelected: (TableViewController *) sender object:(Location *) location;
#end
#property (nonatomic, weak) id <TableViewDelegate> delegate; //define TableViewVCDelegate as delegate
TableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self notifyNow:[_locations objectAtIndex:indexPath.row]];
}
- (void) notifyNow:(Location *) location {
//this will call the method implemented in your other class
[self.delegate locationSelected:self object:(Location *) location];
NSLog(#"Selected: %#",[location name]);
}
MapViewController.h
//make it a delegate for TableViewVCDelegate
#interface MapViewController : UIViewController <TableViewDelegate>
MapViewController.m
- (void)viewDidLoad {
_tableViewController = [[TableViewController alloc]init];
_tableViewController.delegate = self; //set its delegate to self somewhere
}
!!_ This Method Doesn't Get Triggered _!!
- (void)locationSelected:(TableViewController *)sender object:(Location *)location {
NSLog(#"%# is great!", [location name]);
}
MainViewController.m (Master/Root View Controller)
#import "MainViewController.h"
#import "MapViewController.h"
#import "TableViewController.h"
#interface MainViewController ()
#property (strong, nonatomic) IBOutlet UIView *topCont;
#property (strong, nonatomic) IBOutlet UIView *bottomCont;
#property (strong, nonatomic) IBOutlet UIView *safeArea;
#end
#implementation MainViewController
MapViewController *mapViewVC;
TableViewController *tableViewVC;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.navigationController setNavigationBarHidden:YES animated:YES];
tableViewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TableViewController"];
mapViewVC = [self.storyboard instantiateViewControllerWithIdentifier:#"TopChildVC"];
[self addChildViewController:tableViewVC];
[tableViewVC.view setFrame:CGRectMake(0.0f,
0.0f,
self.safeArea.frame.size.width,
self.safeArea.frame.size.height/2)];
[self.bottomCont addSubview:tableViewVC.view];
[tableViewVC didMoveToParentViewController:self];
[self addChildViewController:mapViewVC];
[mapViewVC.view setFrame:CGRectMake(0.0f,
0.0f,
self.safeArea.frame.size.width,
self.safeArea.frame.size.height/2)];
[self.topCont addSubview:mapViewVC.view];
[mapViewVC didMoveToParentViewController:self];
}
#end

Properties are null outside of viewDidLoad

Two properties:
#property (retain, nonatomic) NSString *drinkType;
#property (retain, nonatomic) NSString *wheelType;
When accessed from viewDidLoad as self.drinkType, etc, they hold the value I expect. However, when accessed from a public method
-(void)updateSentenceWithSelectedAromas:(NSMutableArray *)selectedAromas;
they are null. What is happening here?
The "selectedAromas" array is passed from another controller to this method.
ViewController *aromaVC = [[ViewController alloc] init];
[aromaVC updateSentenceWithSelectedAromas:selectedAromas];
ViewController.h
-(void)updateSentenceWithSelectedAromas:(NSMutableArray *)selectedAromas;
#property (retain, nonatomic) NSString *drinkType;
#property (retain, nonatomic) NSString *wheelType;
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// This is working
NSLog(#"The drink type is:%#", self.drinkType);
}
-(void)updateSentenceWithSelectedAromas:(NSMutableArray *)selectedAromas {
// This returns null
NSLog(#"The drink type is:%#", self.drinkType);
}
I think your missing quite a few things, which leads me to think that you're missing some basic understanding of variable scope in ObjectiveC, let's see if this helps you in some way:
First, your selectedAromas array has no relation whatsoever with drinkType and wheelType. So passing this array to the ViewController seems irrelevant.
Second, in your ViewController you're declaring your own drinkType and wheelType variables, so there's no way they will have the value of some other class or Controller.
You probably aren't setting your properties soon enough (init would be a good place). viewDidLoad is called much later in relation to the code you posted.
Okay, Michael Dautermann was absolutely right. The method updateSentenceWithSelectedAromas was in fact running in a separate instance of the view controller. To solve this problem I implemented a protocol listener with my method and set the delegate of the child controller to its parent using a segue.
Thank you everyone for all your help.
In case anyone stumbles upon this, here is what I did:
ViewController2.h
#protocol updateSentenceProtocol <NSObject>
//Send Data Back To ViewController
-(void)updateSentenceWithSelectedAromas:(NSMutableArray *)selectedAromas;
#end
#interface ViewController2 : UIViewController
// delegate so we can pass data to previous controller
#property(nonatomic,assign)id delegate;
#end
ViewController2.m
#synthesize delegate;
-(void)someMethod {
[delegate updateSentenceWithSelectedAromas:selectedAromas];
}
ViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"viewController2Segue"])
{
// Get reference to the destination view controller
ViewController2 *vc = [segue destinationViewController];
vc.delegate = self;
}
}
-(void)updateSentenceWithSelectedAromas:(NSMutableArray *)selectedAromas {
// do stuff with array and properties as needed
}

NSTimer in AppDelegate does not update UILabel in UIViewController

My goal is to start a timer in the app delegate and use it to call methods in other view controllers. When these methods are called they will update the text of the UILabel. I'm performing the method on the main thread but I can't figure out why the UILabel is not being updated. I know the method in the view controller is being called but the UILabel doesn't get updated. I also verified the IBOutlet connection in Interface Builder. I am using storyboard to lay out my views and attach the view controllers to those views. What am I doing wrong?
In my AppDelegate.m:
#import "AppDelegate.h"
#import "GreyViewController.h"
#interface AppDelegate ()
#property (nonatomic) int counter;
#property (strong, nonatomic) GreyViewController *greyClass;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(performOnMain)
userInfo:nil
repeats:YES];
return YES;
}
- (GreyViewController *)greyClass {
if (!_greyClass) {
_greyClass = [[GreyViewController alloc] init];
}
return _greyClass;
}
- (void)performOnMain {
[self performSelectorOnMainThread:#selector(updateLabel) withObject:nil waitUntilDone:YES];
}
- (void)updateLabel {
self.counter++;
NSLog(#"appdelegate counter %i", self.counter);
[self.greyClass updateGreyLabel:[NSString stringWithFormat:#"%i", self.counter]];
}
#end
The GreyViewController.h is:
#import <UIKit/UIKit.h>
#interface GreyViewController : UIViewController
#property (strong, nonatomic) NSString *greyString;
#property (weak, nonatomic) IBOutlet UILabel *greyLabel;
- (void)updateGreyLabel:(NSString *)string;
#end
The method in my GreyViewController.m:
- (void)updateGreyLabel:(NSString *)string {
self.greyLabel.text = string;
NSLog(#"greyviewcontroller string %#", string);
}
Every time you call:
GreyViewController *greyClass = [[GreyViewController alloc] init];
in your "updateLabel" method (i.e. every two seconds) you're instantiating a new GreyViewController. It's very likely you do not want to do that.
Instead, instantiate/create it once (or create it in your storyboard or XIB file) and the update the label which is connected to an outlet.

Delegate to 'Parent' ViewController best practice

The setup:
PickerView (spinSelector) and label (chosenItem) added to ViewController.
Created separate delegate class files (SpinDelegate m&h) for the PickerView delegate.
Created instance of the delegate (SpinDelegate *mySpinDelegate)
Assigned delegate property to delegate instance
ViewController.h
#interface ViewController : UIViewController
{
SpinDelegate *mySpinDelegate;
}
#property (nonatomic, weak) IBOutlet UILabel *chosenItem;
#property (nonatomic, strong) IBOutlet UIPickerView *spinSelector;
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
mySpinDelegate=[[SpinDelegate alloc]init];
self.spinSelector.delegate=mySpinDelegate;
self.spinSelector.dataSource=mySpinDelegate;
}
SpinDelegate.h
#interface SpinDelegate : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
{
ChoiceData *choiceItems;
}
#end
SpinDelegate.m
#pragma mark - PickerView Delegate
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component{
return [choiceItems.choiceList objectAtIndex:row];
}
Next is to use the method:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component;
This is part of the UIPickerViewDelegate protocol. Using this, to simply change the UILabel (chosenItem) in the ViewController to value of row. Something like "The picked item is %i",row
I've read and searched through a ton of posts and questions on UIViewController to UIViewController messaging and looked at protocol/delegate, singleton, NSNotificationCenter... it just seems to me that there is a syntax I am missing to address the parent/super? The ViewController created the instance of the delegate, doesn't the delegate have scope?
Please educate me on this. : )
You can do something like this:
ViewController.h
#import "SpinViewController.h"
#interface ViewController : UIViewController<SpinViewControllerDelegate>
{
}
#property (nonatomic, weak) IBOutlet UILabel *chosenItem;
#property (nonatomic, strong) IBOutlet UIPickerView *spinSelector;
ViewController.m
- (void)someFunction
{
mySpinViewController=[[SpinViewController alloc]init];
mySpinViewController.delegate=self;
// show or present mySpinViewController
}
//implement the followed protocol's method
-(void) optionSelected:(NSString*)cellValue{
}
SpinViewController.h
#protocol SpinViewControllerDelegate <NSObject>
#optional
-(void) optionSelected:(NSString*)cellValue;
#end
#interface SpinViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
{
ChoiceData *choiceItems;
}
#property (nonatomic,retain)id <SpinViewControllerDelegate> delegate;
#end
SpinViewController.m
-(void) pickerValueSelectedOrSimilarFn:(NSString*)cellValue{
// this is how you give a callback to classes following the protocol
[self.delegate optionSelected:cellValue];
}

objective C: Using a Delegate to call a function in parent class

I'm creating a 3 layer navigation popup controller and on the 3rd popup controller I have a delegate method to access dismissPopup method that is in the parent class. I can't seem to call it, my NSLog messages in the function in the parent class isn't even showing so I must be either using delegation wrong or I'm calling it incorrectly.
The 3 classes ParentViewController has a toolbar with a button that brings up the table view --> RegionViewController is the First table view controller with items --> ConusViewController is the 2nd table view controller that is pushed onto the navigation stack. I'm trying to call the method dismissPopover that is in the parent method with a delegation after the selection is clicked on so the whole popover goes away.
In the ConusViewController if the delegation had worked I would have seen "Method Accessed" from the function in the parent class. It doesn't show so I must be using delegation wrong.
Sorry for being so wordy on my post, I wanted to be complete on what I'm trying to do here. Thanks.
ParentViewController.h
#import <UIKit/UIKit.h>
#import "ConusViewController.h"
#interface EnscoWXViewController : UIViewController <ConusViewControllerDelegate> {
UIPopoverController *popoverController;
IBOutlet UIWebView *webImageDisplay;
ConusViewController *cViewController;
}
#property (nonatomic, retain) UIPopoverController *popoverController;
#property (nonatomic, retain) UIWebView *webImageDisplay;
#property (nonatomic, retain) ConusViewController *cViewController;
-(IBAction) buttonShowRegion:(id) sender;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "RegionViewController.h"
#implementation ParentViewController
#synthesize cViewController;
-(IBAction) buttonShowRegion:(id) sender {
...
}
-(void)dismissPopover {
[popoverController dismissPopoverAnimated:YES];
printf("Method Accessed\n");
}
- (void)viewDidLoad {
cViewController = [[ConusViewController alloc] init];
cViewController.delegate = self;
[super viewDidLoad];
}
RegionViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0: {
ConusViewController *conusViewController = [[ConusViewController alloc] initWithNibName:#"ConusViewController" bundle:nil];
conusViewController.contentSizeForViewInPopover = CGSizeMake(320, 350);
[self.navigationController pushViewController:conusViewController animated:YES];
[conusViewController release];
break;
}
case 1: {
break;
}
}
}
ConusViewController.h
#import <UIKit/UIKit.h>
#protocol ConusViewControllerDelegate <NSObject>
#required
- (void)dismissPopover;
#end
#interface ConusViewController : UITableViewController {
NSMutableArray *conusItems;
id delegate;
}
#property (nonatomic, assign) id <ConusViewControllerDelegate> delegate ;
#end
ConusViewController.m
#import "ConusViewController.h"
#import "ParentWXViewController.h"
#implementation ConusViewController
#synthesize delegate;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *itemRequested = [conusItems objectAtIndex:indexPath.row];
NSLog(#"logging: %#", itemRequested);
[delegate dismissPopover];
[itemRequested release];
}
Just before calling [delegate dismissPopover], check if delegate is actually set. It probably isn't.
I see in ParentViewController.m you create an instance of ConusViewController and set its delegate, but never display it. In RegionViewController.m you create another instance of ConusViewController without setting its delegate and that is the one that seems to be being displayed.
Not sure if I missed it, but I never see you set the delegate property in ConusViewController. That needs to be set to an instance of the object that is to be delegated to (the object that has dismissPopover implemented in it).