Okay, so this is pretty weird; before upgrading to the final GMseed of Xcode my buttons worked perfectly fine...
Now I can't even do them on a super simple scale without the program crashing on click.
The absolute most bizarre aspect is that it works SOME of the time, but like 85%, it just crashes; and when it does work, it'll crash after extensive use.
Here is my code (stripped down to the simplest button implementation I could think of):
#interface TESTViewController ()
{
UIButton *button;
}
#end
#implementation TESTViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self addButton];
[button addTarget:self action:#selector(buttonPressed)
forControlEvents:UIControlEventTouchDown];
}
- (void)addButton
{
button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 80, 45)];
[button setTitle:#"CLICK" forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor blackColor]];
[self.view addSubview:button];
}
- (void)buttonPressed
{
NSLog(#"WORKED");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
TESTViewController *test = [[TESTViewController alloc] init];
[self.window addSubview:test.view];
return YES;
}
I've tried creating the button in the init function, viewDidLoad -- neither works consistently, though viewDidLoad seems to work a little more frequently...
I tried -(void) addButton:(UIButton*)button as well
Using:
[button addTarget:self action:#selector(buttonPressed:)
forControlEvents:UIControlEventTouchDown];
--No difference.
I am at a total loss. I tried looking it up here on the forums, but unfortunately most of it refers to programming with IBActions.
The error I get is bad access
So my guess is that when the button is clicked, it's not actually accessing the buttonPressed function but something else entirely... I have no clue as to why this would be the case...
Thanks for any help!
The problem is in your "didFinishLaunchingWithOptions" method. Here you are creating a local object which gets Deallocated i.e "TESTViewController *test = [[TESTViewController alloc] init]".
You should retain this object, so try making it a property and use "self.test = [[TESTViewController alloc] init]". It will work.
Also conventionally you should use rootViewController rather adding your viewController to windows's subview.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.test = [[TESTViewController alloc] initWithNibName:nil bundle:nil];
self.window.rootViewController = self.test;
[self.window makeKeyAndVisible];
return YES;
}
I think you have a memory leak on that Button object are you using ARC or manual memory management? If manual make sure you retain it. If it is ARC use a property and the compiler will do the rest of the work
#property (nonatomic, strong) UIButton *button;
try it this way
- (void)addButton
{
button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 80, 45)];
[button addTarget:self action:#selector(buttonPressed)
forControlEvents:UIControlEventTouchDown];
[button setTitle:#"CLICK" forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor blackColor]];
[self.view addSubview:button];
}
and remove
[button addTarget:self action:#selector(buttonPressed)
forControlEvents:UIControlEventTouchDown];
this line from viewDidLoad
I am not sure if this is the cause of the crash but actions (like your buttonPressed) should have a sender parameter:
[button addTarget:self action:#selector(buttonPressed:)
- (void)buttonPressed:(id)sender
{
...
}
To define a button programatically use the buttonWithType method, Try this
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self
action:#selector(buttonPressed)
forControlEvents: UIControlEventTouchUpInside];//Any control event type
[button setTitle:#"Show View" forState:UIControlStateNormal];
button.frame = CGRectMake(0, 0, 80, 45);
[view addSubview:button];
Related
Team,
I am trying to bring a menu in top of all visible views during runtime. This menu should be easily addable and removable dynamically in certain conditions.
To do this, I have tried adding a button view to the UIWindow as a subview during runtime.
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:nil forControlEvents:UIControlEventTouchDown];
[button setTitle:#"Show View" forState:UIControlStateNormal];
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
[window addSubview:button];
[window makeKeyAndVisible];
[window bringSubviewToFront:button];
But it doesnt worked. Also I have tried to place this button in the root view controller, but no luck again.
Edit - Note: This code is not from a UIViewController. I am trying to build a library this will be in that library code. Use case be like you could post NSNotification to enable and disable this menu dynamically during runtime.
Please suggest.
Thanks !
An easier way would be add the menu view to the controller's view. You will have some benefits:
It will handle rotation correctly
You do not mess with window
Taking a look to your code, I should suppose that you are using it inside a view controller, thus you do not need to call [window makeKeyAndVisible]. Also, you are not showing which window you are talking about. Are you creating it? are you using the one of your root view controller?
in appdelgate.h
-(void)addMarketOpenCloseIcon;
in appdelgate.m
#pragma mark Add Market Open Close Label
-(void)addMarketOpenCloseIcon
if (lblMarketOpenClose==nil) {
lblMarketOpenClose=[[UILabel alloc ] initWithFrame:CGRectMake(116,20, 80, 21)];
lblMarketOpenClose.textColor=[UIColor redColor];
[lblMarketOpenClose setBackgroundColor:[UIColor clearColor]];
lblMarketOpenClose.font=[UIFont fontWithName:#"Arial" size:12];
[self.window addSubview:lblMarketOpenClose];
}
}
#pragma mark DidFinishLauch
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
//create all table
LoginViewController *login=[[LoginViewController alloc]initWithNibName:#"LoginViewController" bundle:nil];
navigationController = [[ UINavigationController alloc] initWithRootViewController:login];
[self.window setRootViewController:navigationController];
self.navigationController.navigationBarHidden=YES;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
In LoginViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
appdelgate=[[UIApplication sharedApplication]delegate];
[appdelgate addMarketOpenCloseIcon];
}
You want to display something that floats over all of the user's views. You want to implement this in a library to be used generically.
That sounds a lot like a UIAlertView. Why not implement it the same way as UIAlertView? Create a new UIWindow and set its windowLevel to UIWindowLevelAlert. Put your “floating” content in your own UIWindow.
You can find some useful tips for creating a second UIWindow in this Q&A.
Create UIWindow object, add the button on it and then add the UIWindow object into [UIApplication sharedApplication].keyWindow
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
UIWindow *buttonWindow = [UIWindow alloc] initWithFrame: self.view.bounds]
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:nil forControlEvents:UIControlEventTouchDown];
[button setTitle:#"Show View" forState:UIControlStateNormal];
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
[buttonWindow addSubview: button];
[keyWindow addSubview: buttonWindow]
[buttonWindow makeKeyAndVisible]
[backgroundWindow bringSubviewToFront:button];
I created the following class to get a toolbar with colored images
#interface likeUIBarButtonItem : UIBarButtonItem
- (id) initWithStatus: (enum ApartmentLikeStatus)p_status;
#end
and the m file looks like this:
- (IBAction) likeStatus_Clicked: (id) sender
{
self.numberOfClicks++;
[self.statusButton setImage:[self getStatusImage] forState:UIControlStateNormal];
}
- (id) initWithStatus: (enum ApartmentLikeStatus)p_status
{
self.statusButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.statusButton setImage:[self getStatusImage] forState:UIControlStateNormal];
[self.statusButton addTarget:self action:#selector(likeStatus_Clicked:) forControlEvents:UIControlEventTouchUpInside];
[self.statusButton setFrame:CGRectMake(0, 0, 20, 20)];
self = [[likeUIBarButtonItem alloc] initWithCustomView:self.statusButton];
return self;
}
in the view controller I do the following:
likeUIBarButtonItem* stausBtn = [[likeUIBarButtonItem alloc] initWithStatus:self.apartmentToShow.userLikness];
// List of toolbaritems
NSArray* toolbarItems = [NSArray arrayWithObjects: stausBtn, nil];
self.toolbarItems = toolbarItems;
self.navigationController.toolbarHidden = NO;
the button gets displayed but when clicking it i get an assembly code
0x120a09f: movl (%edi), %esi <-- thread 1: EXC_BAD_ACCESS (code=2 address=0x0)
although my statusButton is declared (strong, nonatomic) my guess it gets released somewhere between the init and the call to the function, is that the case or is it something else? and whatever it is how do I fix it?
That's not how you override an init* method.
You might be better off with something like this
- (instancetype)initWithStatus:(enum ApartmentLikeStatus)status
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:[self getStatusImage] forState:UIControlStateNormal];
[button addTarget:self action:#selector(likeStatus_Clicked:) forControlEvents:UIControlEventTouchUpInside];
[button setFrame:CGRectMake(0, 0, 20, 20)];
self = [super initWithCustomView:button];
if (self) {
self.statusButton = button;
}
return self;
}
The problem with your version is that you create a button and assign it to self but then you reassign self to a completely new object at the end. So the initial self probably be released immediately as no one else can get a handle on it, which basically means you are passing a dangling pointer into the initWithCustomView: method
HiI am very new to iphone development.I am java background developer.So my approach is like java .
Here my requirement is that i want to call one global function from every view controller.
and from there i pass self.navigationController as a parameter. In that function i add some buton. when user click on that button it should call one more function and it should carry same object as a parameter.
please give me guidance. i tried as following but it is showing error at compiletime
Utilities.m
+(void)setBacKButton:(UINavigationController *)navigationController
{
for(UIButton *btn in navigationController.navigationBar.subviews){
if([btn isKindOfClass:[UIButton class]]){
[btn removeFromSuperview];
}
}
UIButton *btn2=[UIButton buttonWithType:UIButtonTypeCustom];
btn2.frame=CGRectMake(708, 0, 50, 54);
[btn2 setImage:[UIImage imageNamed:#"btn_back.png"] forState:0];
[btn2 setImage:[UIImage imageNamed:#"btn_back_h.png"] forState:UIControlStateSelected];
// here i need to call bellow function when click on this button
//[btn2 addTarget:self action:#selector() forControlEvents:UIControlEventTouchUpInside];
[navigationController.navigationBar addSubview:btn2];
}
+(void)gotoBack:(UINavigationController *)navigationController
{
[navigationController popViewControllerAnimated:YES];
}
and i call that function from my viewcontroller as
[Utilities setBacKButton:self.navigationController];
please tell me how we can achieve this
add a function in your app delegate.and call that function in all your view controllers.with an object of appdelegate.then with in that function add one button with id sender and do watever u want in the method.
Just make a navigation controller in your app Delegate ... and push your view controller using this.. follow the following code...
//YourAppdelegate.h
UINavigationController *navController;
#property(nonatomic,retain)UINavigationController *navController;
// your appdelegate.m
navController = [UINavigationController alloc]initWithRootViewController:self.viewController];
self.window.rootViewController = navController ;
and now make your global function here (in appdelegate.m) like.......
- (void)setBacKButton:(UINavigationController *)navigationController{
// put your code here
}
now call it from your view controllers where you have needed like this...
// yourViewController.m
import "yourAppdelegate.h"
and when you have to call that function just write these two lines..
YourAppdelegate *appdelegate = (YourAppdelegate*)[[UIApplication sharedApplication]delegate];
[appdelegate setBacKButton:self.navigationController];
may this will help you
you would want to create a category of UINavigationBar.
UINavigationBar+customBackButton.h
#import <UIKit/UIKit.h>
#interface UINavigationBar (customBackButton)
- (void)setCustomBackButton;
#end
UINavigationBar+customBackButton.m
#import "UINavigationBar+customBackButton.h"
#implementation UINavigationBar (customBackButton)
- (void)setCustomBackButton
{
for(UIButton *btn in self.subviews)
{
if([btn isKindOfClass:[UIButton class]])
{
[btn removeFromSuperview];
}
}
UIButton *btn2=[UIButton buttonWithType:UIButtonTypeCustom];
btn2.frame=CGRectMake(708, 0, 50, 54);
[btn2 setImage:[UIImage imageNamed:#"btn_back.png"] forState:0];
[btn2 setImage:[UIImage imageNamed:#"btn_back_h.png"] forState:UIControlStateSelected];
// here i need to call bellow function when click on this button
//[btn2 addTarget:self action:#selector() forControlEvents:UIControlEventTouchUpInside];
[self addSubview:btn2];
}
#end
Then in any view controller you can import it like this
#import "UINavigationBar+customBackButton.h"
[self.navigationController.navigationBar setCustomBackButton];
Following the response from #ewiinnnnn above, I came up with a way to use Custom Categories and have the back button navigate as expected:
// UIViewController+customBackButton.h
#import <UIKit/UIKit.h>
#interface UIViewController (customBackButton)
- (void)setCustomBackButton;
#end
// UIViewController+customBackButton.m
#import "UIViewController+customBackButton.h"
#implementation UIViewController (customBackButton)
// sets my custom back button
- (void)setCustomBackButton
{
UINavigationItem *navItem = self.navigationItem;
UIButton *customButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *addButton = [UIImage imageNamed:#"backButton.png"];
UIImage *addButtonSelected = [UIImage imageNamed:#"backButtonHighlighted.png"];
[customButton setBackgroundImage:addButton forState:UIControlStateNormal];
[customButton setBackgroundImage:addButtonSelected forState:UIControlStateHighlighted];
customButton.frame=CGRectMake(0.0, 0.0, addButton.size.width, addButton.size.height);
[customButton addTarget:self action:#selector(navigateBack) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView: customButton];
navItem.leftBarButtonItem = barButtonItem;
}
// the action that is triggered when the back button is pressed
- (void)navigateBack {
[self.navigationController popViewControllerAnimated:YES];
}
#end
And then within a UIViewController:
// MyViewController.m
#import "UIViewController+customBackButton.h"
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// add back button to navcontroller
[self setCustomBackButton];
}
I have a navigationController which holds a view with some buttons, but when I press a button I get a EXC_BAD_ACCESS error. I can't think what I'm doing wrong as the target is set right. It crashes whether the the button is added programmatically or via IB.
Button Code:
UIButton *reportsButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
reportsButton.frame = CGRectMake(20, 100, 100, 50);
[reportsButton setTitle:#"Reports" forState:UIControlStateNormal];
[reportsButton addTarget:self action:#selector(reportsButtonTouched:) forControlEvents:UIControlEventTouchUpInside];
Function button is trying to access:
- (void)reportsButtonTouched:(id)sender
{
NSLog(#"working");
}
Error:
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); //EXC_BAD_ACCESS code=1
}
The function the button is trying to access exists.
Maybe it's something about the way the NavigationController functions that I'm not aware of, but I've done this before without any problems.
Thanks for any answers, I really do appreciate the help I've got from this site before.
EDIT: This is my AppDelegates didFinishLaunching incase that helps in any way.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *homevc = [[HomeViewController alloc] initWithNibName:#"HomeViewController" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:homevc];
[self.window addSubview:nav.view];
[self.window makeKeyAndVisible];
return YES;
}
Nothing in your code seems to point to any issues. Use this tutorial to make Xcode break on all exceptions. This will take you closer to the 'scene of the crime' rather than crash to main.
I was also getting this error; I had been programming using SpriteKit and implementing my buttons a certain way, and then when I went back to not using the framework, all of a sudden every button I implemented was causing a bad access error.
I found out that I was initializing my buttons in viewDidLoad, because I had been using didMoveToView, and I accidentally treated them as the same function (which to be honest I don't really have a firm grasp on what viewDidLoad actually does).
But when I did this:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
[self addButton];
}
return self;
}
- (void)addButton
{
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 45, 45)];
[button setTitle:#"CLICK" forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonPressed:)
forControlEvents:UIControlEventTouchDown];
[self.view addSubview:button];
}
- (void)buttonPressed:(UIButton *)button
{
NSLog(#"WORKED");
}
Everything worked out fine... So try that, if you're still having trouble.
Good luck!
I've been trying to get a basic app working that doesnt use an XIB. It simply should load and draw a button.
At the moment the screen just appears white with apparently nothing on it.
In my delegate I pass across to my viewController Class.
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[window addSubview:viewController.view];
[window makeKeyAndVisible];
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
return YES;
}
In my viewController class I implement loadview and viewDidLoad. Not sure if loadView is getting called. There is no XIB in this project.
-(void)viewDidload
{
[super viewDidLoad];
UIButton *singlePlayerButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect]
retain];
singlePlayerButton.frame = CGRectMake(50.0, 30.0, 100.0, 30.0);
[singlePlayerButton setTitle:#"Test" forState:UIControlStateNormal];
singlePlayerButton.backgroundColor = [UIColor blueColor];
[singlePlayerButton setTitleColor:[UIColor greenColor] forState:UIControlStateNormal ];
[singlePlayerButton addTarget:self action:#selector(playAction:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:singlePlayerButton];
}
-(void)loadView
{
NSLog(#"loadView");
UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen]
applicationFrame]];
self.view = view;
[view release];
}
Is there something I am missing?
I though this would be really quiet simple but after 2hrs of hitting my head against the wall nothing has changed.
Thanks -Code
Your viewDidload method has a case typo. It should be viewDidLoad (capital L).
Stupid suggestion perhaps, but where in the app delegate are you instantiating viewController?