UIButton from my class not working (EXC_BAD_ACCESS) in ViewController - ios

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!

Related

Custom UIBarButtonItem for UIToolbar

In my testing project, I am planning to have multiple picker views. For these picker views, there will be UIToolbars and custom UIBarButtons. I tried to create a class method so that I will not have to repeat the codes, but it seems like the button is not showing up.
Picker Done Button.h
#import <UIKit/UIKit.h>
#interface UIBarButtonItem (test)
+ (UIBarButtonItem*)barButtonItemWithTint:(UIColor*)color andTitle:(NSString*)itemTitle andTarget:(id)theTarget andSelector:(SEL)selector;
#end
Picker Done Button.m
#import "PickerDoneButton.h"
#implementation UIBarButtonItem (test)
+ (UIBarButtonItem*)barButtonItemWithTint:(UIColor*)color andTitle:(NSString*)itemTitle andTarget:(id)theTarget andSelector:(SEL)selector
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tintColor = color;
button.backgroundColor = [UIColor yellowColor];
[button addTarget:theTarget action:selector forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithCustomView:button];
return doneButton;
}
#end
And to use this class method on my View Controller.m (I am omitting picker view methods which come after viewDidLoad)
#import "ViewController.h"
#import "PickerDoneButton.h"
#interface ViewController ()
#property (strong, nonatomic) NSArray *numberofPeople;
#property (strong, nonatomic) UIPickerView *countPeople;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.countPeople = [[UIPickerView alloc] init];
self.countPeople.dataSource = self;
self.countPeople.delegate = self;
self.numberofPeople = #[#"1",#"2",#"3"];
UIToolbar *countPeopleToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0,0,0,50)];
countPeopleToolbar.barStyle = UIBarStyleBlackOpaque;
// Call the UIBarButtonItem from PickerDoneButton.h
countPeopleToolbar.items = #[[UIBarButtonItem barButtonItemWithTint:[UIColor redColor] andTitle:#"Done" andTarget:self andSelector:#selector(doneClicked)]];
[self pickerView:self.countPeople didSelectRow:0 inComponent:0];
}
Am I using the class method correctly?
Yes you are doing it correct. Only thing you are missing is you are not setting frame of your button to be set inside doneButton. Since button doesn't have any frame set it doesn't show up. Add this after creating button.
button.frame = CGRectMake(0, 0, 30, 30); // change frame as per your requirement

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

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.

UIButton with action-selector method in different class

