global variable - ios

I am a newbie to objective c and xcode so please pardon the mistakes in usage of the right technical terms.
I made a new project and have an appdelegate (.h and .m files).
In the app delegate I have some variables (settings). One of the variables is mainImage.
I put it in the appdelegate as I want it to be accessible from any view controller.
I added a viewcontroller called MainViewController (.h and .m files).
In my MainViewController (.m) I have some custom methods.
In the viewDidLoad method I have the following code
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[currentImage setImage:appDelegate.mainImage];
Question:
In any of the methods of the MainViewController, whenever I wanted to read the "mainImage" I have to do the above 2 lines.
Is there a place in the .m file that you can do it once and it is available in all the methods of that file?
Thanks a lot in advance,
Prasad.

You could write a method in MainViewController with those two lines it in that returns an object of type UIImage or whatever it is, and then just call that. Invocation would be something like [self mainImage] and the method signature would be - (UIImage *)mainImage.

You can put these two lines in the viewDidLoad method of your MainViewController as example:
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[currentImage setImage:appDelegate.mainImage];
That's done once, then each time you need currentImage content, you just call it:
UIImage *img= [[UIImage alloc]init];
img = self.currentImage;
Assuming currentImage is declared as property in MainViewController.

#Malloc and #jack...that was pretty close to what I needed..nevertheless, I have quite a few properties..hence I ended including the call in the header file...
#define appDelegate ((MyAppDelegate *)[UIApplication sharedApplication].delegate)
Then I call call appDelegate.any_of_my_properties
From my C# experience, I know that its probably a bad practice to include a global variable in app delegate and reference it this way..however, this one time..I think it makes sense..given that the object is really not tied to any particular view controller (the image was just an example) in reality..I have some dynamic value holders.

Related

Highlighting a button from another class in objective-C, iOS

