App crashes on button Click Action iOS - ios

I have created a button and added its action at runtime. The entire view is created at runtime.
Calling the action saveUserProfileSetting on click crashes the app "NSInvalidArgument: Unrecornized selector sent to a instance . It was working okay when the code was present in
another class. I tried to create an new class for it and it crashes.
#interface LASplashViewer : NSObject
+(void) showSplashScreen;
+(void) dismissSplashScreen;
#end
(void) showSplashScreen
{
UIView *mainScreen = [[[UIApplication sharedApplication]delegate]window];
UIView *windowBlocker = [[UIView alloc]initWithFrame:mainScreen.frame];
windowBlocker.tag = 999;
windowBlocker.backgroundColor = [UIColor clearColor];
UIButton *saveButton = [[UIButton alloc]initWithFrame:CGRectMake(200, 430, 420, 30)];
[saveButton setTitle:#"Save" forState:UIControlStateNormal];
[saveButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[saveButton addTarget:self action:#selector(saveUserProfileSetting) forControlEvents:UIControlEventTouchUpInside];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(165, 200, 450, 480)];
imageView.layer.cornerRadius=10;
imageView.layer.masksToBounds = YES;
[windowBlocker addSubview:imageView];
[imageView addSubview:saveButton];
imageView.userInteractionEnabled = true;
[mainScreen addSubview:windowBlocker];
}
-(void) saveUserProfileSetting
{
// TODO: if validation is successful save the below data. and dismiss the splash screen.
NSUserDefaults *userSettings = [[NSUserDefaults alloc] init];
[userSettings setObject:fullName.text forKey:#"name_pref"];
NSLog(#"%#fullname" , fullName);
[userSettings setObject:jobTitle.text forKey:#"title_pref"];
[userSettings setObject:streetName.text forKey:#"street_name_pref"];
[userSettings setObject:suburbs.text forKey:#"suburb_pref"];
[userSettings setObject:postCode.text forKey:#"postcode_pref"];
[userSettings setObject:phoneNum.text forKey:#"phone_no_pref"];
[userSettings setObject:fax.text forKey:#"fax_pref"];
[userSettings setObject:mobileNumber.text forKey:#"mobile_no_pref"];
[userSettings setObject:email.text forKey:#"email_pref"];
[userSettings synchronize];
}
Call to the method is Show is in another
class - // HomeViewController.
(void)viewDidLoad
{
[super viewDidLoad];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"isFirstLaunch"])
{
// app already launched
[[NSUserDefaults standardUserDefaults]synchronize];
}
else
{
[[NSUserDefaults standardUserDefaults]setBool:YES forKey:#"isFirstLaunch"];
[[NSUserDefaults standardUserDefaults]synchronize];
// call this method only for the first load.
//[self performSelector:#selector() withObject:nil afterDelay:0.5];
[self performSelector:#selector(saveProfile) withObject:Nil afterDelay:0.5];
}
}
-(void) saveProfile
{
[LASplashViewer showSplashScreen];
}
log :
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[LASplashViewer saveUserProfileSetting]: unrecognized selector sent to class 0x2a4dc'
please guide me. Thanks

[LASplashViewer saveUserProfileSetting] is crashing because it is not a static function. Make your function as
1) + (void)saveUserProfileSetting { }
2) Or call as
LASplashViewer *viewer = [[LASplashViewer alloc] init];
[viewer saveUserProfileSetting];

Looks like you call instance selector from class method
LASplashViewer have class method showSplashScreen where you call instance method saveUserProfileSetting
to solve problem try make saveUserProfileSetting also class method (replace - to +)

+(void) showSplashScreen; is a class method, from which you are setting action for the button
[saveButton addTarget:self action:#selector(saveUserProfileSetting) forControlEvents:UIControlEventTouchUpInside];
Here the target is self. Inside a class method self refers to its own Class not to the instance. So here the target of the method is LASplashViewer not its instance. Your button expects a class method like +(void)saveUserProfileSetting. It is not finding a class method with such name. So its crashing. I hope you understood root cause.
Solution is make method as a class method
+ (void)saveUserProfileSetting

Related

Passing UIViewController by reference or get self control in another handler class

I have created a seperate class MyUtil for handling some basic operation like navigation right left button and their events. LeftButton(UIBarButton left) is handled but right is displayed but not handled.
Please suggest me another way round.
MyUtil.h
#import <Foundation/Foundation.h>
#interface
#interface MyUtil : NSObject
#property(strong,nonatomic) UIViewController *uiv;
-(void)NavAndBackBtn:(UIViewController *)context;// it is running awesome.
-(void)NavAndForwardBtn:(UIViewController *)context :(NSString *)vc_name;//it displays right button but don't know how to add pushViewController to it in this class.
//context is self of calling ViewController and vc_name is the name of ViewController which i want to push
#end
MyUtil.m
#import "MyUtil.h"
#import "ForgotPassViewController.h"
#implementation MyUtil
-(void)pushingView:(UIViewController *)vvc secondInput:(NSString *)view_name
{
Class theClass = NSClassFromString(view_name);
id myObject = [[theClass alloc] init];
[vvc.navigationController pushViewController:myObject animated:YES];
}
-(void)method:(NSArray *)val
{
}
-(void)NavAndBackBtn:(UIViewController *)context
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:[UIImage imageNamed:#"icon_back2.png"] forState:UIControlStateNormal];
[button addTarget:context.navigationController action:#selector(popViewControllerAnimated:)forControlEvents:UIControlEventTouchUpInside];
[button setFrame:CGRectMake(0, 0, 36, 20)];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
context.navigationItem.leftBarButtonItem =barButton;
}
-(void)myMethode:(NSArray*)params
{
UIViewController *vvc = [params objectAtIndex:0];
NSString *strArg = [params objectAtIndex:1];
Class theClass = NSClassFromString(strArg);
id myObject = [[theClass alloc] init];
[vvc.navigationController pushViewController:myObject animated:YES];
}
-(void)NavAndForwardBtn:(UIViewController *)context :(NSString *)vc_name
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:#"Skip" forState:UIControlStateNormal];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button setFrame:CGRectMake(0, 0, 36, 20)];
//[button setImage:[UIImage imageNamed:#"icon_back2.png"] forState:UIControlStateNormal];
// NSArray *params = [NSArray arrayWithObjects:context,vc_name,nil];
[button addTarget:self.leftDelegate action:#selector(context)forControlEvents:UIControlEventTouchUpInside];
// [button addTarget:self action:#selector(myMethode:) forControlEvents:UIControlEventTouchUpInside];
// [button add]
//[button performSelector:#selector(myMethode:) withObject:params afterDelay:15.0];
/// doctory...
// NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
// [context methodSignatureForSelector:#selector(changeImage:)]];
//
// [invocation setTarget:context];
//
// [invocation setSelector:#selector(changeImage:)];
//
// [invocation setArgument:(__bridge void *)(context) atIndex:2];
//
// [invocation setArgument:(__bridge void *)(vc_name) atIndex:3];
//
// [NSTimer scheduledTimerWithTimeInterval:0.1f invocation:invocation repeats:NO];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:button];
context.navigationItem.rightBarButtonItem =barButton;
}
-(void) pushNewVC: (UIViewController *) vvc : (NSString *) str
{
NSString *strArg = str; //[NSString stringWithFormat:string];
Class theClass = NSClassFromString(strArg);
id myObject = [[theClass alloc] init];
[vvc.navigationController pushViewController:myObject animated:YES];
}
#end
I have been trying it for two days but didn't find a way to get self in another class by reference . some says it is not possible to pass by reference but it does whatever you want . I sent self to MyUtil.m class it displays right and left UINavigationItem.leftButton and RightButton.
LeftButton in MyUtil
[leftbutton addTarget:context.navigationController action:#selector(popViewControllerAnimated:)forControlEvents:UIControlEventTouchUpInside]; //it is doing absolutely what i wanted..
Now for rightbutton to open(Push ViewController) i did
[rightbutton addTarget:context.navigationController action:#selector(pushViewController:animated:)forControlEvents:UIControlEventTouchUpInside];
// how can i pass a UIViewController to open. how above function knows what to open
I have tried multiple ways to solve this issue
First method to pass multiple params in selector;
myMethode:(NSArray *)params
Problem 1
Is it possible to send self as &self. and get it in another class .I tried it and in MyUtil.h . I also use ** double pointer but it is not passing.
Problem 2
Is it possible to multiple parameter in my case they are UIViewController and NSString or both be UIViewController
Problem 3
Is there a way to send or attach UIViewController to given statement
Problem 4
I have created a #property(strong,nonatomic)UIViewController *vc;
when i assign self of calling ViewController to vc . it is not working as i expected. is there a way to get self of another ViewController to be be handled and capture in a property of that class.
[button addTarget:context.navigationController action:#selector(pushViewController:animated:) forControlEvents:UIControlEventTouchUpInside];
//how can i told this function to push which ViewController because i am having two UIViewController in function.
Note
I am working in another class name MyUtil. Main problem is i am unable to call [self.navigationController pushViewController:anyVC] out of a function in MyUtils.
Maybe I'm wrong, but why don't you create a Category on UIViewController to handle what you want to do ? What your MyUtil is doing sounds like a category to me and in that case you have access to self for your UIViewController.

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.

EXC_BAD_ACCESS on selector in objective C

I've got a UIBarButtonItem category where I build UIBarButtonItems with custom UIButtons, since I've found UIButtons easier to customize then UIBarButtonItems.
Now, I'd like to continue to use the BarButtonItem's target and action properties instead of using those in the button so that the BarButtonItem can continue to be customized externally without anyone having to know the implementation details (i.e., that it is using a button internally).
Now, in order to do that, I'm written up this code in my category:
+ (UIBarButtonItem *)backBarButtonItemWithColor:(UIColor *)color
{
UIImage *closeIcon = [MyImageUtility navBarBackArrow];
if (color) closeIcon = [closeIcon imageWithColorOverlay:color];
UIButton *close = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, closeIcon.size.width+10.0f, closeIcon.size.height+10.0f)];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:close];
[close setImage:closeIcon forState:UIControlStateNormal];
[close addTarget:item action:#selector(SD_executeBarButtonItemAction) forControlEvents:UIControlEventTouchUpInside];
return item;
}
- (void)SD_executeBarButtonItemAction
{
[self.target performSelector:self.action];
}
Whenever the SD_executeBarButtonItemAction is called, I get a exc_bad_access on the selector, though I am not sure why. Any ideas? Is there a way around this?
Thanks!
EDIT:
here is the code being called by that selector that is crashing:
void (^transition)(void) = ^(void) {
[self.rightContainer setFrame:[self offscreenContainerFrame]];
[self.centerContainer setAlpha:1.0f]; //TODO: this is unreliable in iOS6 -- we should add a view to the top of it to darken
[self.centerContainer setTransform:CGAffineTransformIdentity];
};
[self notifyWillShowPrimaryViewController];
[self performBlock:transition animated:YES completion:^(BOOL finished) {
[self notifyDidShowPrimaryViewController];
[self setForegroundController:self.primaryNavigationController];
if (block != NULL) block(finished);
}];
Your code is a recursive call.
- (void)SD_executeBarButtonItemAction
{
[self.target performSelector:self.action];
}
You set like:
[close addTarget:item action:#selector(SD_executeBarButtonItemAction) forControlEvents:UIControlEventTouchUpInside];
Where item is a UIBarButtonItem.

Issue in iOS7, fine in iOS6

Below is the error i am getting in my app, which is working fine in ios6.
[__NSCFString frame]: unrecognized selector sent to instance 0xc075290
I am not getting what is wrong in it. But i guess something related to UINavigationController. Please guide for above.
Thanks in advance.
UPDATE: After enabling Zombies i get this error.
[_UINavigationBarBackIndicatorView frame]: message sent to deallocated instance 0xc0fb860
-(void)viewWillAppear:(BOOL)animated
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"isAcceptTerms"]) {
[adBannerView setDelegate:self];
[adBannerView setHidden:NO];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"isBannerShown"]) //-ive logic is applied
{
[self.adBannerView setHidden:YES];
[self.adBannerView setDelegate:nil];
}
}
else
{
[adBannerView setDelegate:nil];
[adBannerView setHidden:YES];
}
[self.navigationController.navigationBar setHidden:NO];
NSMutableDictionary *dictTemp =[[sqlmessenger shared]fetchOrders];
int count=[[sqlmessenger shared] isuserdetails];
if (count>0)
{
[self updateCoordinate];
}
NSArray *arrContorl = [self.navigationController.navigationBar subviews];
for(UIButton *btnTemp in arrContorl)
{
if([btnTemp isKindOfClass:[UIButton class]])
{
[btnTemp removeFromSuperview];
}
}
UIImageView *imgHeader= [[UIImageView alloc]initWithFrame:CGRectMake(0,0,320,44)];
[imgHeader setBackgroundColor:[UIColor clearColor]];
[imgHeader setImage:[UIImage imageNamed:#"setting.png"]];
[self.navigationController.navigationBar addSubview:imgHeader];
if(lblHeader)
{
lblHeader=nil ;
}
lblHeader = [[UILabel alloc]initWithFrame:CGRectMake(60,5,230,30)];
[lblHeader setBackgroundColor:[UIColor clearColor]];
[lblHeader setTextAlignment:UITextAlignmentLeft];
[lblHeader setTextColor:[UIColor whiteColor]];
[lblHeader setFont:[UIFont boldSystemFontOfSize:18.0]];
if([dictTemp count]==0 && contentView.hidden == FALSE)
{
[lblHeader setText:#"Terms of Service (EULA)"];
}
else
{
[lblHeader setFrame:CGRectMake(110,5,200,30)];
[lblHeader setFont:[UIFont boldSystemFontOfSize:20.0]];
[lblHeader setText:#"Settings"];
}
[self.navigationController.navigationBar addSubview:lblHeader];
}
Obviously, you are trying to access the frame property of a NSString object, which is not permitted, since this object does not have this property.
Try adding more details. (Add the code that causes the crash, usually crash stack are not that helpful).
UPDATE:
Still not sure what's going on, you need to do the actual debug, plant the necessary breakpoints log your variables, see what values they have etc.
I can give you some things you can try:
1.Not sure why are you adding subviews to the navigation bar. You can instead use the navigationItem property of the UIViewController, and then leftBarButtonItem of UINavigationItem, like :
For left bar button item : (make sure you hide the back button first)
self.navigationController.navigationItem.hidesBackButton = YES;
self.navigationItem.leftBarButtonItem = yourLeftBarButtonItem;
And for the right one :
self.navigationItem.rightBarButtonItem = yourRightBarButtonItem;
2.You're allocating the view and the label each time that viewController is appearing. That's inefficient. Both memory and time-wise. Instead, you can allocate them once and change the alpha channels.

Create a custom UIButton class with delete function

I have a grid of UIButtons. When I hit an 'edit' button, I want a delete button to appear over each of these buttons, which when pressed, deletes the button (and associated data). A bit like apple's home screen, when you hold down a button and it starts to wiggle with an X in the corner.
According to this post: Subclass UIButton to add a property I can use Associative References to add a property to each of my buttons. I've tried to add a UIButton as a property of my custom UIButton but I can't seem to get it to appear and have the feeling this isn't the right way to go. Here's my custom button main:
#import "UIButton+Property.h"
#import <objc/runtime.h>
#implementation UIButton(Property)
static char UIB_DELETEBUTTON_KEY;
#dynamic deleteButton;
- (void)setDeleteButton:(UIButton *)deleteButton {
deleteButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
deleteButton.frame = CGRectMake(100, 100, 50, 50);
objc_setAssociatedObject(self, &UIB_DELETEBUTTON_KEY, deleteButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIButton *)deleteButton {
return (UIButton *)objc_getAssociatedObject(self, &UIB_DELETEBUTTON_KEY);
}
#end
And here's where I add the buttons programmatically:
//Create a custom button for each custom book doc
for (int i = 0; i < [customBookDocs count]; ++i) {
BookDoc *customBookDoc = [customBookDocs objectAtIndex:i];
NSString *bookTitle = customBookDoc.book.title;
//create a button for each book
CGRect frame = CGRectMake(xCoord, yCoord, 200, 200);
UIButton *bookButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
bookButton.bookDoc = customBookDoc;
[bookButton setFrame:frame];
[bookButton setTitle:bookTitle forState:UIControlStateNormal];
[bookButton addTarget:self action:#selector(bookButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
xCoord += 250;
[self.view addSubview:bookButton];
[self.view addSubview:bookButton.deleteButton];
}
Is there an easier more sensible way to do this? Or am I on the right track?
ORIGINAL RESPONSE BEGAN:
... Someone else may have more to say about that, but I'm not sure why you'd need to use object association here. You can certainly add another button to your button as a property using regular subclassing, which is the route that I would take. ...
EDITS BELOW:
I thought that I had subclassed a UI control directly, but I realized that I was mistaken when I went to look for the code. #Joe rightly pointed out in the comments that there are issues with directly subclassing UI controls.
I was able to implement something like the functionality you described without using Associated Objects, by creating a wrapper class to hold the button and its related delete button. It works, but it's not very flexible, so I would generally recommend #Joe's method as a better solution.
Here's the relevant code:
I threw all of the code into the appDelegate to keep it simple. I don't recommend that in real life.
AppDelegate.m:
#implementation AppDelegate
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
UIButton *toggleDeleteButtons = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[toggleDeleteButtons setFrame:CGRectMake(20, 45, 280, 45)];
[toggleDeleteButtons setTitle:#"Toggle Delete" forState:UIControlStateNormal];
[toggleDeleteButtons addTarget:self action:#selector(toggleDeleteButtonAction) forControlEvents:UIControlEventTouchUpInside];
[[self window] addSubview:toggleDeleteButtons];
ButtonWrapper *myButtonWrapper = [[ButtonWrapper alloc] init];
[[myButtonWrapper button] setFrame:CGRectMake(20, 100, 200, 45)];
[[myButtonWrapper button] setTitle:#"This is my button" forState:UIControlStateNormal];
[[myButtonWrapper deleteButton] addTarget:self action:#selector(buttonDeleteRequested:) forControlEvents:UIControlEventTouchUpInside];
[[myButtonWrapper deleteButton] setTag:0];
[[self window] addSubview:[myButtonWrapper button]];
buttonWrapper1 = myButtonWrapper;
// Added instance called anotherButtonWrapper with tag 1, as above
// Added instance called stillAnotherButtonWrapper with tag 2, as above
[self.window makeKeyAndVisible];
return YES;
}
- (void)toggleDeleteButtonAction {
static BOOL deleteButtonsShown;
[buttonWrapper1 showDeleteButton:!deleteButtonsShown];
[buttonWrapper2 showDeleteButton:!deleteButtonsShown];
[buttonWrapper3 showDeleteButton:!deleteButtonsShown];
deleteButtonsShown = !deleteButtonsShown;
}
- (void)buttonDeleteRequested:(UIButton *)deleteButton {
// delete the specified button here
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Delete" message:[NSString stringWithFormat:#"Delete was pressed on button %i",[deleteButton tag]]delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
ButtonWrapper.m:
#implementation ButtonWrapper
#synthesize button;
#synthesize deleteButton;
- (ButtonWrapper *)init {
ButtonWrapper *newWrapper = [ButtonWrapper alloc];
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myButton setFrame:CGRectZero];
UIButton *myDeleteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myDeleteButton setFrame:CGRectMake(0, 0, 100, 40)];
[myDeleteButton setTitle:#"Delete" forState:UIControlStateNormal];
[myDeleteButton setHidden:TRUE];
[myButton addSubview:myDeleteButton];
[newWrapper setButton:myButton];
[newWrapper setDeleteButton:myDeleteButton];
return newWrapper;
}
- (void)showDeleteButton:(BOOL)showButton {
if (showButton) {
[[self deleteButton] setHidden:FALSE];
[[self deleteButton] setEnabled:TRUE]; }
else {
[[self deleteButton] setHidden:TRUE];
[[self deleteButton] setEnabled:FALSE];
}
}
#end
This solution did not require me to implement all of the UI properties, but it did require extra work to hook up the embedded delegates, which is cumbersome. There may be a way to pass the delegates into the wrapper at initialization, but I couldn't make it work.

Resources