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.
Related
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
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
I have a UIViewController with its UIView which contains a UIButton. I want to trigger a method in UIViewController on button click event.
Keeping reference of UIViewController doesn't seem to be a good idea like the following link says:
Get to UIViewController from UIView?
So I want to achive this using a delegate. Any hint on how to achieve this?
You can do something like this
CustomView.h
#import <UIKit/UIKit.h>
#protocol CustomViewDelegate <NSObject>
-(void)didButtonPressed;
#end
#interface CustomView : UIView
#property (assign) id<CustomViewDelegate> delegate;
#end
CustomView.m
#import "CustomView.h"
#implementation CustomView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor whiteColor];
//[self addSubview:titleLbl];
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(100, 100, 100, 50);
[button addTarget:self.delegate action:#selector(didButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:#"pressMe" forState:UIControlStateNormal];
[self addSubview:button];
}
return self;
}
in your ViewController.m
-(void)loadView
{
[super loadView];
CustomView *view = [[CustomView alloc]initWithFrame:self.view.bounds];
view.delegate = self;
[self.view addSubview:view];
}
This is what the responder chain was built for. When you add a target to your button, just supply nil for the target:
[mySpecialButton addTarget:nil
action:#selector(mySpecialButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
The nil target basically means "send mySpecialButtonTapped: to any object in the responder chain that can handle it".
Now you can handle this selector anywhere in the responder chain, which includes the button itself, its containing view, its containing view controller, the UIApplication, and finally your AppDelegate. Just place this method in the object most appropriate for your needs:
- (void)mySpecialButtonTapped:(id)sender {
NSLog("My special button was tapped!");
}
You don't need delegates or callback blocks (as in the accepted answer) if you just want to bubble a message up.
I guess that you expected something more fundamental then just pass some button action to controller.
I always follow MVC pattern in case of model/view/controller collaboration. It resolve your issue and many other. And I want to share my experience.
Separate controller from view and model: don't put all of the "business logic" into view-related classes; this makes the code very unusable. Make controller classes to host this code, but ensure that the controller classes don't make too many assumptions about the presentation.
Define callback APIs with #protocol, using #optional if not all the methods are required.
For view define protocol like <view class name>Protocol (example NewsViewProtocol). For controller define delegate like <view class name>Delegate (example NewsViewDelegate) and dataSource like <view class name>DataSource (example NewsViewDataSource). Keep all this #protocols in one separate file named <view class name>Protocol.h (example NewsViewProtocol.h)
Short example:
Contents of NewsView.h
//
// NewsView.h
#interface NewsView : UIView <NewsViewProtocol> {
#protected
NSObject* delegate_;
NSObject* dataSource_;
}
#end
Contents of NewsController.h and .m
//
// NewsController.h
#interface NewsController : UIViewController <NewsViewDataSource, NewsViewDelegate> {
}
#property (nonatomic, weak) UIView<NewsViewProtocol>* customView;
#end
#implementation NewsController
- (void)viewDidLoad {
[super viewDidLoad];
self.customView = (UIView<NewsViewProtocol>*)self.view;
[self.customView setDelegate:self];
[self.customView setDataSource:self];
}
#end
Contents of NewsViewProtocol.h
//
// NewsViewProtocol.h
#protocol NewsViewProtocol;
#protocol NewsViewDelegate<NSObject>
#optional
- (void)someAction;
- (void)newsView:(UIView<NewsViewProtocol>*)newsView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
#end
#protocol NewsViewDataSource<NSObject>
#required
- (id)newsView:(UIView<NewsViewProtocol>*)newsView itemAtIndexPath:(NSIndexPath *)indexPath;
- (NSInteger)numberOfItemsInNewsView:(UIView<NewsViewProtocol>*)newsView section:(NSInteger)section;
- (BOOL)newsView:(UIView<NewsViewProtocol>*)newsView shouldDisplaySection:(NSInteger)section;
#end
#protocol NewsViewProtocol<NSObject>
#required
//Never retain delegate instance into implementation of this method
- (void)setDelegate:(NSObject<NewsViewDelegate>*)delegate;
//Never retain delegate instance into implementation of this method
- (void)setDataSource:(NSObject<NewsViewDataSource>*)dataSource;
- (void)reload;
#end
You may consider that it is redundant. In simple view controller, YES. But if you develop very complex screen with huge amount of data then it gives you some advantages as:
Helps you to separate responsibility between view and controller.
Keeps your code clear.
Makes you code more reusable.
Life is easy in xCode.
At the very beginning be sure that your xib View (the one with your button inside it) is associated to the right ViewController class. Which can be the default ViewController class that comes with a new project or your custom one.
After this, here comes the magic trick! Separate your view into 2 panel. The goal is to see your xib and your viewController code (the .m file). Now press the control key of your keyboard and drag your UIButton to the code. Select IBAction. It will generate something you can call a "listener" in other language. Go to the core code of your View Controller and complete the method!
Easy as that! Have fun :)
You don't really need delegates for this - it is how UIButtons are intended to be used. Just control-click and drag from your button to the .m file for your UIViewController. This will create a new method. From there, you can either make a call to the method you wrote or just copy-paste what you have into the new method.
You can try this:
[yourButton addTarget:self action:#selector(yourButtonAction:) forControlEvents:UIControlEventTouchUpInside];
And in your selector specify the action
- (IBAction)yourButtonAction:(id)sender {
//Action to perform
}
To add a button programmatically, in myViewController.m
UIView *yourView = [[UIView alloc] init];
UIButton *yourButton = [[UIButton alloc] initWithFrame:CGRectMake(0,0,100,21)];
[yourButton addTarget:self action:#selector(yourMethod) forControlEvents:UIControlEventTouchDown];
[yourView addSubview:yourButton];
More info here.
How do I add an action to a custom view? Similar to how UIButtons have an action I can connect in Inteface Builder. I can't use a SEL like below because it's not an object, what do I use?
#property(nonatomic, weak) IBOutlet SEL action;
You want to make your custom object a subclass of UIControl (which is a subclass of UIView). Then you get all the target/action methods... if you are doing this in code the one to explore will be
– addTarget:action:forControlEvents:
If you make a custom UIControl object, then you can drag out a custom view in the storyboard and set it's custom class to your customControl. Then you can drag out IBAction links to your viewController just as if it were a button.
Here is how to make one in code...
- (void)viewDidLoad
{
[super viewDidLoad];
UIControl* customControl = [[UIControl alloc]initWithFrame:CGRectMake(20, 20, 200, 200)];
customControl.backgroundColor = [UIColor redColor];
[customControl addTarget:self
action:#selector(customAction:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:customControl];
}
- (void)customAction:(id)sender
{
NSLog (#"touched");
}
I was digging around on SO and thought I had figured out how to do this but my test failed. I'm trying to close a popovercontroller from the viewcontroller that is launched/contained (I'm still a bit fuzzy on the poc and vc relationship) by it.
In my viewcontroller .h I have this:
#interface OAI_vcOperatingRooms : UIViewController {
OAI_ColorManager* colorManager;
OAI_FileManager* fileManager;
UIPopoverController* myPopOverController;
}
#property (nonatomic, weak) UIPopoverController* myPopOverController;
- (void) closeVC : (id) sender;
and in the .m file
UIButton* btnClose = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btnClose addTarget:self
action:#selector(closeVC:)
forControlEvents:UIControlEventTouchDown];
[btnClose setTitle:#"Close" forState:UIControlStateNormal];
btnClose.frame = CGRectMake(10.0, 210.0, 160.0, 40.0);
[self.view addSubview:btnClose];
- (void) closeVC : (id) sender {
[myPopOverController dismissPopoverAnimated:YES];
}
in the uiview that calls the popovercontroller, I've referenced the viewcontroller and added this:
//operating rooms
controller2 = [[OAI_vcOperatingRooms alloc] initWithNibName:#"OAI_vcOperatingRooms" bundle:nil];
popoverController2 = [[UIPopoverController alloc] initWithContentViewController:controller2];
controller2.myPopOverController = popoverController2;
No errors but nothing happens when I hit the close button. Is it possible to reference a POC from within the VC?
Thanks
I believe ott’s comment is on to something - try using a strong property instead of a weak one. A weak property is likely to get set to nil by ARC immediately after its last use. A strong property will stick around for the lifetime of its parent object (in this case, your UIViewController subclass), unless you set it to nil early.
Note: you probably don’t need UIPopoverController *myPopOverController; in your interface, since Xcode will automatically generate _myPopOverController as a backing variable.