I have an app that includes a bluetooth LE class for handling a BT connection. When a characteristic value is changed I want code in the BT class to change a button state on the main viewcontroller. I'm new to obj-c and usually use stackoverflow to help with my understanding. I have the following defined in my viewcontroller:
#interface myViewController : UIViewController<CLLocationManagerDelegate, UITableViewDataSource, UITableViewDelegate, UINavigationBarDelegate, UIActionSheetDelegate>
#property (weak, nonatomic) IBOutlet UIButton *eraseButton;
within the view controller, I can happily change the button state:
[self.eraseButton setHidden:YES];
and
-(void) deselectEraseButton
{
[self.eraseButton setSelected:NO];
}
within the BT class I have tried many things (changing a property of viewcontroller which changes the button state via a timer, calling a method that directly changes the button state etc) and although the code is executed (I have breakpoints set on the code in myViewController), the button state isn't changed. For example:
myAppDelegate *app = (myAppDelegate *)[[UIApplication sharedApplication] delegate];
app.vCtrl=[[myViewController alloc] init];
[app.vCtrl deselectEraseButton];
What am I doing wrong and what's the best way of doing this? I'm sure it shouldn't be this hard!
UPDATE:
with the help of Zhi-Wei Cai (thanks), I'm made some changes but alas, it's still not working:
I added the following to myAppDelegate.h:
#property (strong, nonatomic) myViewController* vCtrl;
I added the following to myAppDelegate.m:
-(void)eraseButton:(BOOL)state{
[vCtrl accessEraseButton:state];
}
From the BT class, I do the following:
[self performSelectorOnMainThread:#selector(test) withObject:nil waitUntilDone:YES];
with the following method in that class:
-(void) test
{
myAppDelegate *app = (myAppDelegate *)[[UIApplication sharedApplication] delegate];
[app eraseButton:0];
}
This called myViewController and on thread1 did the following:
-(void) accessEraseButton: (BOOL) state
{
if (state==0)
{
[self.eraseButton setSelected:NO];
[self.view setNeedsDisplay];
}
else
{
[self.eraseButton setSelected:YES];
}
}
I then added [self.view setNeedsDisplay]; for good luck. The button is still not changing state but the code is running (I can hit a breakpoint and it breaks on the method and the method is running on thread1)?!? Any ideas?
* UPDATE 2 *
Ok, I've worked out what I was doing wrong. I was doing all of the above without properly referencing the viewcontroller. So although the code was executing, the instance of the viewcontroller wasn't correct and all the button handles were nil. I needed to add the following to myViewController.m, in viewDidLoad:
myAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.myViewController = self;
Now it works. Happy days...
Because when you do app.vCtrl = [[myViewController alloc] init];, you actually created a new instance of the UIViewController, not the original one that's on your screen.
I didn't test them, but you can achieve what you wanted by:
Via Interface Builder: Setup an IBOutlet of the UIViewController in your myAppDelegate, connect it to the ViewController within Interface Builder, then create a method in myAppDelegate to change it or access it directly using myAppDelegate as it's a property. You can now call it within your BT class e.g. [app deselectEraseButton] or [app.myViewController deselectEraseButton] or even do something with the button if the button is a property of the AppDelegate/VC.
Via Delegate: Create a #protocol to setup a delegate for the BT class, so the it can do callback within other classes such as myAppDelegate.
Via NSNotificationCenter: Use - (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender and - (void)postNotificationName:(NSString *)notificationName object:(id)notificationSender userInfo:(NSDictionary *)userInfo to do it. This will work app-wide.
I believe the above three already has tons of code example on SO, just search them and you will get what you need.
I've updated by original question with 2 updates that lead to a final understanding and solution. Hope it helps others who may be similarly confused!

how To set and pass value of variable which also accesible in another View objective C

In AppDelegate.h
#property(strong,nonatomic)NSString *str2;
In ViewController.m
AppDelegate *c3=[[AppDelegate alloc]init];
c3.str2= #"Hello";
NSLog(#"Output:-%#",c3.str2);
Output:-Hello
Navigation code in Tableview didselect method(view Controller):-
Class2 *c2=[self.storyboard instantiateViewControllerWithIdentifier:#"cl2"];
[self.navigationController pushViewController:c2 animated:YES];
In Class2.m:-
AppDelegate *c3=[[AppDelegate alloc]init];
NSLog(#"%#",c3.str2);
Output:-Null
First, let's fix your current approach: the reason some recommend using app delegate to store shared values is that there is one easily accessible instance of it. You never create a new app delegate, but access the shared one instead:
AppDelegate *c3 = (AppDelegate*)[[UIApplication sharedApplication] delegate];
Once you replace [[AppDelegate alloc]init] with the above, your code will start working the way you expect.
However, this solution is not ideal. App delegate is not supposed to be a place for storing shared values; they should be stored in your model object (as "M" in MVC, Model-View-Controller). Create a singleton model object, place shared variable in it, and use that shared variable by accessing the singleton from different view controllers.
In AppDelegate.h
#property(strong,nonatomic) NSString *str2;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
_str2 = "Hello"
}
In ViewController.m and any other view controller that wants to access str2:
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSLog(#"Output: %#", delegate.str2);
Never create AppDelegate object on your own. Instead, access its instance via sharedApplication.

When to unregister a delegate of a UIView or UILabel

I got a customized UILabel which register itself to a communication instance during creation. This part works perfect, the UILabel will . But when I remove the UILabel will update itself after called by the communication instance. I have of course remove the listener delegate too. But as ViewDidUnload is not call anymore, I do not know where.
Here the code sample:
#implementation MyValueLabel
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self)
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate addBalanceListener:self];
}
return self;
}
-(void)communicationUpdate
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
}
// This is the missing Method
-(void) DestructionOfTheLabelWhichIDidNotFound
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate removeBalanceListener:self];
}
#end
Technically you seem to be looking for the -dealloc method which is called on an object when it is to be deallocated. However I do not believe that is an adequate solution.
The example provided here seems to be doing a number of unexpected things which I believe will result in code which is surprising to other developers, requires fighting against common patterns in the UIKit framework, and be difficult to maintain.
Normally iOS view controllers are responsible for mediating or coordinating how data reaches view objects. A controller supplies a view with either the data it should currently display or provides it with a source of that data. This allows a single view class to be used in many different locations and to display data of the same type regardless of its source. Instead here we have a view which reaches out to obtain it's own data. There's no way for a controller to determine when an instance of this view class should update or what data it should display. For example the controller cannot decide to pause updates, or cause other view elements to update at the same time.
In addition this view makes several assumptions about the source of it's data and how that source may be obtained. The view assumes that the current application's app delegate is specifically an AppDelegate class so it will not work if you want to use this view in another application. Since the view also obtains this AppDelegate instance via a call to + sharedApplication there's no hint to users of this view that it has this dependency.
Consider instead allowing classes which use this view class to provide it with the data it should display.
If the property where addBalanceListener: stores the reference is weak, you don't need to do anything. Weak properties are automatically set to nil when the instance they are pointing to is dealloced.

