addTarget for another class - ios

I have a UIView that has some buttons that I need to add actions to.
I've tried the this:
HeaderViewController * header = [[HeaderViewController alloc]initWithNibName:#"HeaderViewController" bundle:[NSBundle mainBundle]];
[header.aboutUs addTarget:self action:#selector(aboutUsPage:) forControlEvents:UIControlEventTouchUpInside];
[header.MyLibrary addTarget:self action:#selector(myLibraryPage:) forControlEvents:UIControlEventTouchUpInside];
[self.tableView addParallaxWithView:header.view andHeight:229];
But the button is not responding at all.
What have I done wrong.

Here's the thing, You are creating the view controller and then setting targets and actions for buttons that are referenced as the view controller's property.
I suspect that if you log or put a breakpoint just before you set the target and actions and then step through, the buttons will be nil.
View controllers load their views lazily. So although you have created the view controller, you haven't asked it to load it's view. So the buttons have not been created from the nib. So the properties are nil, and not target action is getting set.
The first option to fixing it is to move the last line so it looks like this:
[self.tableView addParallaxWithView:header.view andHeight:229];
[header.aboutUs addTarget:self action:#selector(aboutUsPage:) forControlEvents:UIControlEventTouchUpInside];
[header.MyLibrary addTarget:self action:#selector(myLibraryPage:) forControlEvents:UIControlEventTouchUpInside];
That way you are calling the view, which will initialise it, and the button parameters should not be nil after this.
The classier way of doing this sort of thing is to define a protocol for responding to clicks on the button, and make the calling view controller a delegate that implements the protocol methods. That way you only have to set the delegate when you create the object, and you don't have to configure the buttons each time.

If you write :
[header.aboutUs setTarget:self];
AND if your class (I mean here self) implements the function aboutUsPage:, it should work.

Related

Triggering a Method from another ViewController using a button in a UIView

The issue I am having is that I want to call a method in a navigationController by clicking a button inside a UIView (footer). When I press the button, it should call the method I'm trying to access to open the Video recorder in the code below.
I was told I could implement a delegate method or use a NSNotification. Below is what I have:
My footer (ESPhotoDetailsFooterView.m) has my button that I created. My footer only contains a UIView.
The method I'm trying to access in my footer resides in (ESTabBarController.m)
This is what I am trying to trigger when pressing my button:
RecorderViewController *viewController = [[RecorderViewController alloc] init];
[viewController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self.navController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self.navController pushViewController:viewController animated:NO];
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:self.navController animated:YES completion:nil];
});
I am new to Objective C and understand the basics. I cannot figure out what I need to do to accomplish this. Any help would be much appreciated.
The code for the button is as follows:
// Create a standard UIButton programmatically using convenience method
UIButton *camButton = [UIButton buttonWithType:UIButtonTypeCustom];
// Set the location (x,y) and size (width,height) of the button
camButton.frame = CGRectMake(9.0f, 8.0f, 35.0f, 35.0f);
// Create UIImages from image resources in your application bundle
// using convenience methods (no need to release)
UIImage *normal = [UIImage imageNamed:#"BingComm"];
UIImage *highlighted = [UIImage imageNamed:#"BingCommClick"];
// Set the button's background to an image
[camButton setBackgroundImage:normal forState:UIControlStateNormal];
[camButton setBackgroundImage:highlighted forState:UIControlStateHighlighted];
// Add the target-action for the touch event
#pragma GCC diagnostic ignored "-Wundeclared-selector"
[camButton addTarget:self action:#selector(btnClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.mainView addSubview:camButton];
Yes, in this case delegation is a good choice, basically you will need a delegate object in your footer view. Then you set the delegate to TabBarController at runtime.
When user clicks the button, call you delegate with your method [delegate method]
This is a very important design pattern objective-c, it would be very helpful if you can follow this tutorial to fully understand delegation.
http://www.alexefish.com/post/522641eb31fa2a0015000002
I agree that delegation is important and very much worth learning, but another way to solve the same problem would be to the following:
In your method definition for btnClicked, call the following code
if([self tabBarController]) {
[[self tabBarController] methodToCall];
}
Since your view controller should be embedded within a tab bar controller, it will have this property set. You can then call any public methods contained within the tabBarController class.

Switch between ViewControllers using a button?

I am very new to programming, especially iOS, so any answer should be given as if I am a baby with ADHD.
This is what I have in my buttonclick:
ItemCreateViewController *itemCreateViewConrtoller = [[ItemCreateViewController alloc] init];
[[self navigationController] pushViewController:itemCreateViewConrtoller animated:YES];
What am I missing?
There are a lot of reasons why this could be happening among which is your button is not triggering the intended action. Among the reason why a button could not trigger is your XIB button is not connected to the button object defined in your header and method file.
If you look at the .h file look at all your IBOutlet items there should be a circle on the left most column if it is darkened out it is connected other wise it is not, and you need to connect this in your XIB file.
If this is not the problem please display the code where you specify the selector it should be something like this:
[cancel_button addTarget:self action:#selector(Cancel:) forControlEvents:UIControlEventTouchUpInside];
Make sure that the selector is an IBAction function -
-(IBAction)Cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:NULL];
return;
}
One other thing, any statement after pushViewController will get executed prior to any actual change in the view. The actual View change only happens once control is returned to IOS. This is significant if your push view controller command is within a long conditional process and you fail to code the return / exit properly.
Since you are using a XIB file for your second view, you need to create it as follows -
ItemCreateViewController *itemCreateViewConrtoller = [[ItemCreateViewController alloc] initWithNibName:#"ItemCreateViewController" bundle:nil]; //Ensure you use correct nib file name
Then you can push it
[[self navigationController] pushViewController:itemCreateViewConrtoller animated:YES];
But since you are using a Storyboard you could have just added the new view to your storyboard, linked the button to the new view with a "push" segue and everything would have been done for you.

View placeholders replacement with xib using the awakeAfterUsingCoder method

I am currently working on an iOS app that has multiple xibs containing a UIView subclass which is acting as a placeholder for the header of the screen.
When the view controllers are loaded from their nib, they instantiate the HeaderView placeholder. In the HeaderView class I have a method that replace the placeholder view by the real one, here is the code :
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
if (![[self subviews] count])
{
if (headerViewNib == nil)
headerViewNib = [UINib frameworkNibWithNibName: #"HeaderView"];
HeaderView * headerView = [[headerViewNib instantiateWithOwner: controller options: nil] lastObject];
[headerView setTranslatesAutoresizingMaskIntoConstraints: NO];
return headerView;
}
return self;
}
Until now, everything works correctly. But now I need to add a menu similar to a popover to the header. So I added a UIButton to the HeaderView xib, as expected it is showing in all the view controllers, but now I need to manage the touch on that button.
I added a "- (IBAction)showHeaderMenu" method to my base UIViewController class, and I configured in the HeaderView.xib the file's owner to this class, so I could connect the button touch action, to the showHeaderMenu action.
The problem is that I am passing "controller" as owner to the instantiateWithOwner:options: method. But this controller is nil. So my touch events on the menu button are not handled.
So now I am searching a way to get the "controller" property set correctly. At the moment in all my view controllers xib, I connected the placeholder headerview "controller" outlet to the file's owner (which is the controller), but it does not seems to be working.
If you have a different pattern to suggest, that does not force me to drop my HeaderView.xib or the placeholders view, i'm open :)
I'm not sure i get exactly your problem but if it is related to your not being able to set up the IBAction through IB because something is not loaded or whatever you can always setup the IBAction event listener through code.
UIButton *yourButton = [[UIButton alloc] init];
[yourButton addTarget:self action:#selector(myMethod:) forControlEvents:UIControlEventTouchUpInside]

How can a NSObject know what view it was drawn in

If I want to extend a UIButton for example and have it present a new view. Can I create a method within my custom UIButton class to do this. If I initialize the button with a target method that ends up switching views it needs to know what view it is currently in.
ExpandedViewController *expandView = [self.storyboard instantiateViewControllerWithIdentifier:#"ExpandedView"];
[self presentViewController:expandView animated:NO completion:nil];
This is what the code would look like in a view controller, but how can I pass the view to the button so that instead of self, it knows what view to target.
The short answer is that yes, this is possible.
The long answer is that you shouldn't do it because it breaks the rules of MVC. Your button is a view and should not be acting as a controller (this is what the controllers are for). Instead, leave the logic to your view controllers.
To get the correct view controller set as the target of your button, you can do this:
In viewDidDisappear: of your view controllers, remove the action for the controller that is going away from the button:
[myButton removeTarget:self
action:#selector(myButtonWasPressed:)
forControlEvents:UIControlEventTouchUpInside];
And then in viewWillAppear: of your view controllers, add the action for the controller that is being presented:
[myButton addTarget:self
action:#selector(myButtonWasPressed:)
forControlEvents:UIControlEventTouchUpInside];
Give your button a containingViewController property and don't forget to set it as you move the button around.

send a message to a parent object form popover

my iOS application use storyboard
it have 2 view controllers:
- main storyboard view controller
- and popover view controller with some objects in it
i've got a button on main view controller and it creates programing every time i run the application:
*CGRect buttonFrame = CGRectMake(10., 10., 120., 50.);
oneButton = [UIButton buttonWithType:UIButtonTypeCustom];
[oneButton setImage:[UIImage imageNamed:[NSString stringWithFormat:#"someImage.png", img]] forState:UIControlStateNormal];
[oneButton setTag:img];
[oneButton setFrame:buttonFrame];
[oneButton addTarget:self action:#selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:oneButton];*
the action of this button show my popover view like that:
*- (void) pressButton:(id)sender {
popoverViewController *popoverFrame = [self.storyboard instantiateViewControllerWithIdentifier:#"myPopoverView"];
popoverWithObjects = [[UIPopoverController alloc] initWithContentViewController:popoverFrame];
[popoverWithObjects presentPopoverFromRect:[sender frame] inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}*
from now the situation is, that i can't send to my button any message or result.
i want to say to my program button (note - i've got only sender of this button action:#selector(pressButton:) ) that popover return some result or some action of an object in popover send anything (string for example)
Or in another words when i interact with any object like button on popover view, i want to change parent buttons title label
Use NSNotificationCenter.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsnotificationcenter_Class/Reference/Reference.html
You need to use delegate. Check out my answer to this similar question from this SO.
Edit: Link to tutorial on Storyboard and delegate pattern usage. And my original answer to delegate on this SO
How about writing a function on your Main View Controller to do what you want. Then call that function from the popover? (i.e. use 'prepare for segue' to send the popover the id of the Main View Controller, and then use that id to call the function on Main View from the popover)

Resources