Adding an App Delegate to multiple View Controllers with reusable code - ios

Edit: I've since learned that what I was doing is very, very poor design. Do not do what I did. Instead, consider reading up on how MVC works, and making proper classes. Do not substitute controllers for objects.
Not sure if I'm going about my architecture the proper way, but what I'm doing is declaring an app delegate in each of my view controllers, and then giving the app delegate it's own view controller delegate for each view controller.
I end up adding the same three lines to each view controller, and another view controller to the app delegate each time I create a new view controller. Is there an easier way to do this with inheritance (I.E. give all view controllers an appDelegate property, then assign it in some parent function).
Here's what I'm currently doing:
//AppDelegate.h
#interface AppDelegate
#property (strong, nonatomic) MyViewController* myViewDelegate;
#property (strong, nonatomic) MySecondViewController* mySecondViewDelegate;
#end
//MyViewController.m
#interface MyViewController ()
#property (strong,nonatomic) AppDelegate* appDelegate;
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = (AppDelegate*) [UIApplication sharedApplication].delegate;
self.appDelegate.myViewDelegate = self; //MySecondViewController would use:
//self.appDelegate.mySecondViewDelegate = self;
}
...
So two things:
I don't really see a way around declaring new view controllers in the app delegate without using a single root controller that gets assigned on segues, but is having multiple view controller delegates like I'm considered poor design in the first place?
I need a way for ViewDidLoad to call a parent ViewDidLoad to set the value of the app delegate property, as well as the declaration for that app delegate.

Related

Why is it null when I try to access data in another view controller? (Objective-C, XCode)

I am trying to use the .text of UITextField in my first view controller in another .text of UITextField in my second view controller, but my firstPage.firstTField.text turns out to be (null) in my second view controller even though I printed _firstTField.text in my first view controller and it printed out the input that was entered.
What may be the problem? Why is null?
FirstViewController.h
#property (weak, nonatomic) IBOutlet UITextField *firstTField;
SecondViewController.h
#property (weak, nonatomic) IBOutlet UITextField *secondTField;
SecondViewController.m
#import "FirstViewController.h"
- (void)viewDidLoad {
[super viewDidLoad];
FirstViewController *firstPage = [[FirstViewController alloc] init];
_secondTField.text = firstPage.firstTField.text;
}
You should treat a view controller's views as private. That's part of the appearance of the view controller, not it's function, and if objects outside of the view controller expect the views to look a certain way then when you change the appearance of the view controller's views it breaks other code. Bad.
In this situation it's worse. It just doesn't work, for the reason #nhgrif explains. You just created a new FirstViewController object, and it's views don't even exist yet. (A view controllers views are not created until the system is asked to display the view controller to the screen.)
You should create a property in your view controller that exposes the string(s) you need to read/write and use that instead.
However it's strange that you would create a new instance of a view controller and then immediately try to read text from one of it's fields. How can it possibly have useful data if the view controller was created on the line before? What are you expecting to happen?

Calling method declared in ViewController1.m from another ViewController2.m file

I have declared my method 'callWEBservice()' in ViewController1.m and i want to call in ViewController2.m . I have created object of ViewController1.m in ViewController2.m as:
ViewController1* mainVC = [[ViewController1 alloc] init];
Now i am trying to call that method but i am unable to do. Please help on this as I am new to iOS and I have searched some are saying to use delegates.
You need to define the method signature in your .h file -
- (void) callWebService;
and then in your .m file you define the method body:
- (void)callWebService
{
// Whatever you need to do to call the web service
}
Then in ViewController2.m you can #import "ViewController1.h"
Now you can call [mainVC callWebService];
BUT The code you have shown creates a new instance of ViewController1 - If you already have an instance of ViewController1, such as the main view in your app, then this probably isn't what you wanted - you may need to set a property in ViewController2 and store a reference to your ViewController1
e.g. in ViewController2.h
#import "ViewController1.h" // or use #class ViewController1 directive
#property (strong,nonatomic) ViewController1 *mainVC;
Then before in ViewController 1, before you present ViewController2 instance
vc2.mainVC=self;
Your invocation in ViewController2 then becomes
[self.mainVC callWebService];
At the risk of confusing you further, as a design note, it probably isn't best to have the callWebService method in a view controller. It might be more appropriate to create a singleton class for this purpose.
First of all, don't use view controllers for this purpose, create a new class to handle methods of the same kind, then use that one across your view controllers. IF you want the SAME class to be shared across your program, then create a singleton.
How to call method from one class in another (iOS)
However, if you still want to do the view controller to view controller thing, the reason its not working is because you are instantiating a new view controller, not the one you were already using.
You have to pass the reference of the first VC to the second VC. It depends on how you are presenting the second VC. If you are using the Interface Builder, then you need to use:
How to pass prepareForSegue: an object
If you are manually creating and presenting the VC, before presenting it let it know which is the first VC.
You can use delegates like this:
How do I set up a simple delegate to communicate between two view controllers?
STILL consider redesigning your usage of the view controllers.
EDIT:
2 options,
1) Singleton:
Follow this guide http://www.galloway.me.uk/tutorials/singleton-classes/
2) AppDelegate:
Instantiate an object of the class in the .m of the app delegate and assign it to a property in the .h of the App Delegate.
Then, retrieve this object.
This is an example of doing it with the motion manager from ios:
AppDelegate.h:
#property (strong,nonatomic) CMMotionManager *motionManager;
AppDelegate.m
_motionManager = [[CMMotionManager alloc] init];
ViewController1-2-etc
CMMotionManager *motionManager;
motionManager = ((AppDelegate*)[UIApplication sharedApplication].delegate).motionManager;
If you want to use methods from outside your class, you should declare them in your ViewController1.h file, not in the m, otherwise they are not visible (you could still call them using performSelector:withObject:afterDelay: but you should use the first solution)

