iOS - set UIButton target and action from its super class - ios

I have 2 classes
ClassA: UIView
ClassB: UIViewController
I have a UIButton in ClassA
The button is declared in the header and as an #property of A
UIButton *selectBtn;
}
#property (nonatomic, strong) UIButton *selectBtn;
in the .m file of Class A the button is initialized and shows up on screen
ClassB imports ClassA's header file and from it I'm trying to set the target and the action for my button with the following
[ClassA.selectBtn addTarget:self action:#selector(getSomething:) forControlEvents:UIControlEventTouchUpInside];
But i keep getting an Error message saying
"Property "selectBtn" not found on object of type "ClassA"
What am I doing wrong ?
any help or guidance is very appreciated

[ClassA.selectPicBtn addTarget:self action:#selector(getSomething:) forControlEvents:UIControlEventTouchUpInside];
use selectPIcBtn instead of selectBtn

I don't know if I'm missing something because none of the other posters brought this up but from what I see selectBtn is a property of a ClassA instance, not a property of the class itself. Try grabbing an instance of a ClassA object and then accessing the property from that instance:
//WRONG
[ClassA.selectBtn addTarget:self action:#selector(getSomething:) forControlEvents:UIControlEventTouchUpInside];
//Correct
ClassA *aClassAObject = [[ClassA alloc] init];
[aClassAObject.selectBtn addTarget:self action:#selector(getSomething:) forControlEvents:UIControlEventTouchUpInside];
Please keep in mind, the above code is just an example and is not likely the object you are looking for. If you are doing this inside the ClassB object you have to grab the ClassA UIView that is actively being used by the ClassB UIViewController. Typically, I would create a property in ClassB to access the ClassA object similar to the way you created a property for a UIButton object but I'm not sure how you have your classes setup without seeing the header files.

Problem is here.
UIButton *selectBtn;
}
#property (nonatomic, strong) UIButton *selectPicBtn;
selectBtn and selectPicBtn are different. property name should be same as variable name. If you have declared a property then you don't need variable declaration.
Only declare property and use it. While initializing, initialize self.selectPicBtn = [UIButton.... in Class A

Related

How to override the getter method of a super class's private property

Usually, we can use the following code to set a getter method for a property.
- (UIButton *)aButton {
if (!_aButton) {
_aButton = [UIButton buttonWithType:UIButtonTypeCustom];
}
return _aButton;
}
Now, the property is declared in the super class's .m file.
So.
Question: How to do that in a sub class?
Any help will be appreciated.
You can simply declare the existence of private methods in a superclass in a category header, then use them. You cannot assign a private instance variable from the superclass directly, other than using setValue:forKey: or other runtime functions like object_setIvar().
#interface Superclass (PrivateDecls)
#property (nonatomic, strong) UIButton *aButton;
#end
#implementation Subclass
...
- (UIButton *)aButton {
UIButton *button = [super aButton];
if (!button) {
button = [UIButton buttonWithType:UIButtonTypeCustom];
[super setAButton:button];
}
return button;
}
#end
However, normally such properties are privately declared for a reason. They are subject to change with each release, etc., and your code could easily break in the future. Even if just your own classes, a programmer changing the code in the superclass may not be aware that there is another class in the project overriding it, and could easily make a breaking change.
Normally the only reason to do stuff like this is for unit tests, temporary debugging code, or for two closely related classes where just the other class should know about the private/package private methods, and not any other class -- otherwise the superclass should simply declare them in the public header. Even in this latter situation, it may make sense to make a "SuperclassPrivate.h" or "SuperclassProtected.h" header to declare methods/properties used by other related classes (such as UIGestureRecognizerSubclass.h) so that certain other classes can import that header, while not being public for general use. If you do go this route, it may be best to add comments in the superclass to point out there is another class overriding the property in question.

what the difference between following situation in iOS?

i think there are two ways to add a UIControl in my view
status 1:
#property (nonatomic,weak) UIButton *button;
- (void)viewDidload
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button........ (set frame,color,text........)
[self.view addSubView:button];
_button = button;
}
status 2.
#property (nonatomic,strong) UIButton *button;
- (void)viewDidload
{
_button = [UIButton buttonWithType:UIButtonTypeCustom];
_button.......(set frame,color,text......)
[self.view addSubView:_button];
}
i want to know the difference between them and in different situation what should i chose ?
in version 1 (nonatomic, strong) the viewcontroller keeps a strong reference to the button. that means if you for example remove it from the superview somewhere in your code ([self.button removeFromSuperview];) it is still in memory and could be readded at a later point in time ([self.view addSubview:self.button];).
in version 2 (nonatomic, weak) the viewcontroller keeps a weak reference to the button. that means if you for example remove it from the superview somewhere in your code (and no other part of your app keeps a strong reference to it) it gets deallocated.
there is no real difference between creating a local variable UIButton *button = ... and assigning it to the instance variable afterwards _button = button; or working with the instance variable directly _button = [UIButton buttonWithType:....
in status 1:
you have marked you UIButton as weak Property
and A weak reference means the pointer has no owner, therefore it will be deallocated as soon as it is no longer needed (that is, nothing else is pointing to it).
but in status 2 :
you have marked you UIButton as Strong Property and when your Button hasn't mark as IBOutlet , you should use strong

UIButton addTarget in different ViewController

In my project, I have two ViewControllers - mapViewController and dataViewController.
In mapViewController, I have outlets for two buttons :
#property (weak, nonatomic) IBOutlet UIButton *previousButton;
#property (weak, nonatomic) IBOutlet UIButton *nextButton;
For fetching mapViewController in dataViewController,
self.MapViewController = ((OTPAppDelegate *)[[UIApplication sharedApplication] delegate]).mapViewController;
Using the above technique, I can manipulate the properties of mapViewController inside dataViewController by accessing self.MapViewController.property
However, if I wish to add a target for the two buttons inside dataViewController using the following code:
[self.MapViewController.previousButton addTarget:self action:#selector(doNothing:) forControlEvents:UIControlEventTouchDown];
It throws a BAD access error. I was wondering what needs to be fixed, in order to achieve the desired button click behavior.
Create a protocol in MapViewController
#Protocol prtocol_name <NSObject>{
-(void)method_name;
#end
create an object for protocol in MapViewController.
#property(nonatomic) id< prtocol_name> delegate;
in button methods implementation call protocol method like following
[self.delegate method_name];
And finally implement protocol method in DataViewController.
Thanks
If you want the target/selector in different view controller, then pass the delegate parameter as other view controller's instance. For eg:
[self.MapViewController.previousButton addTarget:otherControllerInstance action:#selector(doNothing:)
forControlEvents:UIControlEventTouchDown];
Detailed Explanation:-
You have two classes named FirstVC and SecondVC. A button is present in FirstVC, on which you want to add target in SecondVC.
[button addTarget:objSecondVC action:#selector(doSomething:)
forControlEvents:UIControlEventTouchDown];
I hope you have create property of mapViewController into dataViewController.
If you choose wrong property attributes then it can raise error you got.
Another possibility is, MapViewController property not assigned/initialized properly and it is nil while you are trying to add target of its subview.
The best way to get callback event is to use delegate.
Below are some information on how delegate works:
Delegate are function pointers. Using it, one can call another class' function easily.
To create delegate, common procedure is to, first create protocol and add relevant methods in it (in the class you want to initiate delegate method). This methods can be implemented by class that adopts protocol.
You also need to create generic property of protocol type called delegate property. This will be assigned to instance of class that conforms to protocol.
In your case, class mapViewController define some protocol in it. Here, dataViewController conforms class mapViewController's protocol.
Now, class dataViewController has object defined of class mapViewController in it. In class dataViewController, here we need to assign class mapViewController's delegate to instance of dataViewController(self). (now in class mapViewController, delegate property contains instance of dataViewController and one can easily call protocol method implemented in class dataViewController from class mapViewController).
I hope this will help you.

IOS pass UIbuton to method as reference

In view.h I declare
#property (retain, strong) UIButton *btn;
In view.m I first have:
#synthesize btn;
and then in implementation I have this call to a method in another class:
[self.view addSubview:[otherclass getTestBtn:btn]];
After this line, in my view.m, I try to log the text in the button like this:
NSLog(#"btn.titleLabel.text = %#", btn.titleLabel.text);
Unfortunately the logs says:
btn.titleLabel.text = (null)
In the other class.m I implemented the method this way:
btn = [[UIButton alloc] initWithFrame:CGRectMake(70, 340, 100, 25)];
[btn setTitle:#"HELLO" forState:UIControlStateNormal];
return btn;
In the simulator I see that the button shows the text "Hello". But from view.m the text in the button don't seem to be accessible. Why?
Im afraid that the button that is passed from view.m to other class.m is not passed by reference. And hence the button in view.m is not affected by the method in other class. Is that the reason? If yes: how to pass the button as reference? I tried with & character. But the editor shows errors.
If you want to get the button returned from the other class then you need to assign it to your property -
self.btn = [otherClass getTestBtn];
[self.view addSubView:self.btn];
Also, you should strong rather than retain as #dimimpou said and you don't need #synthesize any more unless you want to use a specific backing variable name.

Creating a class to get all buttons delegates

I have a UIViewController class called , classA . This class is connected to buttons outlets of its connected view (from storyboard ).
What i would like to do , is to create another classB , that will handle all the buttons actions.
My question is, how can i set the delegates of a button in classA to be handled in classB, and how i make classB to be alive for all that time.
I was thinking about this .
in classA :(the main view controller that have the buttons outlets )
ClassB *buttonsResponder=[[classB alloc]init]; //initialize buttons class
[self.button addTarget:buttonsResponder action:#selector(do:) forControlEvents:UIControlEventTouchDown];
My main problem is, when classB handle the action, and lets say he has to open some mail composer view controller, how can classB open that mail composer "inside" classA view ? or even some UIView that i want to show in classA , but from classB
I was thinking of sending a pointer to the current view controller as an argument of the button, but i dont know how, and if it will work :
[self.button addTarget:buttonsResponder action:#selector(do:and
controller pointer?) forControlEvents:UIControlEventTouchDown];
The best way to keep ClassB alive in ClassA instance is make it a private property in class extension:
// In CalssA
#property (nonatomic, strong) ClassB *buttonsResponder;
You need to allocate it somewhere, viewDidLoad is good place:
self.buttonsResponder = [[classB alloc]init];
and in the same method you can make it a button(s) delegate:
[self.button addTarget:self.buttonsResponder action:#selector(do:) forControlEvents:UIControlEventTouchDown];
// Extended
If you want to present another view controller from your ClassB you can create property to hold weak reference to classA in classB:
// In ClassB.h
#property (nonatomic, weak) ClassA *myClassA;
Pass a reference to of classA when you initialise classB in viewDidLoad:
// In ClassA.m
self.buttonsResponder = [[classB alloc]init];
self.buttonsResponder.myClassA = self;
And when you want to present another view controller you can do it from classB:
[self.myClassA presentViewController:...];
[self.button addTarget:self.buttonsResponder action:#selector(do:)
forControlEvents:UIControlEventTouchDown];
If you want to present another viewcontroller from buttonsResponder:
[self.window.rootViewController presentViewController:anotherViewController animated:YES completion:nil];

Resources