Call [tableView realoadData] on RootViewController from AppDelegate? - iphone

In my app I am making an HTTP request and waiting and updating my RootViewController with the data. The problem is, I need to make a call to [tableView reloadData] after my data collection has finished, as it stands right now the tableview will either be partially populated or not populated at all. I came across this thread:
Passing an NSDictionary to a ViewController from AppDelegate
Which included this solution for setting data in the view conroller from app delegate, but I am wondering how I can adapt this for my situation? All I need, I think, is a reference to the rootViewController in appDelegate so I can call reloadData when I need to
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil tableDataSource:(NSArray*)tableData {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
tableDataArray = [tableData retain];
}
return self;
}
Thanks for any ideas

Declare that tableView as a property such as myTableView in that class. Now reference that class and call [classObject.myTableView reloadData];
It should work.

You can give the appDelegate a property that receives a pointer to the RootViewController and which is set by the RootViewController when it is initialized.
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil
tableDataSource:(NSArray*)tableData
{
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])
{
// Custom initialization
tableDataArray = [tableData retain];
(MyAppDelegate *)([[UIApplication sharedApplication] delegate]).rootController = self;
}
return self;
}

Related

NSLog returns (null) when logging string from another view controller

I posted a question earlier about the same thing, but now I have made a simple project to show what I am doing, so the problem can be found easier.
I have two viewControllers, one is called ViewController and the other SecondViewController.
I tried sending a NSString called testy to a viewController and logging it, but it returned null.
Here is my code trying to send the string from viewController to secondViewController
ViewController.m
#import "ViewController.h"
#import "SecondViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize cellName;
- (void)viewDidLoad
{
[super viewDidLoad];
cellName = #"testy";
SecondViewController *obj = [[SecondViewController alloc] init];
obj.cellName2 = self.cellName;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
}
#property(nonatomic,retain) NSString *cellName;
#end
SecondViewController.m
#import "SecondViewController.h"
#import "ViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize cellName2;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated {
NSLog(#"%#",cellName2);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
SecondViewController.h
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController {
}
#property(nonatomic,retain) NSString *cellName2;
#end
Edit
I would like to say that my storyboard has two viewController that each have a button. Each button modally brings you to the other view.
Are you sure viewDidLoad is being called? I think it's not called until the view is loaded. I don't think it's called after an alloc init. Also you are setting the string in obj 2 after init. Even if what you are thinking is correct, the 'viewDidLoad' method may be called before the string is being set.
If you want a variable set on init you need to override viewController 2's init method to something like initWithMyVariable then the var will be set on init.
From: Passing Data between View Controllers
Passing data forward to a view controller from another view controller. You would use this method if you wanted to pass an object/value from one view controller to another view controller that you may be pushing on to a navigation stack.
For this example we will have ViewControllerA and ViewControllerB
To pass a BOOL value from ViewControllerA to ViewControllerB we would do the following.
in ViewControllerB.h create a property for the BOOL
#property(nonatomic) BOOL *isSomethingEnabled;
in ViewControllerA you need to tell it about ViewControllerB so use an
import "ViewControllerB.h"
Then where you want to load the view eg. didSelectRowAtIndex or some IBAction you need to set the property in ViewControllerB before you push it onto nav stack.
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:#"ViewControllerB" bundle:nil];
viewControllerB.isSomethingEnabled = YES;
[self pushViewController:viewControllerB animated:YES];
This will set isSomethingEnabled in ViewControllerB to BOOL value YES.
It might be worth for all who read this article to mention working patterns passing variables between two views:
Options:
Use global variable: SO answer
Use delegation pattern: SO answer
Use notification infrastructure: article
Persist the value in app's userdefault storage, then read when you need : SO answer
For this particular situation it might be better not to create secondviewcontroller in the firstviewcontroller's viewdidload, but to keep the cellName until that point when a user action happens (eg. button press) and then in that method you just set the newly created secondviewcontroller's cellName2 property.
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:#"second" bundle:nil];
secondViewController.cellName = self.cellName;
I've tested and it is properly logging the value in secondviewcontroller's viewdidload.
Instead of doing: cellName = #"testy"; You should Call:
self.cellName = #"testy";
Also, When you alloc and init in:
SecondViewController *obj = [[SecondViewController alloc] init];,
the viewDidLoad() for secondViewController is called right at that time and you are initializing it's iVar later in the line obj.cellName2 = self.cellName;
That's why you are having NSLOG as null.
Print the NSLOG in viewWillAppear() of secondViewController and you will see the right value this time.
-Create a initialization method for second view controller to pass the variable..
in secondview controller
.h file
add init method
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil cellString(NSString* )cellName;
and .m file
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil cellString(NSString* )cellName{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
cellName2=cellName;
}
return self;
}
and in ViewController.m for initialization
SecondViewController *obj = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil cellString:self.cellName];
This should work...
Best of luck
In viewcontroller.m you have the following in viewDidLoad:
..
..
SecondViewController *obj = [[SecondViewController alloc] init];
obj.cellName2 = self.cellName;
}
The SecondViewController "obj" is never being presented before the end of viewDidLoad, hence NSLog printing null.
If you are wanting to pass a value to SecondViewController via a storyboard, you will need to use the prepareForSegue method. A good example of using it can be found here.

