How to invoke a method from UIViewController class to Custom UIView class - ios

I am not able to call the method which is written in UIViewController(aViewController) from my custom view bView .
I want to call the method function on my button(btn) click in UIView(bView). But when I click the button, i am getting the following error.
unrecognized selector sent to instance....
Here is my code:
aViewController.h
#import "bView.h"
#import "cViewController.h"
-(void)function;
aViewController.m
-(void)viewDidLoad{
bView = [[bView alloc]init];
[bView makeView];
bView.bViewController = self;
[self.view addSubView:bView];
}
-(void)function{
cViewController *nextViewController = [[cViewController alloc] initWithNibName: nil bundle: nil];
[self.navigationController pushViewController: nextViewController animated: YES];
}
bView.h
#import "bViewController.h"
#interface bView : UIView{
UIButton btn;
bView *BView;
}
-(void)makeView;
-(void)function;
#property (nonatomic,readonly) UIButton *btn;
#property (assign) UIViewController* aViewController;
bView.m
#import "bView.h"
#import "aViewController.h"
#impementation bView
#synthesize btn;
#synthesize aViewController;
-(void)makeView{
btn= [UIButton buttonWithType:UIButtonTypeRoundedRect];
UIImage *btnimg = [UIImage imageNamed:#"btn.png"];
[btn setBackgroundImage:btnimg forState:UIControlStateNormal];
btn.frame = CGRectMake(65, 380, 60, 60);
[self addSubview:btn];
btn.userInteractionEnabled = YES;
[btn addTarget:aViewController action:#selector(function) forControlEvents:UIControlEventTouchUpInside];
}
How to solve it?

You are setting a target to button in [bView makeView]; method, but at that time bView's aViewController is nil because you are setting it only on the next line. So try this:
-(void)viewDidLoad{
bView = [[bView alloc]init];
bView.bViewController = self;
[bView makeView];
[self.view addSubView:bView];
}

this is how you do it, first off, get rid of the "bView.bViewController = self", it doesn't work that way, you shouldn't do this, ever.
-(void)viewDidLoad {
bView = [[bView alloc]init];
[bView makeView];
[self.view addSubView:bView];
[[bView btn] addTarget:self action:#selector(function) forControlEvents:UIControlEventTouchUpInside];
}
function is declared in your UIViewController, not your UIView, so you must call to the button in your viewdidLoad through the bView and then assign it a target and action. I still wouldn't use this method that you are using, but this will do it.
this is the magic:
[[bView btn] addTarget:self action:#selector(function) forControlEvents:UIControlEventTouchUpInside];
AND, remove these lines from the UIView:
#property (assign) UIViewController* aViewController;
-(void)function;
[btn addTarget:aViewController action:#selector(function) forControlEvents:UIControlEventTouchUpInside];
you should remove these if your intent is for the UIViewController to have the function "-(void)function" and I assume this is what you meant. I'm assuming that your "function" is supposed to be called in your UIViewController
for out purposes here, this:
[[bView btn] addTarget:self action:#selector(function) forControlEvents:UIControlEventTouchUpInside];
is the same thing as this:
[bView.btn addTarget:self action:#selector(function) forControlEvents:UIControlEventTouchUpInside];
and
bView.btn = #property (nonatomic,readonly) UIButton *btn;
In fact, you don't need to call this button in any UIView, given what you are showing, just call this in your viewDidLoad, like this:
-(void)viewDidLoad
{
[super viewDidLoad];
UIButton * btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
UIImage *btnimg = [UIImage imageNamed:#"btn.png"];
[btn setBackgroundImage:btnimg forState:UIControlStateNormal];
btn.frame = CGRectMake(65, 380, 60, 60);
btn.userInteractionEnabled = YES;
[self.view addSubview:btn];
[btn addTarget:self action:#selector(function) forControlEvents:UIControlEventTouchUpInside];
}
I'm showing you this because you aren't setting up the UIView correctly in the first place by invoking super on it's designated initializer so the best bet now is to just place the button in your viewDidLoad and move on, this is the solution until you understand how the initialize a UIView and set it up correctly. There's a lot of stuff that is wrong with the UIView you have set up and tried to subclass and it's going to be easier for you to just make the button in viewDidLoad and then learn how to subclass correctly in the future.

Related

UIButton target action not being called

Forgive me if this is trivial, I am still honing my programing skills. I am trying to set this button as a target. Should be easy but I dont know why it's not working! I inserted a NSLog to test and the method is not being called! Thanks for your help.
//ShareView.h
#property (strong,nonatomic) UIButton *cancelBtn;
//ShareView.m
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
UIImage *shareImage = [UIImage imageNamed:#"shareBox.png"];
[self setFrame:CGRectMake(10, 170, shareImage.size.width, shareImage.size.height)];
self.shareIV = [[UIImageView alloc] initWithImage:shareImage];
self.shareIV.userInteractionEnabled = YES;
[self addSubview:self.shareIV];
[self shareBtnsInit];
[self.shareIV addSubview:self.cancelBtn];
self.userInteractionEnabled = YES;
}
return self;
}
-(void)shareBtnsInit{
UIImage *cancelImg = [UIImage imageNamed:#"cancel27.png"];
self.cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[self.cancelBtn setImage:cancelImg forState:UIControlStateNormal];
[self.cancelBtn setFrame:CGRectMake(277, 3, cancelImg.size.width, cancelImg.size.height)];
}
//MainViewController.m
-(IBAction)settingsButtonPressed:(id)sender{
self.shareVC = [[ShareView alloc] initWithFrame:CGRectMake(0, 0, 0,0)];
[self.view addSubview: self.shareVC];
[self.shareVC.cancelBtn addTarget:self action:#selector(settingsCancel:) forControlEvents:UIControlEventTouchUpInside];
}
-(IBAction)settingsCancel:(id)sender{
NSLog(#"TEST!!!");
[self.shareVC removeFromSuperview];
}
Based on comments, your settingsButtonPressed: method wasn't being called, so the button you were looking for the action on was never being set up.
Try adding [self.cancelBtn addTarget:self action:#selector(settingsCancel:) forControlEvents:UIControlEventTouchUpInside]; to your shareBtnsInit method.
Here's more information on making UIButtons programatically: How do I create a basic UIButton programmatically?
[self.cancelBtn addTarget:MainViewController action:#selector(settingsCancel:) forControlEvents:UIControlEventTouchUpInside];
In ShareView you have to import class in which you want your event should get triggered.
Here in ShareView you can import MainViewController class and can use it.

RemoveFromSuperView not working on UIButton

It seems like such a simple thing to remove a button from a view, but it is not working.
MyViewController.h
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController
#property (strong, nonatomic, readonly, getter = getMyButton) UIButton* myButton;
- (id) init;
- (id) getMyButton;
#end
MyViewController.m
#import "MyViewController.h"
#interface MyViewController ()
#end
#implementation MyViewController
#synthesize myButton = _myButton;
- (id) init
{
if([super initWithNibName: nil bundle: nil])
{
_myButton = nil;
}
return self;
}
- (id) getMyButton
{
if(!_myButton) _myButton = [self createMyButton];
return _myButton;
}
- (void) viewDidLoad
{
[super viewDidLoad];
UIButton* myButton = self.myButton;
[self.view addSubview: myButton];
}
- (void) didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (UIButton*) createMyButton
{
UIButton *button = [[UIButton alloc] init];
[button setTitle: #"My Button"
forState: UIControlStateNormal];
[button addTarget: self
action: #selector(myAction:)
forControlEvents: UIControlEventTouchUpInside];
button.frame = CGRectMake(10, 10, 100, 40);
return button;
}
- (void) myAction: (id) sender
{
[self.myButton performSelectorOnMainThread: #selector(removeFromSuperview) withObject: nil waitUntilDone: NO];
}
#end
But no luck. Clicking the button simply does nothing.
If it is not a problem of concurrency then maybe it is a memory management problem? Maybe it is just something daft, I don't know.
I tried putting the following line into the myAction method
NSLog(#"Test 1");
if(_startButton.superview) NSLog(#"Test 2");
Only 'Test 1' is logged. Perhaps that is a clue but what I don't know is why the button has no superview when it is added to view and is visible on the screen
Additional information
I don't know, if any of this is relevant, but maybe
I just updated Xcode to the latest version from the developer program (it supported up to iOS version 7.0 before, now 7.1)
I just started testing the app on an actual iPhone (I get the same problem testing with the simulator though)
Around the same time as this problem I also noticed that NSLog function doesn't work inside AppDelegate applicationHasLaunched method
Thanks
You didn't set the action for that button.
Change your createMyButton method like:
- (UIButton*) createMyButton
{
UIButton *button = [[UIButton alloc] init];
[button setTitle: #"My Button"
forState: UIControlStateNormal];
button.frame = CGRectMake(10, 10, 100, 40);
[button addTarget:self action:#selector(myAction:) forControlEvents:UIControlEventTouchUpInside];
return button;
}
Also change myAction method like:
- (void)myAction:(UIButton *)sender
{
[sender performSelectorOnMainThread: #selector(removeFromSuperview) withObject: nil waitUntilDone: NO];
}
Your code seems overly complicated:
Why do you need to lazy create the button?
Why does it need to be readonly?
Why do you need the performselector in your action?
I have taken the liberty to rewrite your code:
Header file
#property (nonatomic,strong) UIButton * button;
Code file
- (void)viewDidLoad
{
[super viewDidLoad];
self.button = [UIButton buttonWithType:UIButtonTypeSystem];
[self.button setTitle:#"MyButton" forState:UIControlStateNormal];
self.button.frame = self.view.bounds;
[self.button addTarget:self action:#selector(myAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.button];
}
-(void)myAction:(UIButton*)sender
{
[sender removeFromSuperview];
}
Happy coding!
Edit
If you insist on going with your original code, try changing
- (id) getMyButton
to
-(UIButton*)myButton
Buttons are a class family and should be created with [UIButton buttonWithType:]
I am sure that your myAction is not get fired. Cause you forget to addTarget
on your createMyButton method just add one more line
[button addTarget:self action:#selector(myAction:) forControlEvents:UIControlEventTouchUpInside];
UPDATE: You've also marked this question as iOS but you are importing OSX headers
#import "NSViewController.h"
#interface MyViewController: NSViewController
you should be inheriting from UIViewController
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController
Are you creating the view controller via the init code you've posted?
- (id) init
{
if([super initWithNibName: nil bundle: nil])
{
_myButton = nil;
}
return self;
}
Here you are not allocating anything to self the code should be
- (id) init
{
self = [super initWithNibName: nil bundle: nil];
if(self)
{
_myButton = nil;
}
return self;
}

UIButton from my class not working (EXC_BAD_ACCESS) in ViewController

I made my own class with the UIView including the UIButton and the method for it:
MyClass.h
#property (strong, nonatomic) UIView* mainBack;
#property (strong, nonatomic) UIButton* sortButton;
MyClass.m
- (id) initFor: (int) object {
if (self = [super init]) {
_mainBack = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
_sortButton = [UIButton buttonWithType:UIButtonTypeSystem];
_sortButton.frame = CGRectMake(10.0, 5.0, 60.0, 40.0);
[_sortButton addTarget:self action:#selector(ChangeSort) forControlEvents:UIControlEventTouchUpInside];
[_mainBack addSubview:_sortButton];
}
return self;
}
- (void) ChangeSort {
NSLog(#"change");
}
- (void) ShowBarOnView: (UIViewController*) view AtPoint: (CGPoint) point{
[_mainBack setFrame:CGRectMake(point.x, point.y, _mainBack.frame.size.width, _mainBack.frame.size.height)];
[view.view addSubview:_mainBack];
}
In ViewController I made the instance of myClass and called the method to show view
MyClass* mySort = [[myClass alloc] initFor: rooms];
[mySort ShowBarOnView:self AtPoint:CGPointMake(0, 50)];
So, I got the view with the button, but when pressed, the application crashes and shows:
EXC_BAD_ACCESS (code=1, address=0x0)
What is wrong?
Thanks.
Yes, I had the Same Problem!
I had my MainViewController creating the PlayingViewController in a method and long story short that was the problem.
SOFMainVC.h
SOFMainVC.m
#import "SOFPlayingVC.h" ...
- (void)someMethodBeingCalled {
SOFPlayingVC *playingVC = [[SOFPlayingVC alloc] init];
[self.view addSubview:playingVC.view];
}
SOFPlayingVC.m
...
...
- (void)viewDidLoad {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setFrame:CGRectMake(0, 0, 100, 50)];
[button addTarget:self
action:#selector(buttonAction:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubView:button];
...
...
}
And if we were to Focus on SOFPlayingVC in instruments we'll see that the controller will be garbage collected as soon as it's done being instantiated. However if we simply add it to the controller that is in use as a #property (nonatomic, strong) we wont lose it until its out of the SOFMainViewController.
So the Solution to our issue here was to:
#import "SOFPlayingVC.h"
SOFMainVC.h
...
#interface SOFMainVC : UIViewController
#property (nonatomic, strong) SOFPlayingVC *playingVC;
...
#end
SOFMainVC.m
#import "SOFPlayingVC.h" ... ...
- (void)someMethodBeingCalled {
SOFPlayingVC *playingViewController = [[SOFPlayingVC alloc] init];
[self.view addSubview:playingVC.view];
}
If the posted code snippet appears exactly as written, the problem is that the following lines are NEVER executed. Xcode should probably be warning you about them:
_mainBack = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
_sortButton = [UIButton buttonWithType:UIButtonTypeSystem];
_sortButton.frame = CGRectMake(10.0, 5.0, 60.0, 40.0);
[_sortButton addTarget:self action:#selector(ChangeSort)
forControlEvents:UIControlEventTouchUpInside];
[_mainBack addSubview:_sortButton];
As posted, these lines exist completely outside of any method. What would make most sense is to create a factory method:
+ (instancetype) mainViewAtPoint:(CGPoint)point inContext:
(UIViewController*)viewController {
self.mainBack = [[UIView alloc] initWithFrame:CGRectMake(point.x,point.y,
320,50)];
self.sortButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.sortButton.frame = (CGRectMake(10.0, 5.0, 60.0, 40.0);
[self.sortButton addTarget:self action:#selector(ChangeSort)
forControlEvents:UIControlEventTouchUpInside];
[self.mainBack addSubview:self.sortButton];
[viewController.view addSubview: self.mainBack];
}
Now all you have to do to alloc, init, create, and add this view to your view controller is simple this:
[myClass mainViewAtPoint:aPoint inContext:aViewController];
The obvious clue that your button isn't actually created is tipped off by the EXC_BAD_ACCESS. It's telling you it's trying to reference memory location 0x0. Any time anything is trying to access 0x0 memory address, you're trying to call a method on an object that doesn't have a memory address... it's never been alloc'ed.
I solved this problem, by adding in
in ViewController.h
#property (strong, nonatomic) MyClass* mySort;
and changing my code in ViewController.m to
_mySort = [[SortAndSeach alloc] initFor:for_what];
[_mySort ShowBarOnView:self AtPoint:CGPointMake(0, 50)];
Thanks to all!

selector as parameter in IOS

I want to raise a different method for each created button.
I try to call "FirstImage" method in viewDidLoad but it doesn't work.
I have a problem with the selector in ViewDidLoad. Didn't recognise "FirstImage" which is a void method without parameter.
ViewController.m
- (void)createFirstButton:(NSString *)myName: (SEL *)myAction{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn addTarget:self
action:#selector(myAction)
forControlEvents:UIControlEventTouchUpInside];
[btn setTitle:myName forState:UIControlStateNormal];
btn.frame = CGRectMake(20, 916, 120, 68);
[self.view addSubview:btn];
}
- (void)viewDidLoad{
[self createFirstButton:#"First" myAction:[self FirstImage]];
}
What I have done (I changed "CreateFirstButton" into "CreateButton") :
ViewControler.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize myHeight;
#synthesize myWidth;
#synthesize myX;
#synthesize myY;
- (void)createButton:(NSString *)myName:(SEL)myAction:(NSUInteger)my_x:(NSUInteger)my_y:(NSUInteger)my_width:(NSUInteger)my_height
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn addTarget:self
action:myAction
forControlEvents:UIControlEventTouchUpInside];
[btn setTitle:myName forState:UIControlStateNormal];
btn.frame = CGRectMake(my_x, my_y, my_width, my_height);
[self.view addSubview:btn];
}
- (void)myXcrementation{
myX = myX + 150;
}
- (void)viewDidLoad{
myX = 20; myY = 916; myWidth = 120; myHeight = 68;
[self createButton:#"First":#selector(FirstImage):myX:myY:myWidth:myHeight];
[self myXcrementation];
[self createButton:#"Previous":#selector(PreviousImage):myX:myY:myWidth:myHeight];
[self myXcrementation];
[self createButton:#"Pause":#selector(PauseImage):myX:myY:myWidth:myHeight];
[self myXcrementation];
[self createButton:#"Next":#selector(NextImage):myX:myY:myWidth:myHeight];
[self myXcrementation];
[self createButton:#"Last":#selector(LastImage):myX:myY:myWidth:myHeight];
}
- (void)FirstImage{
current = 0;
[self SetImage];
}
-(void)SetImage{
[myImageView setImage: [myArray objectAtIndex:(current)]];
}
#end
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController{
}
#property(assign, nonatomic) NSUInteger myHeight;
#property(assign, nonatomic) NSUInteger myWidth;
#property(assign, nonatomic) NSUInteger myX;
#property(assign, nonatomic) NSUInteger myY;
#end
I edited this post again and there is no more error.
Special thanks to everybody. It took me time to understand :)
You have to use use #selector as follows
[self createFirstButton:#"First" myAction:#selector(FirstImage)];
Then your signature is wrong, since SEL shouldn't be a pointer.
Change
- (void)createFirstButton:(NSString *)myName: (SEL *)myAction{
to
- (void)createFirstButton:(NSString *)myName: (SEL)myAction{
Finally myAction has type SEL so you can pass it directly to the UIButton method, as follows
[btn addTarget:self
action:myAction
forControlEvents:UIControlEventTouchUpInside];
Also, I'd like to add that using capitalized names for methods it's a very bad practice.
You should use
- (void)viewDidLoad{
[self createFirstButton:#"First" myAction:#selector(FirstImage)];
}
or
- (void)viewDidLoad{
[self createFirstButton:#"First" myAction:NSSelectorFromString(#"FirstImage")];
}
#selector(myAction) should be replaced with myAction, and (SEL *) should be replaced with SEL.
In your code, before -createFirstButton:myAction: is called, its second parameter [self FirstImage] would be evaluated(called) first. #selector() takes a name and returns a SEL.
Change the method literal as in the following
- (void)createFirstButton: (NSString *)myName withAction: (SEL)myAction
For both declaration and definition.
And also why have you declared the same method as a public as well as private?
Use
[self createFirstButton:#"First" withAction:NSSelectorFromString(#"FirstImage")];
And change the method name as :
- (void)createFirstButton:(NSString *)myName: withAction: (SEL) myAction{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn addTarget:self
action:myAction
forControlEvents:UIControlEventTouchUpInside];
[btn setTitle:myName forState:UIControlStateNormal];
btn.frame = CGRectMake(20, 916, 120, 68);
[self.view addSubview:btn];
}
Also,
I wonder-all buttons having same frame, if possible pass frame this method.
If you are looking to pass string as method name to selector, then use NSSelectorFromString. String should end with ':' Example-
NSString *myName=#"aMethod:";
[button addTarget:self
action:NSSelectorFromString(myName)
forControlEvents:UIControlEventTouchDown];

Tapping UIButton that's a subview of UIView

I'm having trouble capturing taps on a UIButton that's a subview of a UIView. Here's how my code is setup:
// in MyClass.m
#interface MyClass ()
#property (nonatomic, retain) UIButton *myButton;
#end
#implementation MyClass
#synthesize myButton;
- (void) buttonTapped:(id) sender {
NSLog(#"button tapped!");
}
- (id) initWithFrame:(CGRect)frame {
if (!(self = [super initWithFrame:CGRectZero]))
return nil;
myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myButton setImage:[UIImage imageNamed:#"image.png"]
forState:UIControlStateNormal];
[myButton addTarget:self
action:#selector(buttonTapped:)
forControlEvents:UIControlEventTouchUpInside];
myButton.exclusiveTouch = YES;
myButton.frame = CGRectMake(100, 100, 100, 100);
[self addSubview:myButton];
return self;
}
- (void) dealloc {
[myButton release];
[super dealloc];
}
#end
The button appears in the view. The button color changes momentarily when I tap it. However the selector buttonTapped: is never called. Any idea why?
How can I verify that buttonTapped: is indeed a target of myButton?
You could verify that your current class is a target by logging the r
NSLog(#"actions for target %#",[myButton actionsForTarget:self forControlEvent:UIControlEventTouchUpInside]);
However, I added your code to a test project (single view template) and the buttonTapped: method worked.
- (void) buttonTapped:(id) sender {
NSLog(#"button tapped!");
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
self.viewController = [[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
UIButton * myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myButton setImage:[UIImage imageNamed:#"image.png"]
forState:UIControlStateNormal];
[myButton addTarget:self
action:#selector(buttonTapped:)
forControlEvents:UIControlEventTouchUpInside];
myButton.exclusiveTouch = YES;
myButton.frame = CGRectMake(100, 100, 100, 100);
[rv.view addSubview:myButton];
return YES;
}
The issue is somewhere else. Is the code posted the whole .h and .m for MyClass?

Resources