Subclass UITabBarController in iOS6 with storyboards

I started a new project with iOS 6 ARC and Storyboards
I made a very simple app that has a Tabbar and 2 views
so now I created a TestViewController file with is extending UITabBarController and I put the custom class in the storyboard.
now the issue is that I am trying to implement some delegate methods like
- (BOOL) tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController;
But it never calls it. Can anyone help?
I noticed too that if I put that code in the first view controller it works fine. It seems like the first view controller overwrites the TabBarController before. I am very confused.
for more testing I added in the TestViewController.m some logging:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"%#",self.tabBarController.viewControllers);
}
and the view controllers are null.
I even tried doing self.tabBarController.delegate = self;
But still no solution.
TestViewController is your tab bar controller, so your log should just be self.viewControllers, not self.tabBarController.viewControllers. You shouldn't need to add the property viewControllers either, since your subclass inherits that property from UITabBarController. The reason your delegate message is not called is because of the same problem. You should set the delegate with (in TestViewController):
self.delegate = self;
This is assuming that you want TestViewController to be the delegate, it wasn't clear to me if that's what you wanted.
Have you tried linking them in Interface Builder?
Add this to your header file first though
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;

iOS: call method from a separate viewController

In my main view controller, I have a button that calls a popover. Since the popover has it's own view controller, its buttons call methods in the popover's view controller. But how would I call a method from the main view controller?
I tried this. In the popover view controller I added a property in the .h
#class ViewController;
#interface PopoverContent : UIViewController <UITextFieldDelegate>
...
#property (strong, nonatomic) ViewController *parentView;
In my popover implementation I did this in viewDidLoad:
self.parentView = [[ViewController alloc] initWithNibName:nil bundle:nil];
In ViewController I have a method called generateHash so I tried
[parentView generateHash];
But I get the error:
No visible #interface for 'ViewController' declares selector 'generateHash'
Any idea what I'm doing wrong? Thanks
remove this line, don't want to create a new instance of view controller
self.parentView = [[ViewController alloc] initWithNibName:nil bundle:nil];
change this line
#property (strong, nonatomic) ViewController *parentView;
to this, so your parent view pointer is of the right class type
#property (weak) ParentView *parentView;
now inside parent views .m file
- (void) createPopup
{
PopoverView *popoverV = [[PopoverView alloc] init];
popoverV.parentView = self;
//And some command to show your popup, addSubview, or presentModal, or whatever
}
Then in PopoverView.m file, you can call methods of the parentView like so
[self.parentView SomeMethod];
This is a good place to use a delegate protocol. In the PopoverContent.h, add something like this:
#protocol PopoverContentDelegate : NSObject
- (void) method1;
#end
Naturally, you can have more than one method, and the method(s) can return values and take parameters like any other method. Also, in the same file, add property called delegate. (Technically, it can be called anything, but everyone who looks at your code will know exactly what you're doing if you call it delegate.)
#interface PopoverContent
#property (weak) id<PopoverContentDelegate > delegate;
//other properties and methods
#end
Finally, in your "main" view controller's .m file, import PopoverContent.h file and set the delegate to self. Also implement method1 to do whatever you need it to do.
//Create the view controller
myPopoverContentController.delegate = self;
//Create the popover with the view controller.
Now, in PopoverContent controller, you can call method1 on the delegate wherever you need to.
[delegate method1];
First, to answer your question, you probably have to define the generateHash method in your ViewController.h file.
Second, I'd suggest that your design approach is not optimal. The generateHash method probably needs to be in another file that can be called from both your ViewController and Popover content controller. For example consider another objective-c .h/.m pair "MyHashMethods":
MyHashMethods.h
+ (void)generateHash;
MyHashMethods.m
+ (void) generateHash
{
// hash code
}
This would allow you to just include MyHashMethods.h in whatever view controllers you need and then call
[MyHashMethods generateHash];
when you need it.

iOS - How to send a reference to a MapView nested within a UITabBarController

This might be a simple question, but I moved one of my apps from a view based application to a window-based application. In the original app, I had one view with a view controller and a map. I had a class that parsed some data and sent it to the view controller. I used the following code from ClassA to send data to ClassB which added an annotation.
AnnotationProblemAppDelegate *appDelegate = (AnnotationProblemAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.viewController loadOutAnnotations];
I cannot alloc the view controller because it will create a new instance of the view controller. I need to pass a reference to the view controller when creating ClassA.
Now that the map view is nested within a UITabBArController, I am not sure exactly how I pass the reference from ClassA to the ClassB with the map. Do I need to add a new delegate method or initiate a protocol? I hope this is enough information. Let me know if I can clarify any further.
Thank you in advance!
I figured it out myself. To call [[UIApplication sharedApplication] delegate], I had to some coded to connect everything up in my window-based application. I completed the following steps to hook everything up:
I referenced the class in MyAppDelegate.h before #interface
#class MyClass
Declare an IBOutlet for my class
IBOutlet MyClass *myClass;
Make my IBOutlet a property
#property (nonatomic, retain) IBOutlet MyClass *myClass;
Synthesize the property (make sure to also release it)
#synthesize myClass;
Connect the IBOutlet to the view controller in Interface Builder. In my app which had a Tab Bar Controller and a Navigation Controller inside the Tab Bar Controller, I had to make sure the IBOutlet went to the view controller which was nested with in the navigation controllers.
Finally, to reference myClass in any of my other classes, I called UIApplication sharedApplication with the following code:
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.myClass methodBeingCalled];
I hope this helps if you come across the same problem!

Resources