I want to make a class registry class that automatically makes a UITableViewCell and when the user presses the button it'll go to that class and run the code.
Currently to do something like that, in the view controller I have to do something like this
Function1Class *function = [[Function1Class alloc] init];
function.view.backgroundColor = [self veryLightGrayColor];
[self.navigationController pushViewController: menu animated: YES];
Then in the data source I make a new case in cellForRowAtIndexPath, and in the delegate I make a case in didSelectRowAtIndexPath for a one line method call.
I'd like to change the code so anyone that wants to add a new class to this page can just do it at one place, something like:
Function1 *function;
[self addClass:function];
And in addClass the "function" passed in will be stored in an array
For the cellForRowAtIndexPath and didSelectRowAtIndexPath cases they can easily call the right methods by storing in an array.
The problem is how do I automate the initialization of each class object passed into addClass?
I could get the user to write code like this to add in their class
Function1 *function = [[Function1 alloc] init];
[self addClass:function];
Then the class object is already initialized, but that means during startup EVERY class object is getting initialized, which is a problem if there is a lot of classes. How can I take out a class object from the array, and then initialize it to use it? The class objects are all different classes, so I can't hardcode arrayOfClassObjects[i] = [[Function1 alloc] init];
Thanks
Classes are objects, so you can store the classes in an array:
NSArray *functions = #[ [Function1 class],
[Function2 class] ];
Then, when you want to instantiate one, you make it indirectly:
id instance = [[functions[0] alloc] init];
So your addClass: should be something like:
- (void)addClass:(Class)cls {
[self.classList addObject:cls];
}
Related
I'm trying to write code that'll simplify the addition of new functions in an internal tool build. There is a UITableView with each cell being a button for a function that'll segue into another view.
Each function is in its own class file. Currently, this is the way I'm doing it.
In the view controller, I initialize the class in a method, then I segue into its view controller
- (void)resetButtonTouched {
[ResetClass *reset = [[ResetClass alloc] initWithNumber num];
[self.navigationController pushViewController: reset animated: YES];
}
Then for cellForRowAtIndexPath I create the cell
cell.textLabel.text = #"Reset";
Then in the didSelectRowAtIndexPath I create the button response
[_viewController resetButtonTouched];
And I do that for the 10 other functions, so
[Function2 *function2 = [[Function2 alloc] initWithNumber num];
[Function3 *function3 = [[Function2 alloc] initWithNumber num];
etc...
I'd like to simplify this so I only have to register a class in one place and it'll automatically add the cell and connect the button. For cellForRowAtIndexPath and didSelectRowAtIndexPath I can just get it to look in an array based on indexPath.row. But, I need a way to store the class objects in an array and initialize them when called upon.
I'm thinking I can have every function that gets added here to have class methods that'll
return a title (for the button title in cellForRowAtIndexPath)
the class name (for NSClassFromString so I can determine the class of the class object in the array)
and a normal method to be called after initialization to do its stuff.
I want to write something where if I just write these two lines,
ResetFunction *reset;
[self registerFunction:reset];
It'll do everything I did above (create a working button that calls that class object)
I'm thinking of having an array that will store all the different class objects, call it allFunctions, and a method runFunction that'll do the initialization and segueing. Then in cellForRowAtIndexPath it'll just be
cell.textLabel.text = [allFunctions[indexPath.row] getTitle];
and in didSelectRowAtIndexPath it'll call [_viewController runFunction:allFunctions[indexPath.row]];
How would I do that? I tried creating a method with an id * parameter, which takes the class object, with the intent of storing that in the array allFunctions I'm having trouble making a method that'll store all the functions because it's giving me the "implicit conversion of a non-objective-c pointer to autoreleasing id is disallowed with ARC" when I try to call [self registerClass:reset].
What should I do?
Class objects are objects, too. Rather than id, you use a Class variable to hold a reference to a class object.
Class theClass = [ResetClass class];
You can store these in an NSMutableArray.
[someArray addObject:theClass];
You can later retrieve the class and send messages to it:
Class theClass = someArray[index];
id object = [[theClass alloc] init];
If you need to be able to query each class object for a title property, you'll need to implement on each class that you add to your array. For example, this is a class method implementing such a class property:
+ (NSString*) title
{
return #"Blah, blah, blah";
}
You could obtain it using code like:
Class theClass = someArray[index];
NSString* title = [theClass title];
// ... code which uses the title ...
I would like to know which is the best way to send parameters between viewControllers. I know there are two possibilities, pass the parameters in the public properties after the init call.
ViewController *vc = [ViewController alloc] init];
vc.propertyOne = #"whatever";
vc.propertyTwo = #"whatever2";
Or creating a new Custom init like
initWithProperty:(NSString *)prperty1 andPropertyTwo:(NSString *)property2
{
self = [super init];
if (self) {
self.propertyOne = prperty1;
self.propertyTwo = property2;
}
return self;
}
ViewController *vc = [[ViewController alloc] initWithProperty:#"whatever andPropertyTwo:#"xxxx"];
I would like to know Advantages and disadvantages of each one, and "when" and "why" is better use one of them.
You should pass parameters to a custom init method if those values are needed immediately in the implementation of the init method.
You should use properties, that are set just after calling alloc/init, if you have several properties to set and those properties aren't needed in the init method itself.
Many times the properties aren't needed until viewDidLoad (in the case of a view controller) so using properties is cleaner. You don't want to end up with an init method with a dozen parameters.
I have 2 classes which names are A and B, I have UIScrollView with pagecontroller in class A and I have a UILabel and NSMutableArray in B.
I used this event for get pagecontroller's page and i am sending number of page to classB for use array's element.
//ClassA
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat pageWidth = self.imageScrollView.frame.size.width;
int page = floor((self.imageScrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
ClassB *obj = [[ClassB alloc]init];
[obj changeDiscount:page];
}
//ClassB
- (void)viewDidLoad
{
numbers = [[NSMutableArray alloc]initWithObjects:#"15",#"25",nil];
}
-(void) changeDiscount:(int)currentPagePresentation{
NSLog(#"currentI = %i",currentPagePresentation);
_discountLabel.text = [NSString stringWithFormat:#"%# Discount",[numbers objectAtIndex:currentPagePresentation]];
}
I can call the changeDiscount method but array is coming null every time and I can't set the string to label.
What am I doing wrong?
Thanks for your answer and advice.
The reason why your _discountLabel.text's string is equal to null is because the numbers array that you are accessing has not even been initialised.
The reason why your numbers array has not been initialised is because the viewDidLoad method only gets called as the method states: WHEN the view has loaded ;)
If you want to access the array after creating an instance of your class, its best to setup the numbers array in an init method or so.
All you've done is:
//This creates a new instance of your second class B.
ClassB *obj = [[ClassB alloc]init];
//Youre trying to access the numbers array when you havent even loaded the view
//All you've done is create an instance of it and then calling a method with an empty numbers array.
[obj changeDiscount:page];
And that doesn't sit well with your existing code. Please continue to read to understand why.
You also definitely don't want to be creating new instances of your class every time your scroll view delegate method is called. I highly suggest you revisit that code and find an appropriate place for that code.
Solution
I suggest you revise the view controllers programmers guide on the apple website before doing anything else.
Follow step 1.
Follow step 1 again.
Then something you can do is:
Method 1: - lazy method In class B you could create an instance method like so:
//.h
-(void)setupArray;
//.m
-(void)setupArray{
numbers = [[NSMutableArray alloc]initWithObjects:#"15",#"25",nil];
}
//Then you can do something like this in class a
ClassB *obj = [[ClassB alloc]init];
[obj setupArray];
[obj changeDiscount:page];
Method 2: more appropriate If you want to do it in one go you can do this, and create an init method.
//.h
//in your Class B .h file you create an instance method like so:
-(void)init;
//.m file
-(id)init{
self=[super init];
if(self)
numbers = [[NSMutableArray alloc]initWithObjects:#"15",#"25",nil];
return self;
}
//Then in your class a method you can do this:
//Like before.
ClassB *obj = [[ClassB alloc]init];
[obj changeDiscount:page];
Ok, there are a few things causing issues here:
You are creating an instance of ClassB within the scope of scrollViewDidScroll of classA.
As soon as that method completes, that new object will be deallocated.
ClassB initialises the numbers array in viewDidLoad. This method will be called only when a UIViewController subclass loads it’s UIView, so ClassB must be a UIViewController subclass and you need to have presented it.
viewDidLoad is called when you first time access view property of that viewController.until then view is nil.
So your numbers array wont be initialised because you are calling changeDiscount method before viewDidLoad is executed.
So, move the initialising from viewDidLoad to init or initWithNib.
-(id)init{
self=[super init];
if(self)
numbers = [[NSMutableArray alloc]initWithObjects:#"15",#"25",nil];
return self;
}
I need some help in QuickDialog. I am using this tutorial QuickDialog but i cannot find what i would like to do in my QuickDialog.
First i have a controller A that will transfer to controller B using QuickDialog, values are in controller A. Now, my problem is how can i access the values when I'm already in controller B.
For example: i have declared QEntryElement *amountEntry = [[QEntryElement alloc] initWithTitle:#"Amount" Value:#""]; in controller A and passed this on controller B, how will i access amountEntry in controller B.
I hope i have explained it well. Please help on this.
You can access all values within the QRootElement. One way to do so is setting the key property of every QElement and afterwards fetch all key-value pairs into a NSMutableDicionary like so:
NSMutableDictionary *results = [[NSMutableDictionary alloc] init];
[fooRootElement fetchValueIntoObject:results];
You can use the onSelected completion code to trigger such action with a QButtonElement
QSection *confirmButtonSection = [[QSection alloc] init];
QButtonElement *confirmButton = [[QButtonElement alloc] initWithTitle:#"Accept"];
[fooRootElement addSection:confirmButtonSection];
[confirmButtonSection addElement:confirmButton];
[confirmButton setOnSelected:(^{[self fetchResultsAndCheckThemAndDismissControllerBMethod];})];
the button will then call the method on controller A which will leave you with a filled dictionary full of sweet information.
I have a class named SPPanelManager, which has a property of another class, named SPPanelSettingsManager. SPPanelManager has the following in it's -init method:
self.settingsManager = [[SPPanelSettingsManager alloc] init];
The purpose of SPPanelManager is to be subclassed, and the subclasses are used throughout my app. For example, there's SPGreetingManager. In the .h file of SPGreetingManager, I have declared:
#property (nonatomic, strong) SPGreetingSettingsManager *settingsManager;
which makes the settingsManager be of the correct class. The problem is that when the SPGreetingManager subclass is initialized, it calls the init method above, and initializes the settingsManager as the SPPanelSettingsManager class, rather than SPGreetingSettingsManager.
How can I get it to initialize this as the correct class for that property without having to re-write the init code in every subclass?
The super class (SPPanelManager) somehow has to know which class the concrete panel manager wants to use as a settingsManager.
Apple uses the following approach to match CALayers to UIViews:
The base class declares a class method that returns the concrete SPPanelSettingsManager subclass:
// in SPPanelManager.h
+ (Class)settingsManagerClass;
... which subclasses override to return their custom class:
// in SPGreetingManager.m
+ (Class)settingsManagerClass
{
return [SPGreetingSettingsManager class];
}
Now the superclass can instantiate the settings manager as follows:
self.settingsManager = [[[[self class] settingsManagerClass] alloc] init];
Another common solution is to use a naming convention. Just match the names of the classes: SPGreetingManager has a SPGreetingSettingsManager.
By definition each ...Manager has to have a matching ...SettingsManager.
// in SPPanelManager.m init
NSString *className = NSStringFromClass([self class]);
className = [className stringByReplacingOccurrencesOfString:#"Manager"
withString:#"SettingsManager"];
Class settingsManagerClass = NSClassFromString(className);
NSAssert(settingsManagerClass != Nil, #"no settings manager class found");
self.settingsManager = [[[settingsManagerClass settingsManagerClass] alloc] init];
The advantage is that subclasses don't have to override a common method to declare the class type. On the other hand it might seem a bit obfuscated what's going on.
Also, above code forces a one to one relationship between the classes. No settings controller could be reused.
Is SPPanelManager a class you developed? Simply give it a init with a parameter for the settings instance.