Can someone please explain to me the uibutton target functionality from this example:
I have a ViewController. I add a uiview with two buttons to this viewcontroller. One button is crated in the init and the other one is created by a method 'addSecondButton'. Both buttons have the same action on [self superview] target which is the ViewController. The code:
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
MySpecialView *myspecialview = [[MySpecialView alloc] initWithFrame:self.view.frame];
[self.view addSubview:myspecialview];
[myspecialview addSecondButton];
}
- (void)specialMethod { NSLog(#"right here!"); }
MySpecialView.m
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 30, frame.size.width, 50)];
[button addTarget:[self superview] action:#selector(specialMethod) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor blueColor];
[self addSubview:button];
}
return self;
}
- (void)addSecondButton {
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 90, self.frame.size.width, 50)];
[button addTarget:[self superview] action:#selector(specialMethod) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor redColor];
[self addSubview:button];
}
So when tapping the blue button which is created in the init, specialMethod does execute. When pressing the red one which is added after the init, the app crashes with the warning - unknow selector for uiview.
What I really don't understand is when I NSLog [self superview] in the init, it is null, because the object is not yet created and returned to the superview, but the action does get executed for some reason.
Thanks for the help!
You have indeed added a nil target for the blue button in initWithFrameMethod. According to the Apple's Event Handling Guide, when you add a nil target, the message (specialMethod in your case) will be passed through the responder chain:
When the user manipulates a control, such as a button or switch, and the target for the action method is nil, the message is sent through a chain of responders starting with the control view.
As your ViewController is a part of the responder chain, it will receive this message and call its specialMethod.
[self superview] is a pointer to ViewController's view, not ViewController itself. The selector you are defining as the action for the button should target the instance of ViewController, not the ViewController's view since you have the method specialMethod defined in ViewController. If you really wanted to use [self superview] as the target for the action, then you'll need to subclass UIView, implement specialMethod and set that new UIView subclass as the view of the view controller (not a subview). This way, [self superview] will refer to a class that actually has the selector specified on the target.
I didn't actually try your code sample, but it's so obviously wrong, if it does work, it's coincidence and shouldn't be relied upon.
Related
Essentially what I'm trying to do here is have a function which sets the alpha of a subview of self.view to 1 (it is hidden by default) which gets called upon pressing a button. i've run into this problem a few times because i declare subviews in viewDidLoad and i don't know of a way to interact with those in a different function (i've tried self.view.subviews[n] but xcode is not happy with that). the only other way i can think of to make this happen is to set self.view equal to the view i want, or maybe even create the view within the function i'm hoping to call with the button (the issue i see with that is potentially the view will not overlay the button, which i want to happen).
here's what i have so far:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIView *firstBackground = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIView *blackOverlay = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
blackOverlay.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.0];
firstBackground.backgroundColor = [UIColor whiteColor];
UIButton *fakeNotifButton = [UIButton buttonWithType:UIButtonTypeCustom];
[fakeNotifButton addTarget:blackOverlay action:#selector(setupFakeNotifScreen:) forControlEvents:UIControlEventTouchUpInside];
//i haven't finished the button yet, that i don't need any help with
}
-(IBAction)setupFakeNotifScreen:(id)sender {
[UIView animateWithDuration:1.0
delay:0.5
options:UIViewAnimationOptionCurveEaseInOut
animations:^{view = 1.0;} //here is where i want to call blackOverlay
completion:^(BOOL finished){}];
}
#end
edit: i figured it out... didn't even need the blackOverlay view. here's the solution for any future google people... there are probably more efficient ways to do this so i would appreciate anybody who's better than me weighing in:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *fakeNotifButton = [UIButton buttonWithType:UIButtonTypeCustom];
[fakeNotifButton addTarget:self action:#selector(setupFakeNotifScreen:) forControlEvents:UIControlEventTouchUpInside];
fakeNotifButton.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width/2) - 80, 300, 160, 40);
[fakeNotifButton setTitle:#"test" forState:UIControlStateNormal];
[fakeNotifButton setBackgroundColor:[UIColor systemGreenColor]];
fakeNotifButton.layer.cornerCurve = #"continuous";
fakeNotifButton.layer.cornerRadius = 15;
[self.view addSubview:fakeNotifButton];
}
-(void)setupFakeNotifScreen:(UIButton *)sender {
[UIView animateWithDuration:0.2
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{self.view.backgroundColor = [UIColor blackColor]; sender.alpha = 0.0;}
completion:^(BOOL finished){}];
}
#end
When you declare
UIView *blackOverlay = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
...that reference is inside the method viewDidLoad, so its existence is confined to that method. It is visible only inside the method, and indeed the reference goes out of existence when the method ends. (The view itself also goes out of existence; you have not put it into the interface.)
If you want a persistent reference to this view, you need to declare a property (or at least an instance variable, aka iva), and set it to this view. You still won't be able to do anything visible with the view if you don't actually put it into the interface.
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.
I’m adding a UISegmentedControl right under the NavigationBar in a UITableViewController. This is the code.
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationBar = self.navigationController.navigationBar;
UIView *segmentView=[[UIView alloc] initWithFrame:CGRectMake(0, self.navigationBar.frame.size.height, self.navigationBar.frame.size.width, 50)];
[segmentView setBackgroundColor:[UIColor whiteColor]];
segmentView.alpha = 0.95;
self.tabSegmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"Favourites", #"All", nil]];
self.tabSegmentedControl.frame = CGRectMake(20, 10, self.navigationBar.frame.size.width - 40, 30);
[self.tabSegmentedControl addTarget:self action:#selector(tabChanged:) forControlEvents:UIControlEventValueChanged];
[segmentView addSubview:self.tabSegmentedControl];
[self.navigationBar addSubview:segmentView];
[self.tabSegmentedControl setSelectedSegmentIndex:1];
}
The view and the SegmentedControl appear on the screen well, but they are not clickable. The selector doesn’t get executed when tapped on the SegmentControl; it doesn’t even switch tabs! In fact, the stuff that is underneath the segmentView (items in the TableView) get clicked when you tap on it. I have tried but failed to understand why this is happening! Any suggestions would be helpful!
You are adding a view below the bounds of its super view. You may see the view however you cannot click it because it is out of bounds. If you set the property of the navigation bar clipsToBounds to YES you should see that the view disappears. What you need to do is add the segment controller to the table view. Here is an example:
- (void)viewDidLoad
{
[super viewDidLoad];
...
[self.view addSubview: self.segmentView]; // need to keep a pointer to segmentView
self.tableView.contentInset = UIEdgeInset(self.segmentView.frame.size.height, 0,0,0);
}
-(void) scrollViewDidScroll:(UIScrollView*) scrollView{
CGRect rect = self.segmentView.frame;
rect.origin = self.tableView.contentOffset;
self.segmentView.frame = rect;
}
I have one tableview in my viewcontroller and in that i have customTableViewCell.i have dynamic buttons which generated runtime in my customeview cell.and on that button's click i want to push my view controller which is holding my tableview controller with the customtableviewcell to another view controller so how can i do it ?
in my custometableviewcell.m file i have this button.
CommentButton = [[UIButton alloc]init];
CommentButton.frame = CGRectMake(CommentLabel.frame.origin.x+CommentLabel.frame.size.width+5, 5, 110, 35);
CommentButton.titleLabel.font = [UIFont systemFontOfSize:12];
[CommentButton setTitle:#"Add Comment" forState:UIControlStateNormal];
CommentButton.backgroundColor = [UIColor clearColor];
[CommentButton addTarget:self action:#selector(GoToComment:) forControlEvents:UIControlEventTouchUpInside];
folowing is my postnotification in customtableviewcell.m file which will called on comment button click
-(IBAction)GoToComment:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:#"GoToComment" object:nil];
}
above is my button on this i have registered on NSNotification which is called the following method
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(GotoComents)
name:#"GoToComment" object:nil];
above line is in viewdidload of my viewcontroll in which i have tableview with that customtableviewcell.
and from customtableview cell i am posting the above notofication wich will push the new view controller
-(void)GotoComents
{
TableTextViewController *settingView=[[TableTextViewController alloc]initWithNibName:#"TableTextViewController" bundle:nil];
[self.navigationController pushViewController:settingView animated:YES];
}
right now i have just did it with the nsnotofication center which just call from my customTableViewcell and invoke the viewcontroller method.
but is it right way to do it?if its not then can anyone please help me how can i achieve this.?
remember that i have dynamic button which generated runtime.so please can any one guide me?
on click button event
-(void)buttonAction:(UIButton*)sender
{
YourViewControllerVC *yvc=[YourViewControllerVC alloc] init];
[self.yournavigatioonController pushViewController:yvc animated:YES];
}
You can declare your TableViewController subclass as custom cell delegate, create GoToComment method inside it, then, instead of
[CommentButton addTarget:self action:#selector(GoToComment:) forControlEvents:UIControlEventTouchUpInside];
do
[CommentButton addTarget:self.delegate action:#selector(GoToComment:) forControlEvents:UIControlEventTouchUpInside];
Add target-action for dynamically generated button:
[myButton addTarget:targetObject action:#selector(actionMethod) forControlEvents:UIControlEventTouchUpInside];
Then implement actionMethod for the targetObject and it will be called whenever button is touched. In this action method you need the following code:
UIViewController *viewController = [[UIViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
you have not described your question correctly ..
you can do what you want this way:
LoginViewController * login_view = [[ LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
UINavigationController *Nav01 = [[UINavigationController alloc] initWithRootViewController:login_view];
Nav01.navigationBar.hidden = YES;
login_view.fromProfileView = FALSE;
[self presentModalViewController:Nav01 animated:YES];
I think what you need is
- (UIViewController *)viewController {
for (UIView* next = [self superview]; next; next = next.superview) {
UIResponder *nextResponder = [next nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return (UIViewController *)nextResponder;
}
}
return nil;
}
With above code, you can find your current view controller inside your custom cell.
You got to add your button in this way :
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self
action:#selector(aMethod:)
forControlEvents:UIControlEventTouchDown];
[view addSubview:button];
where aMethod is an IBAction returning method.
I am new to IOS programming. I am starting from the basic application created by XCode 4.3 (SingleView application).
Continuing from my above question, I was able to add Views programmaticaly to the main ViewController in the Appdelegate but If I want to do the same after the view has loaded (inside viewDidLoad), I am facing the trouble. I tried using the same logic of adding subviews but I am not sure if the view needs a refresh after adding inside 'viewDidLoad'
My views are very simple, here is the code
-(void)viewDidLoad
{
[super viewDidLoad];
UIBUtton *btn = [UIBUtton] alloc] initWithFrame:CGRect(10,10,50,50)];
[self.view addSubView:btn];
[self.view setNeedsDsiplay];
}
Can someone please help?
You are not instantiating the UIButton correctly. You should use buttonWithType: method.
For example:
- (void)viewDidLoad {
[super viewDidLoad];
UIButton * btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setFrame:CGRectMake(10.0, 10.0, 50.0, 50.0)];
[[self view] addSubview:btn];
}
You have placed square brackets at wrong places. Probably you are new to Objective-C. You should pay more attention to learning syntax before creating UI based apps.
UIBUtton *btn = [UIBUtton] alloc] initWithFrame:CGRect(10,10,50,50)];
Should be:
UIBUtton *btn = [[UIBUtton alloc] initWithFrame:CGRect(10,10,50,50)];
Also
[self.view setNeedsDsiplay];
is not needed.