Encapsulation when moving from xib to storyboards?

With xibs, you could call different initializers:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Fetch Note...
// Initialize Edit Note View Controller with the fetched Note
EditNoteViewController *vc = [[EditNoteViewController alloc] initWithNote:note];
// Push View Controller onto Navigation Stack
[self.navigationController pushViewController:vc animated:YES];
}
This allowed me to keep my variables (in the EditNoteViewController) private and I was also able to set default values to some of the variables, e.g.
- (id)initWithNote:(Note *)note {
// ....
if (self) {
self.note = note;
self.isEditing = YES;
}
return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
//...
if (self) {
self.isEditing = NO;
}
return self;
}
I'm now trying to work with storyboards:
is there a good way to set variables from prepareForSegue without exposing variables or any other implementation?
how can I set default values?
Please be as explicit as possible
You can use - initWithCoder:
This method is called whether you instantiate a UIViewController from nibs or storyboards.
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self) {
// initializations here
}
return self;
}
You should user prepareForSegue. Expose only what you need to in the receiving View Controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Prepare for next view controller
if ([segue.identifier isEqualToString:#"someThing"]) {
SomeThingViewController *viewController = segue.destinationViewController;
viewController.someProperty = #"something else";
}
}
The property someProperty would need to be exposed in the header for SomeThingViewController
#property (nonatomic, strong) NSString *someProperty;
To set a default value, check the value of the property in viewDidLoad of the receiving view controller
if (someProperty==nil) someProperty = #"Default";

UITextView.text lags when loading text

I want to display text with an UITextView, which is part of an UITabBarView and do so in the following Class:
.h - File:
#import <UIKit/UIKit.h>
#interface DescriptionViewController : UIViewController{
IBOutlet UITextView *descriptionText;
}
#end
.m - File:
#import "DescriptionViewController.h"
#import "Globals.h"
#interface DescriptionViewController ()
#end
#implementation DescriptionViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(void) viewWillAppear:(BOOL)animated{
Globals* myGlobals = [Globals sharedGlobals];
descriptionText.text = [myGlobals.currentLine objectAtIndex:5];
}
#end
When the TextView is displayed the first time it is empty, when i switch to another tab and switch back to the "TextView - Tab" it is displayed properly. But I obviously want it to be displayed correctly the first time..
I already tried to move the relevant code to the -viewDidLoad function, but nothing changed. Also, I tried the -setNeedsDisplay function without success (maybe I used it wrong? - [descriptionText setNeedsDisplay].
I appreciate any help and further code will be posted on request.
Please set breakpoint on ViewWillAppear Method of your class then check that in first time what you are getting from NSLog("%#", [myGlobals.currentLine objectAtIndex:5]);
and you are missing [super viewWillAppear:animated];
try this,
- (void)viewDidLoad
{
[super viewDidLoad];
Globals* myGlobals = [Globals sharedGlobals];
descriptionText.text = [myGlobals.currentLine objectAtIndex:5];
}
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
Globals* myGlobals = [Globals sharedGlobals];
descriptionText.text = [myGlobals.currentLine objectAtIndex:5];
}

Initialization of controls in custom uiview