Related to this previous question (Creating UI elements programmatically in Objective-C), this method resides in one class of the project, and other classes are able to call it to programmatically create buttons:
UIButton *createButton(CGFloat x, CGFloat y, CGFloat width, CGFloat height, NSString *caption, NSTextAlignmentCenter textPosition, UIColor *textColor, UIColor *backColor) {
UIButton *control = [[UIButton alloc] initWithFrame: CGRectMake(x, y, width, height)];
[control setTitle:caption forState:UIControlStateNormal];
control.titleLabel.textAlignment = textPosition;
control.backgroundColor = backColor;
[control setTitleColor: textColor forState:UIControlStateNormal];
return control;
}
I am now trying to add the target and action methods, to be handled by a common function, like so:
[control addTarget:nil
action:#selector(commonHandler)
forControlEvents:UIControlEventTouchDown];
It works for buttons that reside in the same class as the createButton() function, but although the buttons are successfully created in the other classes as well, they don't trigger the commonHandler() method.
NOTE: the createButton() and commonHandler() methods have been declared in its header file, and imported by all classes.
Is there any way to achieve this?
EDIT: The proposed solution still crashes
I modified the code as per the suggestions, and converted both the createButton and buttonAction to Objective-C methods, to enable reference to self.
class1 header
// class1.h
#import <UIKit/UIKit.h>
#import "class2.h"
#interface class1 : UIViewController {
UIButton *button1;
}
- (void) buttonAction:(UIButton *)control;
- (UIButton *) createButtonWithxPos:(CGFloat)x
yPos:(CGFloat)y
width:(CGFloat)width
height:(CGFloat)height
caption:(NSString *)caption
textPos:(NSTextAlignment)textPosition
textClr:(UIColor *)textColor
backClr:(UIColor *)backColor;
#end
class1 implementation
// class1.m
#import "class1.h"
#import "class2.h"
#implementation class1
- (void)viewDidLoad {
[super viewDidLoad];
button1 = [self createButtonWithxPos:10
yPos:10
width:200
height:30
caption:#"Cls1 Button"
textPos:NSTextAlignmentCenter
textClr:[UIColor whiteColor]
backClr:[UIColor blackColor]];
[self.view addSubview: button1];
}
- (UIButton *) createButtonWithxPos:(CGFloat)x
yPos:(CGFloat)y
width:(CGFloat)width
height:(CGFloat)height
caption:(NSString *)caption
textPos:(NSTextAlignment)textPosition
textClr:(UIColor *)textColor
backClr:(UIColor *)backColor
{
UIButton *control = [[UIButton alloc] initWithFrame: CGRectMake(x, y, width, height)];
[control setTitle:caption forState:UIControlStateNormal];
control.titleLabel.textAlignment = textPosition;
control.backgroundColor = backColor;
[control setTitleColor: textColor forState:UIControlStateNormal];
[control addTarget:self action: #selector(buttonAction:) forControlEvents:UIControlEventTouchDown];
return control;
}
- (void) buttonAction:(UIButton *)control {
NSLog(#"button clicked...");
}
#end
class2 header
// class2.h
#import <UIKit/UIKit.h>
#interface class2 : UIViewController {
UIButton *button2;
}
#end
class2 implementation
// class2.m
#import "class1.h"
#import "class2.h"
#implementation class2
- (void) viewDidLoad {
[super viewDidLoad];
class1 *cls1 = [[class1 alloc] init];
button2 = [cls1 createButtonWithxPos:10
yPos:10
width:200
height:30
caption:#"Cls2 Button"
textPos:NSTextAlignmentCenter
textClr:[UIColor whiteColor]
backClr:[UIColor blackColor]];
[self.view addSubview: button2];
}
#end
Both button1 and button2 are created properly, but button1 triggers the buttonAction method while button2 does not - it crashes on touch.
Is it implemented wrongly?
(please excuse any omissions as I have only extracted the relevant portions from the actual project to illustrate here)

UIView (with UIButton) on top of a UIView - how to activate button programatically?

I have a UIVIew with Button. This is loaded in the VIewController as hidden. No problem unhiding it but the button on top of it is not working. Is the UIview class the right place to put the codes for the exit button (hiding it again)?
in .h
#interface infoView : UIView
#property (nonatomic, strong) IBOutlet UIView *infoTextView;
#property (nonatomic, strong) UIButton *exitButton;
-(void)exitView:(UIButton*)sender;
#end
in .m
#import "infoView.h"
#implementation infoView
#synthesize infoTextView = _infoTextView;
#synthesize exitButton=_exitButton;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
_infoTextView = [[UIView alloc]initWithFrame:CGRectMake(40, 40, 240, 240)];
[_infoTextView setBackgroundColor: [UIColor blueColor]];
[self addSubview:_infoTextView];
_exitButton = [[UIButton alloc] initWithFrame:CGRectMake(200, 10, 30, 30)];
//other info
[self.infoTextView addSubview:self.exitButton];
}
return self;
}
-(void)exitView:(UIButton*)sender
{
self.infoTextView.hidden=YES;
}
viewController
#implementation gjViewController
#synthesize imageViewMain = _imageViewMain;
#synthesize changeImageButton = _changeImageButton;
#synthesize infoButton = _infoButton;
#synthesize myInfo = _myInfo;
- (void)viewDidLoad
{
[super viewDidLoad];
_imageViewMain = [[UIImageView alloc] init];
//other info
[self.view addSubview:self.imageViewMain];
_changeImageButton = [[UIButton alloc] initWithFrame:CGRectMake(110, 50, 100, 50)];
//other info
[self.view addSubview:self.changeImageButton];
_infoButton = [[UIButton alloc] initWithFrame:CGRectMake(260, 30, 40, 40)];
//other details
[self.view addSubview: self.infoButton];
the section below is the part where the UIView is loaded. It loads and unhides with the IBAction at the bottom. I think you are pointing that this is wrong, not sure how. Thanks for comments.
_myInfo = [[infoView alloc] init];
[self.view addSubview:_myInfo];
[_myInfo setHidden:YES];
}
-(IBAction)newImage:(UIButton*)sender{
//task
-(IBAction)oldImage:(UIButton*)sender{
//taks
-(IBAction)viewInfo:(UIButton*)sender{ <--- unhides the UIView
[_myInfo setHidden:NO];

Adding addTarget: method to custom UIView

I created a custom UIView something like UIStepper, because I don't like the appearance of the UIStepper. I want UIStepper have a personal count label so I added it to my custom view and created a method to increase-decrease it. Now I need a [customView addTarget:(id) action:(del) forControlEvents:(UIControlEvents)] method to catch UIControlEventValueChanged; but I couldn't find anything about implementing it. I dig around the UIButton.h, UIStepper.h files but no luck too.. Can anyone help me to do that?
Here is how I created the custom view...
CounterView.h
#import <UIKit/UIKit.h>
#interface CounterView : UIView
#property NSString* name;
#property NSInteger count;
#property UILabel *label;
#property NSInteger customTag;
- (id)initWithX:(CGFloat)xPoint WithY:(CGFloat)yPoint WithName:(NSString*)newName withCount:(NSInteger)newCount withCustomTag:(NSInteger)newTag;
#end
CounterView.m
#implementation CounterView
- (id)initWithX:(CGFloat)xPoint WithY:(CGFloat)yPoint WithName:(NSString*)newName withCount:(NSInteger)newCount withCustomTag:(NSInteger)newTag
{
self = [super initWithFrame:CGRectMake(xPoint, yPoint, 24, 52)];
if (self) {
self.customTag = newTag;
self.count = newCount;
self.name = newName;
UIButton *btnUp = [[UIButton alloc] initWithFrame:CGRectMake(3, 2, 18, 12)];
[btnUp setImage:[UIImage imageNamed:#"top.png"] forState:UIControlStateNormal];
[btnUp addTarget:self action:#selector(increaseValue) forControlEvents:UIControlEventTouchUpInside];
UIButton *btnDown = [[UIButton alloc] initWithFrame:CGRectMake(3, 38, 18, 12)];
[btnDown setImage:[UIImage imageNamed:#"bottom.png"] forState:UIControlStateNormal];
[btnDown addTarget:self action:#selector(decreaseValue) forControlEvents:UIControlEventTouchUpInside];
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 14, 24, 24)];
[self.label setText:[NSString stringWithFormat:#"%ld", (long)self.count]];
self.label.textAlignment = NSTextAlignmentCenter;
[self addSubview:btnUp];
[self addSubview:btnDown];
[self addSubview:self.label];
}
return self;
}
-(void) increaseValue{
self.count++;
[self.label setText:[NSString stringWithFormat:#"%ld", (long)self.count]];
}
-(void) decreaseValue{
self.count--;
[self.label setText:[NSString stringWithFormat:#"%ld", (long)self.count]];
}
#end
in the ViewController where you instantiate your CounterView you add this
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSingleTap:)];
[yourViewInstantiation addGestureRecognizer:singleFingerTap];
[singleFingerTap release];
and the you implement the call back method:
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {
CGPoint location = [recognizer locationInView:[recognizer.view superview]];
//Do stuff here...
}
i hope that help you !
Just allow the actions to be set from outside of your control. Here is how to do it:
#interface CounterView : UIView
...
#property(nonatomic, strong) UIButton *btnUp;
#property(nonatomic, strong) UIButton *btnDown;
- (void)addUpTarget:(id)target action:(SEL)action;
- (void)addDownTarget:(id)target action:(SEL)action;
#end
#implementation CounterView
...
- (void)addUpTarget:(id)target action:(SEL)action
{
[_btnUp addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
}
- (void)addDownTarget:(id)target action:(SEL)action
{
[_btnDonw addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
}
...
#end
Then use methods:
- (void)addUpTarget:(id)target action:(SEL)action;
- (void)addDonwTarget:(id)target action:(SEL)action;
to set a target and actions for increasing and decreasing the values.

Resources