I create a class for dealing with dismissing keyboard
custom class:
tapReconizer.h:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface tapReconizer : NSObject {
UIView* myView;
}
-(void) tapreg:(UIView*)view;
#end
tapReconizer.m:
#import "tapReconizer.h"
#implementation tapReconizer
-(void) tapreg:(UIView*)view {
myView = view;
UITapGestureRecognizer *tapGestureRecognizer;
tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTap:)];
tapGestureRecognizer.numberOfTapsRequired = 1;
[myView addGestureRecognizer:tapGestureRecognizer];
}
- (void)singleTap:(UIGestureRecognizer *)recognizer {
[myView endEditing:true];
}
#end
and in my viewcontorller:
tapReconizer* reg = [[tapReconizer alloc]init];
[reg tapreg:self.view];
but it's seems singleTap: doesn't been called.
Can I do this all just in this custom class?
Thanks in advance.
Your tapReconizer instance (reg) gets deallocated when it goes out of scope at the end of whatever block it is created in.
You need to keep a strong reference to it. The easiest way is probably to make reg an instance variable.
FYI - you probably meant to name your class TapRecognizer. You misspelled recognizer and it is standard practice to name classes to start with uppercase letters. Method and variable names start with lowercase.
In your View controller
tapReconizer* reg = [[tapReconizer alloc]init];
[reg tapreg:self.view];
Here reg is getting deallocated, so make it is global for strong.
Declare it in your viewcontroller.h
#property (strong, nonatomic) TapReconizer* reg;
or in your .h or .m of view controller
after
#interface ViewController : UIViewController
{
TapReconizer* reg;
}
Hope it will help you.
Related
EDIT:
In my ViewController i am adding multiple buttons to its view.
ViewController1.h
#import <UIKit/UIKit.h>
#interface ViewController1 : UIViewController
-(void)buttonTapped:(id)sender;
#end
ViewController1.m
#import "MyViewCreatorClass.h"
#interface ViewController1 ()
#end
#implementation ViewController1
- (void)viewDidLoad {
[self.view addSubView:[MyViewCreatorClass createButtonWithParentView:self]];
}
-(void)buttonTapped:(id)sender{
NSLog(#"tapped");
}
The implementation for creating the button is inside the class 'MyViewCreatorClass'
MyViewCreatorClass.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "MainViewController.h"
#interface MyViewCreatorClass : NSObject
+ (UIButton*)createButtonWithParentView:(ViewController1*)parentView;
#end
MyViewCreatorClass.m
#import "MyViewCreatorClass.h"
#implementation MyViewCreatorClass
+ (UIButton*)createButtonWithParentView:(ViewController1*)parentView {
UIButton *button = [[UIButton alloc] initWithFrame:....];
//some other implementation (title, color etc.)
[button addTarget:parentView action:(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
return button;
}
#end
At first i had this implementation inside ViewController1 and it worked fine, but now
the warning "undeclared selector 'buttonTapped:'" shows up (this was solved by declaring the method buttonTapped: in ViewController1.h)
the method won't get called as i moved this method into MyViewCreatorClass.
I know that parentView needs to be an instantiated object, and it is. In another method of MyViewCreatorClass i'm also setting some views delegate to parentView and it works.
I even tried to use this method as an instance method not a class method but it did not work either.
What could it be that prevents the method from being called?
Additional question:
One could also use protocols to achieve this (comments). I know how to use protocols, but sadly i don't know how to implement this so a button calls a method on touchUpInside.
Any ideas?
I have a custom view that uses a gesture recognizer that calls a method handleSingleTap. This works fine if the setup for the gesture recognizer and the selector it calls are in the same custom view (CircleView).However, I need the handleSingleTap method in the viewController, so I (1) created a protocol in the custom view (CircleView), (2) created the delegate property in the view, and when I (3)created the gesture recognizer, I set self.delegate for the target of handleSingleTap (rather than just self as it was initially). In the viewController.h, (4) I said that it conformed to the protocol and declared the method handleSingleTap. In the .m file of the view controller, I created a property for the custom view (CircleView) and then in viewDidLoad of the .m file, set self to be the delegate of the custom view. However, the handleSingleTap method is never getting called in the viewController when I tap on the circle in the custom view.
Can you explain why the selector handleSingleTap is not getting called in the viewController?
1) created protocol
#protocol CircleViewDelegate <NSObject>
-(void)handleSingleTap:(UITapGestureRecognizer *) tapper;
#end
2) created delegate property
#property (nonatomic, weak) id <CircleViewDelegate> delegate;
in CircleView
3) created gesture recognizer with target self.delegate
-(void)setUpGestureRecognizers{
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self.delegate action:#selector(handleSingleTap:)];
singleFingerTap.numberOfTapsRequired=1;
[self addGestureRecognizer:singleFingerTap];
}
4) in the viewController.h,
#interface ViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate, CircleViewDelegate>
-(void)handleSingleTap:(UITapGestureRecognizer *) tapper;
in the viewController.m
#property (strong, nonatomic) CircleView *overlay;
viewDidLoad
self.overlay.delegate = self;
This method in the viewController is not getting called
-(void)handleSingleTap:(UITapGestureRecognizer *) tapper {
NSLog(#"never getting called");
}
This is one solution for you. Of course you can do it with Selectors too, but this will work for sure too.
.h
#import <UIKit/UIKit.h>
#protocol CircleViewDelegate <NSObject>
#required
- (void) circleViewPressedByOneTap;
#end
#interface CircleView : UIView
#property (nonatomic,assign) id<CircleViewDelegate> delegate;
- (id)initWithFrame:(CGRect)frame andWithDelegate:(id<CircleViewDelegate>)del;
#end
.m:
#import "CircleView.h"
#interface CircleView () <UIGestureRecognizerDelegate>
#end
#implementation CircleView
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame andWithDelegate:(id<CircleViewDelegate>)del
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
delegate = del;
[self setUpGestureRecognizers];
}
return self;
}
-(void)setUpGestureRecognizers{
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleFingerTap.numberOfTapsRequired=1;
[self addGestureRecognizer:singleFingerTap];
}
-(void)handleSingleTap:(UITapGestureRecognizer *) tapper{
[delegate circleViewPressedByOneTap];
}
And in your ViewController .m:
#import "YouClassViewController.h"
#import "CircleView.h"
#interface YouClassViewController () <CircleViewDelegate>
#end
#implementation YouClassViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
CircleView* yourCircleView = [[CircleView alloc] initWithFrame:CGRectMake(0, 0, 50, 50) andWithDelegate:self];
[self.view addSubview:yourCircleView];
}
- (void) circleViewPressedByOneTap{
NSLog(#"Yeaah you pressed it ! :) ");
}
I have a custom class/object that handles gestures and conducts animations for a given view using a CADisplayLink. In its simplest form my class looks something like follows:
#interface SomeClass : NSObject
#property (strong) UIView *someView;
#end
When I add the following code to my view controller....
- (void)viewDidLoad
{
[super viewDidLoad];
SomeClass *someClass = [[SomeClass alloc] init];
someClass.someView = someView;
}
... I was anticipating my someClass object would be retained for the life of the view controller, since I am using a strong reference to someView.
However someClass is immediately deallocated.
I am already aware that I can overcome the deallocation simply by adding someClass as a property (or indeed iVar) of the view controller however I would ideally like to avoid this extra work...
so is there anyway I can have my class retained until either the view or view controller its associated with are deallocated?
EDIT
UIGestureRecognizer objects are an exmaple of a class that doesn't get deallocated when I associate them with a view...
- (void)viewDidLoad
{
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] init];
[someView addGestureRecognizer:gestureRecognizer];
}
// tapGestureRecognizer still lives
Presumably this is because the UIView takes owner ship of the UIGestureRecognizer object. Is there anyway to achieve this with my class and a UIView category? I.e....
- (void)viewDidLoad
{
[super viewDidLoad];
SomeClass *someClass = [[SomeClass alloc] init];
[someView addSomeClass:someClass];
}
If you want to associate the object with a UIView in the same way a UIGestureRecognizer does then this is technically possible using associatedObjects as follows (but I'm not sure I'd advocate this approach since associatedObjects are often frowned upon)...
SomeClass.h
#class SomeClass;
#interface UIView (SomeClass)
- (void)addSomeClass:(SomeClass *)someClass;
- (void)removeSomeClass:(SomeClass *)someClass;
#end
#interface SomeClass : NSObject
#property (strong) UIView *someView;
#end
SomeClass.m
#import "SomeClass.h"
#import <objc/runtime.h>
#implementation UIView (AssociatedObject)
- (void)addSomeClass:(SomeClass *)someClass
{
NSMutableArray *someClasses = [self someClasses];
if (someClasses == nil) {
someClasses = [[NSMutableArray alloc] init];
[self setSomeClasses:someClasses];
}
[someClasses addObject:someClass];
}
- (void)removeSomeClass:(SomeClass *)someClass
{
NSMutableArray *someClasses = [self someClasses];
if (someClasses != nil) {
[someClasses removeObject:someClass];
if (someClasses.count == 0) {
[self setSomeClasses:nil];
}
}
}
#pragma mark - Private Methods
- (NSMutableArray *)someClasses
{
return (NSMutableArray *)objc_getAssociatedObject(self, #selector(someClasses));
}
- (void)setSomeClasses:(NSMutableArray *)someClasses
{
objc_setAssociatedObject(self, #selector(someClasses), someClasses, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
#implementation SomeClass
#end
Implementation
- (void)viewDidLoad
{
[super viewDidLoad];
SomeClass *someClass = [[SomeClass alloc] init];
someClass.someView = someView;
[someView addSomeClass:someClass];
}
Some further reading on associatedObjects from NSHipster...
http://nshipster.com/associated-objects/
But you can declare SomeClass instance instead of property like this:
#implementation ViewController
{
SomeClass* _someClass;
}
...
- (void)viewDidLoad
{
[super viewDidLoad];
_someClass = [[SomeClass alloc] init];
_someClass.someView = someView;
}
Your SomeClass instance is holding a strong reference to the someView, but nothing is holding a reference to the SomeClass instance except the local variable inside your viewDidLoad message, so as soon as the method exits, that instance can be deallocated. As that was the object holding the only reference to your UIView the view can also be deallocated.
Your only options are to store the reference to the SomeClass object in an instance variable (or iVar) as stosha suggested or in a property. Properties are the preferred method and with automatic synthesis they don't take much more effort than a local variable declaration.
You can declare the property inside the .m file so that it isn't visible to other classes that reference your ViewController class -
In your ViewController.m file -
#interface ViewController ()
#property (strong, nonatomic) SomeClass *someClass;
#end
#implementation ViewController
...
(void)viewDidLoad
{
[super viewDidLoad];
self.someClass = [[SomeClass alloc] init];
self.someClass.someView = someView;
}
You can download code example from this libk:
https://github.com/mriddi/ExempleCustomGestureRecognizer.git
I create my cusom gesture recognizer class like this:
#protocol MyGestureRecognizerDelegate <UIGestureRecognizerDelegate>
#optional
-(void) willDo;
#end
#interface MyGestureRecognizer: UIGestureRecognizer
{
…
}
#property (nonatomic, assign) id <MyGestureRecognizerDelegate> delegate;
…
#end
#implementation MyGestureRecognizer
#synthesize delegate;
…
-(void) call{
[delegate willDo];
}
…
#end
My ViewController adopt MyGestureRecognizerDelegate protocol.
In ViewController I create instances of my class:
MyGestureRecognizer * grFerst;
MyGestureRecognizer * grSecond;
grFerst.delegate=self;
grLeft.delegate=self;
[self.view addGestureRecognizer: grFerst];
[self.view addGestureRecognizer: grSecond];
I want to make both gesture recognizers instances to work simultaneously.
I try to add to my ViewController method
shouldRecognizeSimultaneouslyWithGestureRecognizer
but this method never calls, I checked it using NSLog function.
Please help me solve this problem (allow work both gesture recognizers simultaneously).
Maybe you should allocate the Gestures
MyGestureRecognizer * gesture = [[MyGestureRecognizer alloc] initWithTarget:self action:nil];
gesture.delegate = self;
[self.view addGestureRecognizer:gesture];
Or you could use the control wheel as the target and do all your angle calculations in there. Then you delegate back to the mainViewController.
I am having trouble calling the the popoverControllerDidDismissPopover method in as much as I do not know where to put it and how to call it.
I have created a popover as follows -
// SettingsViewController.h
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "ViewController.h"
#import "SharedData.h"
#import "PlayerPopUpVC.h"
#interface SettingsViewController : UIViewController <UITableViewDataSource, UIPopoverControllerDelegate> {
- (IBAction)popUp:(id)sender;
#property (strong, nonatomic) UIPopoverController *playerPopUpVC;
#property (strong, nonatomic) PlayerPopUpVC *popUp;
// SettingsViewController.m
#import "SettingsViewController.h"
- (IBAction)popUp:(id)sender {
UIButton *editPlayers = (UIButton *)sender;
if(self.playerPopUpVC) {
self.popUp= [[PlayerPopUpVC alloc] initWithNibName:#"PlayerPopUpVC" bundle:nil];
self.popUp=[[UIPopoverController alloc] initWithContentViewController:self.popUp];
}
[self.playerPopUpVC presentPopoverFromRect:[editPlayers frame] inView:[editPlayers superview] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
I know I have to set the delegate of my PopOver to self in order to call the method but cannot work out what the code is.
I have tried -
self.playerPopUpVC.delegate=self
but Xcode does not like it.
My popOver class looks like this -
// PlayerPopUpVC.h
#import <UIKit/UIKit.h>
#interface PlayerPopUpVC : UIViewController <UITableViewDataSource, UIPopoverControllerDelegate> {
}
// PlayerPopUpVC.m
#import "PlayerPopUpVC.h"
#interface PlayerPopUpVC ()
#end
- (void)viewDidLoad
{
[super viewDidLoad];
self.modalInPopover = NO;
self.contentSizeForViewInPopover = CGSizeMake(240, 400);
}
Any help would be most welcome. I have spent a week now trying to sort it.
First, you need to understand the delegate pattern, which seems that you dont fully understand yet.
The popover will be the one which will call the popoverControllerDidDismissPopover method on the delegate. You only have to implement the UIPopoverControllerDelegate protocol in your class and assign yourself as the delegate of the popover. Why do you say that XCode doesn't like it? please, provide more info.
Furthermore, you are making an incorrect assignment here:
self.popUp=[[UIPopoverController alloc] initWithContentViewController:self.popUp];
Edit: Provided more code to help with the error. Please, review the delegate pattern next time before making these questions.
Your SettingsController.m should have this instead:
- (IBAction)popUp:(id)sender {
UIButton *editPlayers = (UIButton *)sender;
if(!self.popUp) {
self.popUp= [[PlayerPopUpVC alloc] initWithNibName:#"PlayerPopUpVC" bundle:nil];
}
self.playerPopUpVC=[[UIPopoverController alloc] initWithContentViewController:self.popUp];
self.playerPopUpVC.delegate = self;
[self.playerPopUpVC presentPopoverFromRect:[editPlayers frame] inView:[editPlayers superview] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
// Your code here
}