Refresh a view in UINavigationController Stack

I am currently building my view in the viewDidLoad method, it adds various buttons that links through to another view. The issue I am having is that when a user clicks on a button and goes through to the other view they can purchase an IAP, then when the user clicks on the back button I would like the view to 'refresh' to show that this purchase has now become active.
How do I refresh the view?
Thanks in advance
any view can be explicitly refreshed by calling setNeedsDisplay on that view.
Further you should not mix up the terms UIViewController with UIView.
I asume you dont have a refresh problem, that you would only have in self programmed custom views. I excpect that the data of the related views was not updated.
You have a few options. Either store whether the purchase was made and then run the method that checks if a purchase was made in the viewWillAppear method of the view you are wishing to change
Or...
Setup a delegate callback that changes the button when a change is made on the purchase page (http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-custom-delegates/)
Or...
Manipluate the previous view directly by accessing it from the navigationController stack ([[self.navigationController viewControllers]objectAtIndex:YOURVIEW])
build view in viewDidLoad so you add controls only once, but refresh in viewWillShow. Keep in mind, viewWillShow will refresh it right after the build is done by viewDidLoad. So keep values that define your controls appearance on the 1st viewController, say, in delegate mutable array
e.g. create and initiate an NSMutableArray in your AppDelegate.h and .m
in .h interface
NSMutableArray *yourArray;
add property
#property (retain, nonatomic) NSMutableArray *yourArray;
in .m
#synthesyze yourArray;
initiate it in .m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
yourArray=[[NSMutableArray alloc]init];
//add initial objects to it as many as you have controls
}
in the 1st viewController
#import "AppDelegate.h"
-(void)viewWillShow:(BOOL)animated{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//pull objects from array and apply to your controls, say you have a string as a 1st obj
NSString *tmpStr=[appDelegate.yourArray objectAtIndex:0];//use it some where, say button's title
//etc
}
you will change those values in the second viewController according to users choices
#import "AppDelegate.h"
somewhere in 2nd viewController code update values in yourArray say on a
-(void)viewWillDisappear:(BOOL)animated{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//run a loop and update all objects
[appDelegate.yourArray replaceObjectAtIndex:i withObject:newObj];
}
and when you go back to 1st viewController, viewWillShow will refresh them from your delegate array.

iOS - Calling App Delegate method from ViewController

