Declare an object of a class inside a condition - ios

My requirement is that I want to declare an object of a class depending upon a condition that deviceFamily is iPad or iPhone.
I have developed an application for iPhone. Now I want to develop same application for iPad. Now I want to have two different .h , .m and .xib for iPhone and iPad with different names. But I want to use the same name object of two different classes in AppDelegate class of universal application.
Something like in .h file:
if(IS_iPad)
{
ViewController_iPad * obj;
}
else
{
ViewController_iPhone * obj;
}
So the I can use the same name of the object throughout the application.
How can I achieve this. Help!

This is probably the not best approach for creating a 'Universal' app (both iPad and iPhone) but for all of the instances I've seen adopting this model they seem to take the logic you have down a chain in regards to controllers...
UIViewController *sharedController = [[UIViewController alloc]init]; // things that are shared between both devices go in here (write once no duplication)
if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) // PAD
{
ViewController_iPad * obj;
}
else if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) // PHONE
{
ViewController_iPhone * obj;
}
Both of the view controller above should either include sharedController as a property to make use of the code in there OR better...
Make sharedController the 'base class'. Then make both ViewController_iPad and ViewController_iPhone sub classes of this class (shared).
To do this, in both ViewController_iPhone and ViewController_iPad's .h header files, set them as subclasses of sharedController like this.
#interface ViewController_iPad : sharedController
Its one approach if you really have to do it this way.

I would look into class clusters as explained at the iOS7 Tech Talks. Look at the end of the session "Architecting Modern Apps, Part 2", in the slides it starts at page 120.
Essentially, you can hide the exact implementation of a class from other classes that use it. In the image below, the app uses a class "Download Controller" but does not need to know if the OS is capable of NSURLSession or needs to default to NSURLConnection. I think this kind of pattern would work well with your problem.
The code for two such implementations would look something like this:
#interface MyViewController_iPhone : MyViewController #end
#interface MyViewController_iPad : MyViewController #end
#implementation MyViewController
+ (instancetype)newViewController {
if (IS_iPad) {
return [[MyViewController_iPad alloc] init];
} else {
return [[MyViewController_iPhone alloc] init];
}
}
...
#end

Maybe I understand now what you want to achieve: You want to define alternatively different objects, but access them outside the declaration scope by the same name. If so, what about:
#property (strong) UIViewController *obj;
...
if(IS_iPad)
{
obj = [[ViewController_iPad alloc] initWithNibName:nibName bundle:nil];
}
else
{
obj = [[ViewController_iPhone alloc] initWithNibName:nibName bundle:nil];
}
I assume both classes are subclasses of UIViewController, so the assignment would be OK. Of course one had to use the proper initialization for the view controllers.

Related

Objective-C Protocol Conformance Using Subclass in Method Signature

