How to pass self to another class in iOS - ios

I have to create a button multiple times on different ViewControllers and add the event to it. What i have done i created a class to handle them. I pass my UINavigationController to it and add a button to it. but nothing happens. i mean the button doesn't display. i call my Util class like
MyUtil *util = [[MyUtil alloc];
[util initWithNavAndAddBackButton:self.NavigationController];
// now it should display my custom back button which i have created but it is not displayed.
// i have passed nav by value and add a button to it but don't know why it is not showing it.
MyUtil.h
-(void)initWithNavAndAddBackButton:(UINavigationController *)nav;
MyUtil.m
-(void)initWithNavAndAddBackButton:(UINavigationController *)nav
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:#"icon_back.png"] forState:UIControlStateNormal];
// [btn setTarget:self action:#selector(popView:) forControlEventTouchUPInside];
// how can i pass self(Context in Android) that for which activity it is calling for
[button setFrame:CGRectMake(0,0,36,20);
UIBarButtonItem *barbtn = [[UIBarButton alloc] initWithCustomView:btn];
nav.navigationController.navigationItem.leftBarButtonItem = barbtn;
}
-(void) popView:(UINavigationController *)nav
{
[nav popViewControllerAnimated:YES];
}
Problems i have
First i have pass UINaviagation and add the UIBarbutton to it but it is not displaying .When I write the same code in each ViewController class . it is running accurately. I simply want to do this in a seperate class like a handler.
And i also want to push and pop in that activity. How can i pass self that or i mean how does this class knows the target that for which class this action belongs to. How can i pass the event for the ViewContoller.

Try returning your navigation controller instance to ViewController from MyUtil.
MyUtil.h
-(UIBarButtonItem *)initWithNavAndAddBackButton();
MyUtil.m
-(UIBarButtonItem *)initWithNavAndAddBackButton()
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:#"icon_back.png"] forState:UIControlStateNormal];
[button setFrame:CGRectMake(0,0,36,20);
UIBarButtonItem *barbtn = [[UIBarButton alloc] initWithCustomView:btn];
return barbtn;
}
ViewController.m
MyUtil *util = [[MyUtil alloc];
self.navigationItem.rightBarButtonItem = [util initWithNavAndAddBackButton:self.NavigationController];
Because when you pass an object, it is not passed by reference.

MyUtil.m
+ (void)setNavigationItem:(UINavigationItem *)navItem forViewController:(UIViewController *)controller
{
UIImage *image = [UIImage imageNamed:#"icon_back.png"];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:image
style:UIBarButtonItemStylePlain
target:controller
action:#selector(popView)];
navItem.leftBarButtonItem = item;
}
ViewController.m
[MyUtil setNavigationItem:self.navigationItem forViewController:self];
- (void)popView
{
[self.navigationController popViewControllerAnimated:YES];
}

MyUtil *util = [[MyUtil alloc];
[util initWithNavAndAddBackButton:self.NavigationController];
Should be
MyUtil *util = [[MyUtil alloc] init];
[util initWithNavAndAddBackButton:self.NavigationController];
You are used with Android. In iOS is diffrent.
self.navigationController takes care of pushing and pop viewcontrollers
If you want a custom button to be used everywhere you can use the delegate method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyButtonHere:button];
}
}

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.

How to push New View Controller From any dynamic button click which is in CustomTableViewCell?

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.

Make modal push segue programmatically — Objective-c

I have a button, which when is pressed, adds a new subview with other buttons on it. I have done like this.
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget: #selector(newView)];
- (void)newView
{
UIView *newView = [[UIView alloc]initWithFrame:CGRectmake(0,0,100,100)];
[self.view addSubview:newView];
}
Now when the button is pressed the new view is just added. I want to animate the new view like I can do in the IB, with push segue modal style. How can I do that programmatically?
You can do that with performSegueWithIdentifier, here is the code
- (void)newView
{
//execute segue programmatically
[self performSegueWithIdentifier: #"MySegue" sender: self];
}
Try something like this.
- (void)newView
{
[self.view addSubview:newView]
newView.frame = // somewhere offscreen, in the direction you want it to appear from
[UIView animateWithDuration:10.0
animations:^{
newView.frame = // its final location
}];
}
You can present a new viewcontroller like this:
- (void)newView
{
//Embed your new View into a plain UIViewController
UIView *newView = [[UIView alloc]initWithFrame:CGRectmake(0,0,100,100)];
UIViewController *yourNewViewController= [[UIViewController alloc] init];
controller.view = newView;
//Present it animated
[self presentViewController:yourNewViewController animated:YES completion:nil];
}
Hope it helps! :)

How to interrupt viewWillAppear hierarchy

I have a rootViewController, in it's viewDidLoad method, I initialized another two ViewController2* object and their views as subview of rootViewController.view, then I set first ViewController2* controller.view.hidden = YES.
Then, on v1 has a button handler, when touch it, it present a UINavigationController, after that touch 'dismiss' button call dismissViewControllerAnimated on v1.
The question is: when dismiss complete, the two of ViewController2* fire viewWillAppear. How to make it only fire the viewWillAppear on the visible one, but not on the hidden one?
the rootViewController's implementation:
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.v1 = [[ViewController2 alloc] init];
self.v1.title = #"v1";
[self.view addSubview:self.v1.view];
self.v1.view.hidden = YES;
self.v2 = [[ViewController2 alloc] init];
self.v2.title = #"v2";
[self.view addSubview:self.v2.view];
UIButton * btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setTitle:#"POP" forState:UIControlStateNormal];
[btn sizeToFit];
[btn addTarget:self action:#selector(touchHandler:) forControlEvents:UIControlEventTouchDown];
[self.view addSubview:btn];
}
- (void)touchHandler:(id)sender {
UINavigationController * nc= [[UINavigationController alloc] initWithRootViewController:[[UIViewController alloc] initWithNibName:nil bundle:nil]];
((UIViewController *)[nc.viewControllers objectAtIndex:0]).navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"dismiss" style:UIBarButtonItemStyleBordered target:self action:#selector(dismissHandler:)];
[self presentViewController:nc animated:YES completion:nil];
}
- (void) dismissHandler:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
ViewController2:
#implementation ViewController2
- (void)viewWillAppear:(BOOL)animated
{
NSLog(#"%#",self.title);
}
#end
viewWillAppear will fire on your UIViewController's, even if the view controllers view is set hidden=YES.
You can surely test if (self.view.hidden == YES) in your viewWillAppear delegate method if you want to prevent some expensive operation from occurring, but beware that if you later make that view un-hidden, that viewWillAppear won't fire then.
Simple, the reason why those methods are called is because the viewController's view is part of the main window's view hierarchy. This means that it has a superview that has a superview that has a superview and so on until that superview is the main window.
Instead of hiding and unhiding the viewController views, you should instead add and remove them from their superview. Also, to make sure that viewWillAppear and viewDidAppear are called correctly at the correct times, take a look at ViewController Containment:
http://www.cocoanetics.com/2012/04/containing-viewcontrollers/

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