What I am trying to do is click a button (that was created in code) and have it call up a different view controller then have it run a function in the new view controller.
I know it could be done relatively easily in IB but that isn't an option.
An example of what I want to do would be if you had two view controllers one with a splash screen of house. The other view controller had a walk through of the house on it that you could go through all the rooms in a set order. The splash screen would have buttons for each room that would allow you to jump to any point on the walk through.
You can access the delegate like this:
MainClass *appDelegate = (MainClass *)[[UIApplication sharedApplication] delegate];
Replace MainClass with the name of your application class.
Then, provided you have a property for the other view controller, you can call something like:
[appDelegate.viewController someMethod];
And if anyone is wondering how to do this in swift:
if let myDelegate = UIApplication.sharedApplication().delegate as? AppDelegate {
myDelegate.someMethod()
}
Sounds like you just need a UINavigationController setup?
You can get the AppDelegate anywhere in the program via
YourAppDelegateName* blah = (YourAppDelegateName*)[[UIApplication sharedApplication]delegate];
In your app delegate you should have your navigation controller setup, either via IB or in code.
In code, assuming you've created your 'House overview' viewcontroller already it would be something like this in your AppDelegate didFinishLaunchingWithOptions...
self.m_window = [[[UIWindow alloc]initWithFrame:[[UIScreen mainScreen]bounds] autorelease];
self.m_navigationController = [[[UINavigationController alloc]initWithRootViewController:homeViewController]autorelease];
[m_window addSubview:self.m_navigationController.view];
After this you just need a viewcontroller per 'room' and invoke the following when a button click event is picked up...
YourAppDelegateName* blah = (YourAppDelegateName*)[[UIApplication sharedApplication]delegate];
[blah.m_navigationController pushViewController:newRoomViewController animated:YES];
I've not tested the above code so forgive any syntax errors but hope the pseudo code is of help...
This is how I do it.
[[[UIApplication sharedApplication] delegate] performSelector:#selector(nameofMethod)];
Dont forget to import.
#import "AppDelegate.h"
Just Follow these steps
1.import your app delegate in your class where you want app delegate object.
#import "YourAppDelegate.h"
2.inside your class create an instance of your app delegate object(Its basically a singleton).
YourAppDelegate *appDelegate=( YourAppDelegate* )[UIApplication sharedApplication].delegate;
3.Now invoke method using selector
if([appDelegate respondsToSelector:#selector(yourMethod)]){
[appDelegate yourMethod];
}
or directly by
[appDelegate yourMethod];
for swift
let appdel : AppDelegate = UIApplication.shared.delegate as! AppDelegate
i will recommend the first one. Run and Go.
NSObject <UIApplicationDelegate> * universalAppDelegate =
( NSObject <UIApplicationDelegate> * ) [ [ UIApplication sharedApplication ] delegate ];
It avoid having to include your AppDelegate.h everywhere.
It's a simple cast that goes a long way, allowing to develop independent Controller and reuse them elsewhere without to worry about class name and so on...
Enjoy
A lot of good answers are already added. Though I want to add something which suits me most of the time.
#define kAppDelegate ((YourAppDelegate *)[[UIApplication sharedApplication] delegate]);
and thats it. Use it throughout the application just like a constant.
e.g.
[kAppDelegate methodName];
Don't forget to import yourAppDelegate.h in corresponding .pch or macros file.
If someone need the same in Xamarin (Xamarin.ios / Monotouch), this worked for me:
var myDelegate = UIApplication.SharedApplication.Delegate as AppDelegate;
(Require using UIKit;)
Update for Swift 3.0 and higher
//
// Step 1:- Create a method in AppDelegate.swift
//
func someMethodInAppDelegate() {
print("someMethodInAppDelegate called")
}
calling above method from your controller by followings
//
// Step 2:- Getting a reference to the AppDelegate & calling the require method...
//
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.someMethodInAppDelegate()
}
Output:
And in case you need access to your WatchKit extension delegate from a view controller on watchOS:
extDelegate = WKExtension.sharedExtension().delegate as WKExtensionDelegate?
You can add #define uAppDelegate (AppDelegate *)[[UIApplication sharedApplication] delegate] in your project's Prefix.pch file and then call any method of your AppDelegate in any UIViewController with the below code.
[uAppDelegate showLoginView];
In 2020, iOS13, xCode 11.3. What is working for Objective-C is:
#import "AppDelegate.h"
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
It's working for me, i hope the same to you! :D
Even if technically feasible, is NOT a good approach.
When You say: "The splash screen would have buttons for each room that would allow you to jump to any point on the walk through."
So you want to pass through appdelegate to call these controllers via tohc events on buttons?
This approach does not follow Apple guidelines and has a lot of drawbacks.

Resources