I'm probably missing something here as this seems like a pretty basic Objective-C question, but not finding any similar questions (maybe I'm searching the wrong thing, sorry in advance if so!). My question is around enforcing a class that conforms to a protocol to have the correct type in it's method signatures. Since that doesn't seem to be enforced by the compiler, what's the best practice here.
Let me provide an example.
// View.h
#protocol View <NSObject>
- (void)createNewViewUsing:(UIView *)view;
#end
// SomeView.h
#interface SomeView : NSObject <View>
#end
- (void)createNewViewUsing:(UIStackView *)view {
NSLog(#"The view is %#", view);
NSLog(#"%#", view.arrangedSubviews); // CRASH!
}
// Implementation
SomeView *const view = [[SomeView alloc] init];
UIView *const anotherView = [[UIView alloc] init];
[view createNewViewUsing:anotherView];
How do I avoid this crash? Is the correct thing to do to just make sure I'm always matching the method signature as my protocol and then check the type? How do I ensure someone else (or my future self) does this? Why does the compiler not enforce this?
In Objective-C you have to keep track of it yourself. So you are totally allowed to do
- (void)createNewViewUsing:(UIStackView *)view
with the understanding that you will then only message this with stackviews and not with views. If you deviate from this you do so at your own risk.
However, a better approach is to stick to the original protocol with something as below
- (void)createNewViewUsing:(UIView *)view {
if ( [view isKindOfClass:UIStackView.class] )
{
// Cast it
UIStackView * sv = view;
// Now you are safe ... use sv in what follows
}
else // ... not a stackview ... maybe pass to parent class
This of course adds a lot of overhead to your code but you may totally need it for e.g. a large project. You are the one making the judgement call here.

Accessing UI image from another class

I'm programming an application for road safety, and there will be a lots of code, so what I thought is I will divide each function into a class.
Now I'm trying to set the background of the main ViewController From another class, however it doesn't work. This is the code that I tried:
-(void)StartBackgroundAnimation
{
ViewController *MainRoot = [[ViewController alloc] init];
MainRoot.BackgroundViewer.image = [UIImage imageNamed:#"Lunch_Background.png"];
NSLog(#"ok");
}
What I did is that I created a function called StartBackgroundAnimation, and I imported the main view-controller class, and I created an object from it. After that, I imported this class to the main view-controller, and I called this function but the image doesn't work.
Note: NSLogworks perfectly and the function is called however the image doesn't change.
If you really want to store functions in different classes just return a UIImage and assign it in the view controller.
So inside your custom class.
- (UIImage*)backgroundImage{
return [UIImage imageNamed:#"Lunch_Background.png"];
}
Inside of your main view controller:
self.view.backgroundView.image = [instanceofyourclass backgroundImage];
If you'd like to access data from a class outside of a class, it is usually best in Objective-C to use a property.
// MyClass.h
#interface MyClass : Superclass
#property (strong, nonatomic) UIImage * backgroundImage;
#end
This way, you can read/write the property by using self.backgroundImage from within the class and anotherClass.backgroundImage from outside of it.
Edit: spelling...

How to make settings inheritance

In my XCode project, I want some default setup settings, which basically is a set of variables like GlobalTintColor, ServerUrl and so forth.
I then need to override some of these settings per client/target.
These settings are only for interval use, which means I'm not looking for settings bundle type solution.
I don't want to have duplicate settings, so some sort of inheritance seems to be the right way to go.
I was thinking I'd make a parent class carrying all the default settings, and then a subclass for each client, overriding settings as needed. I just can't figure out how I'm going to load these settings. I figured only the clients that needed to override settings had a subclass. Other clients just used the default settings as defined by the parent class.
But when I'm loading the settings at application start, I then need to check if the subclass is available, and if not, I only load the super class.
But then I get the problem of what kind of class the settings are: subclass or superclass?
I've been looking into categories as well as class clustering, but haven't found a solution so far.
Seems to me this is functionality a lot of app developers need. Does any of you know of a good pattern to solve this?
To illustrate:
- (id) getAppConfigurationSettings {
id settings;
if ([AppConfigurationSettings class]) {
settings = [AppConfigurationSettings class];
} else {
settings = [DefaultAppConfigurationSettings class];
}
return settings;
}
Do you want something like this ?
"Parent.h"
#interface Parent : NSObject
#property(nonatomic,strong)UIColor *color;
#end
"Parent.m"
#import "Parent.h"
#implementation Parent
-(void)setColor:(UIColor *)color{
self.color=color;
}
#end
Then you create another class which will inherit Parent say Child
Child.h
#import "Parent.h"
#interface Child : Parent
#end
Child.m
#import "Child.h"
#implementation Child
//Override the actual color
-(void)setColor:(UIColor *)color{
self.color=color;
}
#end
Then you can use it like below
Parent *parent=[[Parent alloc] init];
[parent setColor:[UIColor redColor]];
Child *child=[[Child alloc] init];
[Child setColor:[UIColor blueColor]];
I hope it will give you enough idea..
Updated
For custom initialization you can create some enum, and do your initializations accordingly like below
typedef enum {
kParent = 1,
kChild = 2
}kSettings;
-(void)updateColor:(kSettings)settingType{
id classObj;
switch (settingType) {
case kParent:
classObj=[[Parent alloc] init];
break;
case kChild:
classObj=[[Child alloc] init];
break;
default:
break;
}
[classObj setColor:[UIColor redColor]];
}
Note - The above code is not tested may not be completely correct, but can be like this.
When I hear about "base" and "override", I immediately think of a hierarchy of classes, so #iphonic answer does the job pretty well, although I would design it in a slightly different way:
"BaseSettings"
#interface BaseSettings : NSObject
... properties
#end
#implementation BaseSettings
- (instancetype) init {
self = [super init];
if (self) {
[self constantInit];
[self dynamicInit];
}
}
// Put here initialization that won't be overridden
// in inherited classes
- (void) constantInit {
}
// Put here initialization that will be overridden
// in inherited classes
- (void) dynamicInit {
}
#end
"SettingsInheritor"
#interface SettingsInheritor : BaseSettings
#end
#implementation SettingsInheritor
- (void) dynamicInit {
// Call base method so that not overriden settings
// are still initialized properly
[super dynamicInit];
// Override settings here
...
}
The constantInit method is for convenience only, to let you visually separate constant from overrideable settings - so you can get rid of it if you won't need or like it.
What can be improved in #iphonic's answer is how the actual settings class is instantiated, so I propose a different approach.
As described here, you can use obj_getClassList() to obtain the list of all registered class definitions - then you can loop through all of them, and check if its superclass is BaseSettings (or whatever you want to call the base settings class), using class_getSuperClass() or isSubclassOfClass:. Note: the latter method returns YES if subclass or identical, something to take into account when comparing.
Once you find a class inheriting from BaseSettings, you can break the loop and instantiate the found class (for instance using class_createInstance()). A (untested) skeleton is like this:
int numClasses = objc_getClassList(NULL, 0);
if (numClasses > 0) {
BOOL found = NO;
Class settingsClass;
Class *classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
for (int index = 0; index < numClasses; ++index) {
Class curClass = classes[index];
Class superClass = class_getSuperclass(curClass);
const char *superClassName = class_getName(superClass);
if (strcmp(superClassName, "BaseSettings") == 0) {
settingsClass = curClass;
found = YES;
break;
}
}
if (found) {
// Create the class instance from `settingsClass`
}
free(classes);
}
Credits to Ole Begemann for (most of) the above code

Trying to store values received in a ViewController and store them in a single instance of an NSMutableArray in another View Controller in iOS

I have an application where A View Controller (A)is called twice in close succession. Now each time it is called, an NSString object is created, and I need this value to be stored in an NSMutableArray that is a public property of ANOTHER View Controller (B).
In A, I create an instance of the second View Controller (B), and using that instance, add the NSString objects into the NSMutableArray which I've created as a public property. Later, when I am inside View Controller B and print the contents of the NSMutableArray property, the array is empty. Why? Here is the code that is inside View Controller A:
-(void)viewDidLoad {
ViewControllerA *aVC = [[ViewControllerA alloc] init];
if (aVC.stringArray == nil) {
aVC.stringArray = [[NSMutableArray alloc] init];
}
[aVC.stringArray addObject:#"hello"];
[aVC.stringArray addObject:#"world"];
for (NSString *wow in aVC.stringArray) {
NSLog(#"The output is: %#", wow);
}
}
Inside my View Controller B class, I have the following code:
- (IBAction)buttonAction:(UIButton *)sender {
NSLog(#"Button selected");
for (NSString *test in self.stringArray) {
NSLog(#"Here are the contents of the array %#", test);
}
}
Now the buttonAction method gets called, as I do see the line Button selected in the system output, but nothing else is printed. Why? One thing I want to ensure is that View Controller A is called twice, which means I would like to see in the output, "Hello World", "Hello World" (i.e. printed twice), and not "Hello World" printed just once.
The other thing I wish to point out is that View Controller B may not be called at all, or it may be called at a later point in time. In any case, whenever View Controller B is called, I would like to have the values inside the array available, and waiting for the user to access. How do I do this?
Your approach is not ideal, potentially leading to a memory cycle, with two objects holding strong pointers to each other.
You can instead achieve your goal in two ways;
Delegate Protocol
This method allows you to set delegates and delegate methods to pass data back and forth between view controllers
in viewControllerA.h
#protocol viewControllerADelegate <NSObject>
- (void)addStringToNSMutableArray:(NSString *)text;
#end
#interface viewControllerA : UIViewController
#property (nonatomic, weak) id <viewControllerADelegate> delegate;
in viewControllerB.m
// create viewControllerA class object
[self.viewControllerA.delegate = self];
- (void)addStringToNSMutableArray:(NSString *)text
{
[self.mutableArray addObject:text];
}
in viewControllerA.m
[self.delegate addStringToNSMutableArray:#"some text"];
Utility Classes
Alternatively you can use a utility class with publicly accessible methods (and temporary data storage). This allows both viewController classes to access a shared data store, also if you use class methods, you don't even need to instantiate the utility class.
in XYZUtilities.h
#import <Foundation/Foundation.h>
#interface XYZUtilities : NSObject
+ (void)addStringToNSMutableArray;
#property (strong, nonatomic) NSMutableArray *array;
#end
in XYZUtilities.m
+ (void)addStringToNSMutableArray
{
NSString *result = #"some text";
[self.array addObject:result];
}
+ (NSArray)getArrayContents
{
return self.array;
}
in viewControllerA.m
NSString *stringFromObject = [XYZUtilities addStringToNSMutableArray];
in viewControllerB.m
self.mutableArray = [[NSMutableArray alloc] initWithArray:[XYZUtilities getArrayContents]];
I'm not sure what kind of a design pattern you are trying to follow but from the looks of it IMHO that's not a very safe one. However, there are many, many ways this could be accomplished.
One thing though, you said that View Controller B may never get allocated and if it is alloc-ed, it will be down the road. So you can't set a value/property on an object that's never been created.
Since you already aren't really following traditional patterns, you could make a static NSMutableArray variable that is declared in the .m of your View Controller B Class and then expose it via class methods.
So it would look like this:
viewControllerB.h
+(void)addStringToPublicArray:(NSString *)string;
viewContrllerB.m
static NSMutableArray *publicStrings = nil;
+(void)addStringToPublicArray:(NSString *)string{
if (publicStrings == nil){
publicStrings = [[NSMutableArray alloc]init];
}
if (string != nil){
[publicStrings addObject:string];
}
}
Then it would be truly public. All instances of view controller B will have access to it. This, of course is not a traditional or recommended way of doing it—I'm sure that you will have many replies pointing that out ;).
Another idea would be to use a singleton class and store the values in there. Then, when or if view controller B is ever created, you can access them from there.

Pass data to segue destination without iVar

Since switching to storyboards, I load a view controller via
[self performSegueWithIdentifier:#"identifier" sender:self]
This works perfectly. Now, if I want to set any properties on the destination view controllers, I implement the method prepareForSegue:sender: and set what properties I need to set. Everything works as expected without any problems.
Ever since I starting using this approach over the old
MyViewController *vc = ....
vc.prop = #"value";
[self.navigationController pushViewController:vc];
I've felt that passing parameters to the destination view controller is a little hacky, in particular if the value you're trying to set is not just a static value.
Lets say for example, I have a button which fetches some data from a server. When the data returns, it creates a new object, and then presents a new view controller to display this object. To do this, I call performSegueWithIdentifier:sender:, but that's the end of it. My object is now deallocated and no longer exists, and I have no way of passing it to the prepareForSegue:sender: method, unless I store it in an instance variable.
This feels pretty horrible, as the object isn't meant to last longer than this action, and has no relation to anything else in my current view controller.
In this situation, I understand that I could quite simply request the data in the new view controller but it's just an example.
My question is, is there another way of doing this without it feeling so hacky? Can I get this data into the destination view controller without storing it in an instance variable?
I know I could still use the old approach, but I'd like to stick with the storyboard methods if I can.
Well the sender parameter of the performSegueWithIdentifier:sender is the same one received by the prepareForSegue:sender. So if you want to send a variable to your prepareForSegue:sender the sender is your friend. In your case:
SomeViewController.m
-(void)aMethodThatDownloadsSomeDataFromServer {
NSString *exampleData = [self someDataThatIDownloaded];
[self performSegueWithIdentifier:#"yourSegueIdentifier" sender:exampleData];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if(segue.identifier isEqualToString:#"yourSegueIdentifier"]) {
if([sender isKindOfClass:[NSString class]]) { //maybe you want to send different objects
segue.destinationViewController.stringProperty = sender;
}
else {
segue.destinationViewController.objectPorperty = sender;
}
}
}
The accepted solutios is correct but I frequently use another approach when data are shared between more than two segue. I frequently create a singleton class (let's call it APPSession) and I use it as a datamodel, creating and maintaining a session-like structure I can write and read from everywhere in the code.
For complex applications this solution maybe requires too much error prone coding but I've used it succesfully in a lot of different occasions.
APPSession.m
//
// APPSession.m
//
// Created by Luca Adamo on 09/07/12.
// Copyright 2012 ELbuild. All rights reserved.
//
#import "APPSession.h"
#implementation APPSession
#synthesize myProperty;
static APPSession *instance = nil;
// Get the shared instance and create it if necessary.
+ (APPSession *)instance {
if (instance == nil) {
instance = [[super allocWithZone:NULL] init];
}
return instance;
}
// Private init, it will be called once the first time the singleton is created
- (id)init
{
self = [super init];
if (self) {
// Standard init code goes here
}
return self;
}
// This will never be called since the singleton will survive until the app is finished. We keep it for coherence.
-(void)dealloc
{
}
// Avoid new allocations
+ (id)allocWithZone:(NSZone*)zone {
return [self sharedInstance];
}
// Avoid to create multiple copies of the singleton.
- (id)copyWithZone:(NSZone *)zone {
return self;
}
APPSession.h
//
// APPSession.h
//
// Created by Luca Adamo on 09/07/12.
// Copyright 2012 ELbuild. All rights reserved.
//
#import <Foundation/Foundation.h>
#interface APPSession : NSObject{
}
#property(nonatomic,retain) NSString* myProperty;
+ (id)sharedInstance;
#end
How to read and write the property myProperty from every part of the app code.
// How to write "MyValue" to myProperty NSString *
[APPSession instance] setMyProperty:#"myValue"]
// How to read myProperty
NSString * myVCNewProperty = [[APPSession instance] myProperty];
With this mechanism I can safely write for instance a value in the APPSession in the first ViewController, perform a segue to a second one, perform another segue to a third one and use the variable written during the first segue.
It's more or less like a SessionScoped JavaBean in Java EE. Please feel free to point out problems in this approach.
All of these answers are correct, but I've found a pretty cool way of doing this. I've tested only in iOS 7 and iOS 8
After declaring and setting the value of the object you wish to pass, in the prepareForSegue method,
[segue.destinationViewController setValue:event forKey:#"property"];
//write your property name instead of "property

Resources