I've created a custom uiview in IB and set the class for it.
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface myView : UIControl {
IBOutlet UITextView *textView;
}
#end
#import "myView.h"
#implementation myView
- (void)commonInit
{
[textView setText:#"lajsdfklasdfjl"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self commonInit];
}
return self;
}
-(id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
[self commonInit];
}
return self;
}
#end
I put a textview with text on this view in IB and linked it to IBOutlet IUTextView *textView.
When I drag this custom view on my UIViewController (from classes tab in IB's library) the view is empty.
- (id)initWithCoder:(NSCoder *)aDecoder is calling but the textView is null.
What is wrong? Thanks
As far as I remember, the hierarchy is not properly set up in init, as the properties can only be set after init has finished.
You want to use
-(void)awakeFromNib
{
[super awakeFromNib];
[self commonInit];
}
instead and remove the initWithCoder: thing altogether.
As a side note, you should let your class names begin with upper case letters.
There should be nothing stopping you from using a custom UIViewController which has methods built in for initializing or deallocating the UIView that it contains. I found this to be a simpler solution. For example, you can set up your custom UIViewController using a nib with a UIView, then make sure your File's Owner is set to the custom class.
You can then remove the following 3 instance methods
- (void)commonInit
- (id)initWithCoder:(NSCoder *)aDecoder
- (id)initWithFrame:(CGRect)frame
and in your custom UIViewController class use initWithNibName as below
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.view.frame = CGRectMake(0, 44, self.view.frame.size.width, self.view.frame.size.height);
}
return self;
}
This initWithNibName instance method will be called automatically when you alloc your custom UIViewController class like this
CustomUIViewController *vc = [[CustomUIViewController alloc] init];
Put a break point in initWithNibName and you will see it called.

UIViewController with Nib File and Inheritance

I'm trying to do something really tricky and I'm still stuck at a point. I'm attempting to instance an UIViewController with a Nib file inherited from an other UIViewController with an other Nib file.
The problem is when I instantiate my son UIViewController .
// SonViewController
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil
{
if ((self = [super initWithNibName:nibNameOrNil
bundle:nibBundleOrNil])) {
// Custom initialization.
}
return self;
}
The init method initWithNibName:bundle: should call the super class but it only call its own nib file. In the super class, I tried to override the initWithNibName:bundle: method and put the nibName myself like that :
// MotherViewController
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil
{
if ((self = [super initWithNibName:#"MotherViewController"
bundle:nibBundleOrNil])) {
// Custom initialization.
}
return self;
}
It only init and display the Mother Class and its IB Object. I understand why but I begin thinking it is impossible to do what I want. Any suggestion ?
Edit:
I would use my SonViewController just like that :
SonViewController *son = [[SonViewController alloc]
initWithNibName:#"SonViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:son animated:YES];
[son release];
It should display son and mother IB Object...
Regards,
kl94
Normally, you should only use a specific nib in the init method, and not the initWithNibName:bundle:, for this reason.
#implementation MotherViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
//custom initialization
}
return self;
}
- (id)init {
return [self initWithNibName:#"MotherViewController" bundle:nil];
}
Then, to use the default nib for MotherViewController, you just use [[MotherViewController alloc] init];.
As an alternate, you could define a different initializer in MotherViewController for this reason.
#implementation MotherViewController
- (id)_initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
//custom initialization
}
return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
return [self _initWithNibName:#"MotherViewController" bundle:nibBundleOrNil];
}
Then, use a private category interface to tell SonViewController about this method.
//SonViewController.m
#interface MotherViewController (PrivateInit)
- (id)_initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
#end
#implementation SonViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
//custom initialization
}
return self;
}
I know this is an old thread, but I just found an incredibly blog post here.
Essentially, you have to iterate through all the views of the parent class and add them as subviews to your child class. Here's how I implemented it in my project:
// ChildViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self addSubviewsFromSuperclass];
}
// ParentViewController.h
- (void)addSubviewsFromSuperclass;
// ParentViewController.m
- (void)addSubviewsFromSuperclass
{
UIView *selfView = self.view;
UIView *nibView = nil;
#try
{
nibView = [NSBundle.mainBundle loadNibNamed:NSStringFromClass([self superclass]) owner:self options:nil][0];
}
#catch (NSException *exception)
{
NSLog(#"Something exceptional happened while loading nib:\n%#", exception);
}
self.view = selfView;
for (UIView *view in nibView.subviews)
{
[self.view addSubview:view];
}
}
That addSuviewsFromSuperclass method is not my coding. I have to give full credit to the author of the blogpost I mentioned above. Download his example project and you'll find it in his JMViewController.m.