I have two view controllers A,B in storyboard with the same class CustomTableViewController for TableViewController element.
There are standard delegates for tables in this class like as:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
In the method:
- (void)viewDidLoad {// Load data for A and B controllers }
How I can rule this class for two controllers?
For example, if I open view controller A, class must be load data A, else load data B.
Problem is how to call different methods in method viewDidLoad:
- (void)viewDidLoad {
if(class A ViewController uses this){
self.response = // Do request
}
if(class b ViewController uses this){
self.response = // Do another request
}
}
There are many ways to do this. Here's one.
Add an inspectable property to CustomTableViewController, like this:
#property (nonatomic, copy) IBInspectable NSString *dataSetName;
By declaring it IBInspectable, you're telling Xcode to let you set the property directly in the storyboard when you select the controller in the document outline:
Now you can type “A” or “B” in the “Data Set Name” field. In viewDidLoad, you can check it:
- (void)viewDidLoad {
[super viewDidLoad];
if ([self.dataSetName isEqualToString:#"A"]) {
[self loadDataSetA];
} else {
[self loadDataSetB];
}
}
One solution can be have custom init for this class and have private property to store the controller like this -
1.Have a instance variable in class like this
#property (strong, nonatomic) UIViewController *controller;
-(instanceType)initWithController:(UIViewController*)controller {
self = [super init];
if(self) {
self.controller = controller;
}
return self;
}
Now in in your viewDidLoad just check your stored property before loading data like this -
- (void)viewDidLoad {
if( [self.controller isMemberOfClass:[A class]] ) {
self.response = // Do request
}
else if ([self.controller isMemberOfClass:[B class]]) {
self.response = // Do another request
}
}
Related
I'm trying to use an xib file (which is just a simple view) on my viewcontroller more than once. I can add it on my viewcontroller more than once and interact with both of them. The question is, how can i distinguish between these views to know which one i'm clicking?
For example, when i tap on my firstview, i want to print "apples" and when i tap on second view i wan to print "oranges"
Below you can see my code and here is github repo for you to play with my code: https://github.com/TimurAykutYildirim/demoView/tree/multiple-instance
ViewController.h
#import <UIKit/UIKit.h>
#import "Mini.h"
#interface ViewController : UIViewController <SelectionProtocol>
#property (weak, nonatomic) IBOutlet Mini *miniView;
#property (weak, nonatomic) IBOutlet Mini *miniView2;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.miniView.delegate = self;
self.miniView2.delegate = self;
}
-(void) isClicked {
NSLog(#"apples");
NSString * storyboardName = #"Main";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
UIViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Mini.h
#import <UIKit/UIKit.h>
#protocol SelectionProtocol;
#interface Mini : UIView
#property (nonatomic, weak) id<SelectionProtocol> delegate;
- (IBAction)btnClick:(id)sender;
#end
#protocol SelectionProtocol <NSObject>
#required
-(void) isClicked;
#end
Mini.m
#import "Mini.h"
#implementation Mini
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self load];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self load];
}
return self;
}
- (void)load {
UIView *view = [[[NSBundle bundleForClass:[self class]] loadNibNamed:#"Mini" owner:self options:nil] firstObject];
[self addSubview:view];
view.frame = self.bounds;
}
- (IBAction)btnClick:(id)sender {
if ([self.delegate conformsToProtocol:#protocol(SelectionProtocol)]) {
[self.delegate isClicked];
}
}
#end
There are lots of ways that you could do that. Here are two:
Add a sender parameter to your -[ViewController isClicked] method, and change Mini so that calls to -isClicked pass in a pointer to self. Then the code in -isClicked can compare that to each of the instances of Mini that it knows about, i.e. self.miniView and self.miniView2, to see if either of those is the one that sent the message.
Add a property to Mini that lets you distinguish between the two, e.g. name. You can configure that property in -viewDidLoad, like self.miniView.name = #"apples", or you can even do it in the .xib file using "user defined runtime attributes." Then you can have Mini pass it's name property as a parameter to methods that need to know which instance of Mini is the caller. (Or, combine 1 & 2 and pass a reference to self so that ViewController can examine the name parameter or anything else it wants.)
I'm assuming from your question that somewhere in the Mini xib there is an outlet with the text "apples" (or whichever fruit for that particular xib).
In that case, you can just change your protocol to:
- (void)isClickedFromView:(Mini *)mini
In the delegate (ViewController.m) change the btnClick action to:
- (IBAction)btnClick:(id)sender {
if ([self.delegate conformsToProtocol:#protocol(SelectionProtocol)]) {
[self.delegate isClickedFromView:self];
}
}
Add an outlet like fruitLabel to your Mini class.
#property (weak, nonatomic) IBOutlet UILabel *fruitLabel
Now when the delegate gets the call you can call:
NSLog(#"Fruit: %#", mini.fruitLabel.text);
===== Additional answer for if the data (in this case fruits) is available in code =====
If you have the data programmatically already (like an array of fruits), It might be easier to just put the Mini classes in an array ordered the same way.
So if miniViewArray contains an array of your Mini classes,
and fruitArray contains an array of NSStrings of fruits you can do:
At the time you set the miniView's delegate you can add them to the array..something like:
NSArray *fruitArray = #[ #"apples", #"oranges" ];
NSArray *miniViewArray = #[ miniView, miniView2 ];
Then in the delegate call you can do (Using the same protocol change as above):
- (void)isClickedFromView:(Mini *)mini {
NSInteger fruitIndex = [miniViewArray indexOfObject:mini];
NSString fruitName = fruitArray[fruitIndex];
NSLog(#"Fruit: %#", fruitName);
}
I use ENUM for distinguish between the view type.
typedef NS_ENUM(NSUInteger, <#MyViewType#>) {
<#MyViewTypeDefault#>,
<#MyViewTypeA#>,
<#MyViewTypeB#>,
};
#property (nonatomic, assign) MyEnum viewType;
Protocol:
-(void) isClickedForViewType:(MyEnum)viewType;
Common approach for calling delegate method is to include caller as a param. In your case it should be something like this:
- (IBAction)btnClick:(id)sender {
if ([self.delegate conformsToProtocol:#protocol(SelectionProtocol)]) {
[self.delegate isClickedOnMiniView:self];
}
}
Basically, this convention was created to cover such cases - one object is a delegate of many similar ones.
Check apple docs for examples of this convention:
https://developer.apple.com/documentation/uikit/uitableviewdelegate
I have been creating a cocoa static library in which I have a public nsobject file where I created a custom delegate. In the app I imported the nsobject file and implemented the delegate but the delegate is not getting called... the static library name is glamApi.
the SKUIDPasser.h file of the NSObject in the library
#import <Foundation/Foundation.h>
#protocol SubClassDelegate <NSObject>
#required
- (void)MethodNameToCallBack:(NSString *)s;
#end
#interface SKUIDPasser : NSObject
-(void)getSKUIDsFromCart:(NSString *)SKUIDs;
#property (nonatomic, weak) id <SubClassDelegate> delegatePasser;
#end
and the SKUIDPasser.m file
#import "SKUIDPasser.h"
#implementation SKUIDPasser
#synthesize delegatePasser;
-(void)getSKUIDsFromCart:(NSString *)SKUIDs{
NSLog(#"getSKUIDsFromCart %#",SKUIDs);
[delegatePasser MethodNameToCallBack:SKUIDs];
}
#end
And the method is called from a Viewcontroller in static library
- (IBAction)CartShowEvent:(id)sender {
if (![cartBadge isHidden]) {
buyClicked = TRUE;
[self loadCart];
[self showCartItemsAll];
self.cartView.frame = self.view.bounds;
[self.view addSubview:self.cartView];
SKUIDPasser *pass = [[SKUIDPasser alloc] init];
[pass getSKUIDsFromCart:#"sssss"];
} else {
[Utilities alert:#"No products to display !!!"];
}
}
The Viewcontroller which the custom delegate has to be implemented Viewcontroller.h
#import <glamAPI/SKUIDPasser.h>
#interface ViewController : UIViewController<SubClassDelegate>{
SKUIDPasser *sk;
}
Viewcontroller.m
- (void)viewDidLoad {
[super viewDidLoad];
sk = [[SKUIDPasser alloc] init];
sk.delegatePasser = self;
NSLog(#"sk.delegatePasser %#",sk.delegatePasser);
}
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"MethodNameToCallBack %#",s);
}
I didn't get any error but the method is not calling..Please help me to resolve this
The very first thing you need to understand is that each instance object of a class is entirely different entity and maintains it's state separately.
In you case your have created an object of your static library in viewDidLoad: and set the delegate accordingly, but when you are making the call to method getSKUIDsFromCart, you are using a different instance for which you never set the delegate property. That's why there was no callback.
To solve this, you can set the delegate in method CartShowEvent: before making the call, something like this
SKUIDPasser *pass = [[SKUIDPasser alloc] init];
pass.delegatePasser = self;
[pass getSKUIDsFromCart:#"sssss"];
However i would suggest that you should use the instance variable of library which you already created in viewDidLoad:
- (IBAction)CartShowEvent:(id)sender {
if (![cartBadge isHidden]) {
buyClicked = TRUE;
[self loadCart];
[self showCartItemsAll];
self.cartView.frame = self.view.bounds;
[self.view addSubview:self.cartView];
//No need to create another object.
//SKUIDPasser *pass = [[SKUIDPasser alloc] init];
//Use the previously created instance object
[sk getSKUIDsFromCart:#"sssss"];
}
else {
[Utilities alert:#"No products to display !!!"];
}
}
The SKUIDPasser object that you are calling within (IBAction)CartShowEvent:(id)sender and the SKUIDPasser object that you are setting the delegate are NOT the same.
Just for a test, try calling the method [sk getSKUIDsFromCart:#"sssss"]; just after you set the delegate and you will see that it will be called because this instance has the delegate set correctly:
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
sk = [[SKUIDPasser alloc] init];
sk.delegatePasser = self;
[sk getSKUIDsFromCart:#"sssss"];
NSLog(#"sk.delegatePasser %#",sk.delegatePasser);
}
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"MethodNameToCallBack %#",s);
}
Update
I updated my answer to help you call the trigger from the static library
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
sk = [[SKUIDPasser alloc] init];
sk.delegatePasser = self;
/*
You now can pass this variable to the static library to get called
from there ...
example:
viewControllerOnStaticLibrary.passer = sk;
*/
NSLog(#"sk.delegatePasser %#",sk.delegatePasser);
}
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"MethodNameToCallBack %#",s);
}
Viewcontroller_in_static_library.h
#property (nonatomic, strong) SKUIDPasser *passer;
Viewcontroller_in_static_library.m
- (IBAction)CartShowEvent:(id)sender {
if (![cartBadge isHidden]) {
buyClicked = TRUE;
[self loadCart];
[self showCartItemsAll];
self.cartView.frame = self.view.bounds;
[self.view addSubview:self.cartView];
//now you are calling the same instance
[self.passer getSKUIDsFromCart:#"sssss"];
} else {
[Utilities alert:#"No products to display !!!"];
}
}
I am having a hard time communicating data between two view controllers that are inside a UISplitViewController. I am following this tutorial. I was able to create a split view controller with UITableViews on both master and detail views. Now, What I really want is that when I tap on a particular row in the master table, it has to send some value to the detail view.
I am just playing around with a custom delegate to pass some value from one view controller to another to see if there is any communication between them but nothing seems to work any way.
In MasterTableView.h
#protocol sendingProtocol <NSObject>
-(void)passSomeValue:(NSString *)someValue;
#end
#interface MasterTableView : UITableViewController
{
NSArray *menuArray;
id<sendingProtocol>delegate;
}
#property (nonatomic,assign) id<sendingProtocol>mydelegate;
#end
Synthesized in .m file.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[self mydelegate] passSomeValue:#"Some Value"];
}
In DetailTableView.h
-(void)passSomeValue:(NSString *)someValue
{
NSLog(#"%#", someValue);
}
Please note that I am calling the mydelegate inside the ViewDidLoad method. Is this the write way? Can someone help?
- (void)viewDidLoad
{
[super viewDidLoad];
MasterTableView *masterView = [[MasterTableView alloc] init];
masterView.mydelegate = self;
}
Thank you in advance!
In viewDidLoad method of your DetailTableView you should not create a new MasterTableView object. The error is here in this method:
- (void)viewDidLoad
{
[super viewDidLoad];
MasterTableView *masterView = [[MasterTableView alloc] init];
masterView.mydelegate = self;
}
You are creating another object of MasterTableView and setting its delegate to self and hence all the problem.
To set the delegate of MasterTableView to DetailTableView, go to AppDelegate.h. You must have defined the MasterTableView and DetailTableView objetcs in AppDelegate.
//Set the DetailTableView as the master's delegate.
self.masterTableView.delegate = self.detailTabelView;
I have two UIViews. One UIView, called SelectText has an ivar of NSMutableArray which is populated after performing a certain function.
Here is a snippet code:
- (void)fillDrawPoints
{
//the codes....
[self.drawnPoints addObject:[NSValue valueWithCGPoint:currPoint]];
}
NOTE: The drawnPoints array is initialized in the initWithFrame of SelectText. Also, I always check if the array is actually populated inside the view by putting a log in the function.
Now what I want to do is to access this array from another view. This is what I do:
TextView.h
#import "SelectText.h"
#interface TextView : UIView
{
SelectText *txtSel;
}
#property (nonatomic, retain) SelectText *txtSel;
TextView.m
#synthesize txtSel;
- (void)getDrawingPoints:(NSMutableArray *)pointArray
{
self.pointArray = pointArray;
NSLog(#"Array count: %d", [self.pointArray count]);
}
As you can see from the above code, I am trying to pass the data inside txtSel.drawnPoints to the textView.pointArray for later use. The problem is, the txtSel.drawnPoints always returns empty when I try to access it from another view. What am I doing wrong here?
ADDITIONAL:
This is how I instatiate SelectText
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
txtSel = [[SelectText alloc]init];
[self addSubView:txtSel];
//rest of code...
}
change TextView class init method as below
- (id)initWithClassSelectText:(SelectText *)selectText {
if ((self = [super init])) {
txtSel = selectText;
}
return self;
}
And when you make an instance of TextView you use this:
TextView textView = [[TextView alloc] initWithClassSelectText:self];
now you can access all properties of SelectText class using txtSel object
I want to load data (an array of strings) from the parent view into a set of UITextFields in the child view upon presenting the modalView.
I know how to pass from child to parent, and I'm sure it's even easier to go the other way, but I don't know how.
UPDATE: Update removed because I found the problem (double releasing of modal view)
Override the init method for the child view controller.
- (id) initWithStrings:(NSArray *)string {
if (self = [super init]) {
// Do stuff....
}
return self;
}
Then in the parent:
MyChildViewController *vc = [[[MyChildViewController alloc] initWithStrings: strings] autorelease];
Two ways you could do it:
1.Override the init method as Matt suggests
2.Create fields in your child class and pass those values to your text field.
#interface ChildViewController : UIViewController{
NSArray *strings;
UITextfield *textField1;
UITextfield *textField2;
}
...
- (void)viewDidLoad {
[super viewDidLoad];
textField1.text = [strings objectAtIndex:0];
textField2.text = [strings objectAtIndex:1];
}
Then in the parent class:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ChildViewController *childController = [[ChildViewController alloc] init];
childController.strings = your_array_of_strings;
[self.navigationController pushViewController:childController animated:YES];
[childController release];
}
- (id)initWithDataObject:(YourDataObjectClass *)dataObject {
if (self = [super init]) {
self.dataObject = dataObject;
// now you can do stuff like: self.myString = self.dataObject.someString;
// you could do stuff like that here or if it is related to view-stuff in viewDidLoad
}
return self;
}
If you want to get really fancy, you can make a delegate for your child view.
#protocol MyChildViewDelegate
- (NSArray*)getStringsForMyChildView:(MyChildView*)childView;
#end
#interface MyChildView : UIView
{
id <MyChildViewDelegate> delegate;
...
}
#property (nonatomic, assign) id <MyChildViewDelegate> delegate;
...
#end
Then somewhere in your view you would ask for the strings:
- (void)viewDidLoad
{
...
NSArray* strings = [delegate getStringsForMyChildView:self];
...
}
Then in your controller (or where ever) you can do:
myChildView = [[MyChildView alloc] initWith....];
myChildView.delegate = self;
...
- (NSArray*)getStringsForMyChildView:(MyChildView*)childView
{
return [NSArray arrayWithObjects:#"one", #"two", #"three", nil];
}
It's probably a little overkill in this case, but this is how UITableViews do it too: they have a data source delegate to provide them with their contents.