NSNumber instance will not retain value - ios

Im using an nsnumber as a counter but its not retaining its value. It may be a pointer issue because its showing up as 13 in my init statement but its null in my tap. I'm not sure where or why this is happening. I've never really messed with the automatically generated _instanceValue for my properties. I've always used self.propertyName as an accessor. I believe this may have something to do with it.
header file
#interface PezStoryViewer : UIViewController
#property(nonatomic, strong) NSNumber *counter;
-(id)initWithScene:(NSArray *)scenes;
+(void)viewTapped:(UIGestureRecognizer *)gesture;
+ (id)sharedInstance;
#end
m file
-(id)initWithScene:(NSArray *)scenes{
self = [super init];
if (self) {
//reverse array order
scenes = [[scenes reverseObjectEnumerator] allObjects];
//set counter
_counter = [[NSNumber alloc] initWithUnsignedInteger:[scenes count]];
NSLog(#"_counter = %#", _counter);
//initialize subviews
//and other stuff
}
return self;
}
-(void)viewTapped:(UIGestureRecognizer *)gesture{
NSLog(#"tap");
NSLog(#"_counter = %#", _counter);
NSUInteger i = [_counter unsignedIntegerValue];
i--;
NSLog(#"counter = %d", i);
if ([gesture.view isKindOfClass:[PezFrontImageView class]]) {
//3 Steps
//1: pop off subview
[gesture.view removeFromSuperview];
//2: if next view is a video, play it
//3: initialize new frontView under the existing backView
}
if (i==7) {
[self dismissViewControllerAnimated:NO completion:nil];
}
_counter = [NSNumber numberWithUnsignedInt:i];
}
+ (id)sharedInstance {
static id sharedInstance;
#synchronized(self) {
if (!sharedInstance)
sharedInstance = [[PezStoryViewer alloc] init];
return sharedInstance;
}
}
tap gesture on my subview that is of type UIImageView custom class
//add tapGesture
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:[PezStoryViewer sharedInstance] action:#selector(viewTapped:)];
[self addGestureRecognizer:tapGesture];

Please note that your sharedInstance method does not call initWithScene:. It just calls init. So you never initialize the counter property.
You really shouldn't be setting up your view controller as a singleton. Just create the view controller when needed and use the initWithScene: method.

Related

Show custom View issue

when I click on button I get only 1 extra view, but I want a the view to be added dynamically on number of click, (like for loop)...Kindly help me with this code, Thanks in Advance,
this is just a part of my code
if (boolVal == true) {
CGRect newFrameC = CGRectMake(_centreView.frame.origin.x, _centreView.frame.origin.y, _centreView.frame.size.width, 50);
CGRect newFrameL1 = CGRectMake(_label2.frame.origin.x, _label2.frame.origin.y+50, _label2.frame.size.width, 50);
_centreView.frame = newFrameC;
_label2.frame = newFrameL1;
boolVal = false;
}else if (boolVal == false){
CGRect newFrameC = CGRectMake(_centreView.frame.origin.x, _centreView.frame.origin.y, _centreView.frame.size.width, 1);
CGRect newFrameL1 = CGRectMake(_label2.frame.origin.x, _label2.frame.origin.y-50, _label2.frame.size.width, 50);
_centreView.frame = newFrameC;
_label2.frame = newFrameL1;
boolVal = true;
}
DummyViewController.m
#import "DummyViewController.h"
#import "ExtraView.h"
#interface DummyViewController ()
#property (nonatomic) unsigned int numberOfExtraViews;
#property (nonatomic, strong) NSMutableArray<ExtraView*>* extraViews;
#property (nonatomic, strong) UILabel* label1;
#property (nonatomic, strong) UILabel* label2;
#end
#implementation DummyViewController
-(void) removeExtraViews{
for (ExtraView* extraView in _extraViews){
[extraView removeFromSuperview];
}
[_extraViews removeAllObjects];
}
-(CGRect) getExtraViewFrame{
CGRect extraViewFrame = _label1.frame;
if (_numberOfExtraViews > 0) {
extraViewFrame = [_extraViews lastObject].frame;
}
extraViewFrame.origin.x += extraViewFrame.size.height;
return extraViewFrame;
}
- (void) addExtraViews{
//[self removeExtraViews];
int numberOfExtraViewsToDraw = _numberOfExtraViews - _extraViews.count;
for (int iterator = 0; iterator < numberOfExtraViewsToDraw; iterator ++){
CGRect extraViewFrame = [self getExtraViewFrame];
ExtraView *extraView = [[ExtraView alloc]initWithFrame:extraViewFrame];
[self.view addSubview:extraView];
[_extraViews addObject:extraView];
}
if (numberOfExtraViewsToDraw > 0) {
CGRect label2Frame = [_extraViews lastObject].frame;
label2Frame.origin.x += label2Frame.size.height;
_label2.frame = label2Frame;
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self addExtraViews];
//rest of your code
}
//Use this code in initWithNib/initWithCoder. Don't copy paste the same
-(instancetype)init{
self = [super init];
if (nil != self){
_numberOfExtraViews = 0;
_extraViews = [[NSMutableArray alloc]init];
}
return self;
}
//the function that gets hit when the button is tapped.
- (void) onButtonTap{
_numberOfExtraViews++;
[self.view setNeedsDisplay];
}
Note the following:
I do not understand what the boolVal is, but if it keeps toggling upon the click of the button, you'll never have multiple views.I assumed it toggles, so I havent used it at all.
Use the init code in initWithNib/initWithCoder. Don't copy paste the same. Don't overwrite the given code. Just append to your current init.
In my code onButtonTap is the function that gets hit when the button is tapped.
I've done it by ensuring viewDidLoad method gets hit agin. But I dont think it's necessary. You can also do this:
//the function that gets hit when the button is tapped.
- (void) onButtonTap{
_numberOfExtraViews++;
[self addExtraViews];
}
What has been done:
Let me call what you call as centerView as extraView. There will be many extraViews, so I'll create an array for the same (extraViews).
The count of the number of views is stored in numberOfExtraViews. Initiated to 0 in init.
Whenever the button is tapped, we increase the count and call view's setNeedsDisplay, which in turn hits the viewDidLoad method
In viewDidLoad, we add the extraViews.

How to fix error on handling PanGestureRecognizer when called from another class?

I want to add PanGestureRecognizer to UIView of UIViewController. I'm dividing view on nine equal parts and create an UIViews by them. Also I added PanGestureRocgnizer on every part. When I call it in another class where I need it it works fine but when I touch on the screen in some part to start handle method for PangestureRecognizer I get the Error: [UIView handlePanGestureRocognizer:]: unrecognized selector sent to instance 0x17de2ee0. This is my code:
#import "CustomGestureRecognizer.h"
#implementation CustomGestureRecognizer
#synthesize arrayOfPatternsForComparison;
#synthesize arrayOfSubviews;
#synthesize arrayOfPanGestureRecognizers;
- (void)initialize:(UIView *)view {
arrayOfPatternsForComparison = [[NSMutableArray alloc] init];
arrayOfSubviews = [[NSMutableArray alloc] init];
arrayOfPanGestureRecognizers = [[NSMutableArray alloc] init];
[self createPatternsForComparison];
[self splitScreenInParts:view];
[self setPanGestrueRecognizersOnEveryPartOfTheScreen];
}
- (void)createPatternsForComparison {
}
- (void)splitScreenInParts:(UIView *)view {
CGSize onePart = CGSizeMake(view.frame.size.width/3, view.frame.size.height/3);
CGFloat x_positionOfPart = 0;
CGFloat y_positionOfPart = 0;
for (NSUInteger j=0; j<3; j++) {
for (NSUInteger i=0; i<3; i++) {
UIView *_view = [[UIView alloc] initWithFrame:CGRectMake(x_positionOfPart, y_positionOfPart, onePart.width, onePart.height)];
[[_view layer] setBorderWidth:1.0];
[[_view layer] setBorderColor:[UIColor redColor].CGColor];
x_positionOfPart += onePart.width;
[arrayOfSubviews addObject:_view];
[view addSubview:_view];
}
y_positionOfPart += onePart.height;
x_positionOfPart = 0;
}
}
- (void)setPanGestrueRecognizersOnEveryPartOfTheScreen {
for (UIView *view in arrayOfSubviews) {
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:view action:#selector(handlePanGestureRocognizer:)];
[[panGestureRecognizer view] setTag:[arrayOfSubviews indexOfObject:view]];
[view addGestureRecognizer:panGestureRecognizer];
}
}
- (void)handlePanGestureRocognizer:(UIPanGestureRecognizer *)sender {
NSLog(#"%d", [[sender view] tag]);
}
#end
and in another class of UIViewController I called it like this:
- (void)viewWillAppear:(BOOL)animated {
CustomGestureRecognizer *cgr = [[CustomGestureRecognizer alloc] init];
[cgr initialize:[self view]];
}
How to fix this? Thanks for your answers.
Because cgr is released when you call it.
You have to create a var CustomGestureRecognizer *cgr in .h and then:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
cgr = [[CustomGestureRecognizer alloc] init];
[cgr initialize:[self view]];
}
And then change this line
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:view action:#selector(handlePanGestureRocognizer:)];
to
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGestureRocognizer:)];
The problem is with this line.
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:view action:#selector(handlePanGestureRocognizer:)];
Here you are passing the target as "view" which is here a subview. The target should be a viewController object like "self" or any other viewController object.
And also here you are just creating 9 views (I think in NSObject subclass) and adding to an array, but those are not added to any view as sub views. For this you can return that array to your viewController, and there add pan gesture to those views with self as target. Then it'll work.
I solved it by adding var *CustomGestureRecognizer cgr in .h of another class like anhtu said and change
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:view action:#selector(handlePanGestureRocognizer:)];
target to self like Dev said.

Custom UIView and setting and getting custom value back from variable inside UIView

I have a custom uiview where i have a setter and a getter when the uiview is dynamically created i set this value like this:
for(NSDictionary *dictCategory in arrCategoryList)
{
NSString *strCategoryId = [dictCategory objectForKey:#"CategoryId"];
NSString *strCategoryName = [dictCategory objectForKey:#"Name"];
NSLog(#"%# : %#",strCategoryId,strCategoryName);
UIViewMenuItem *linkMenu = [[UIViewMenuItem alloc] init];
[linkMenu setFrame:CGRectMake(10, i+1, 300, 35)];
[linkMenu setId:strCategoryId]; //here i set the value in the custom uiview
linkMenu.layer.zPosition = 7;
[viewSlide3 addSubview:linkMenu];
[linkMenu setBackgroundColor:[UIColor blueColor]];
linkMenu.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:.9];
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSingleTap:)];
[linkMenu addGestureRecognizer:singleFingerTap];
UILabel *labelMenu = [[UILabel alloc] init];
[labelMenu setFrame:CGRectMake(20, 0, 300, 35)];
[labelMenu setFont:[UIFont systemFontOfSize:16]];
[labelMenu setTextColor:[UIColor whiteColor]];
[linkMenu addSubview:labelMenu];
[labelMenu setText:strCategoryName];
i = i + 35 + 1;
}
Now when i tap on the custom uiview i want to get back the value from the custom uiview so I'm doing this:
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {
CGPoint location = [recognizer locationInView:[recognizer.view superview]];
CGPoint touchPoint=[recognizer locationInView:[recognizer.view superview]];
UIViewMenuItem *tempView = (UIViewMenuItem *)recognizer.view;
NSNumber *tag = [NSNumber numberWithInt:tempView.tag];
NSString *idCat = [tempView getCatId];
NSLog(#"TAG %#",idCat);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString: [NSString stringWithFormat:#"http://localhost:8888/MAMP/WHFC/SubCategories.php?categoryid=%d", idCat]]];
int i = 0;
NSError *e;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&e];
NSArray *arrCategoryList = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&e];
UIViewController *viewController = [[UIViewController alloc] init];
viewController.view.backgroundColor = [UIColor whiteColor];
UIView *uiView = [[UIView alloc] init];
[uiView setFrame:CGRectMake(0, 480, 320, 480)];
[uiView setBackgroundColor:[UIColor grayColor]];
viewController.view = uiView;
UITableView *uiTableView = [[UITableView alloc] init];
[uiTableView setFrame:CGRectMake(0, 0, 320, 480)];
[uiView addSubview:uiTableView];
[self presentViewController:viewController animated:YES completion:nil];
//Do stuff here...
}
But i keep get the same value "13" from NSString *idCat = [tempView getCatId];
This is the custom UIView class:
#import <UIKit/UIKit.h>
#interface UIViewMenuItem : UIView
- (void) setId: (NSString *) cId;
- (NSString *) getCatId;
#end
NSString *catId;
#import "UIViewMenuItem.h"
#implementation UIViewMenuItem
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void) setId: (NSString *) cId;
{
catId = cId;
//If possible, set things up for the new word
}
- (NSString *) getCatId{
return catId;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
There is nothing wrong, in principle, with using a hidden (not visible from outside the class) instance variable cId and providing getters and setters.
But yours isn't an instance variable. It is declared somewhere between the interface and the implementation. It's a global variable or static. (I am not 100% positive about the global, but I think it is. I ran into a linker issue - dublicate object - once when I did the same mistake and did it in two classes using the same variable name. However, it is not that important whether it is global or just static. Being static is bad enough.)
This means at least that all the instances of UIViewMenuItem share the same variable (!!!).
First:
Move it to somewhere between #implementation and its #end.
Then it should work as expected.
Then:
Be lazy and solve it the objective-c-way. Get rid of the variable and get rid of the getter and setter.
If you are happy with the variable being public (it's accessible though getter and setter anyway) then just add a #property (nonatomic, retain) NSString *cid; to the interface. Unless you've got an older compiler then that is basically it. The compiler will add a getter and a setter (getCId and cId) automatically.
The compiler will add an iVar _cId that you would not use yourself in most cases. I am not 100% positive whether the name of the iVar will be cId or _cId when you use the comfortable way. If you care then you can conrol the name of the iVar by adding an #synthesize statment to the implementation and define the iVar name as you like. You could add much more customization (again declaring the iVar yourself, providing custom getters and setters) but there is no need for that when all you need from the property is what you have shown in your question and its code examples.

iOS: Dereferencing NSMutableArray

When someone votes down the question... kindly please also comment why you had voted it down.
I have a UIView (myHolderView) on which I want to place 9 or more other views.
I added some UIView (MyView) instances to an NSMutableArray of 9 elements (MyArray).
MyView has a label (UILabel) on it. Can someone please assist how to get label's text using MyArray?
Note:
I am seeing the UIViews added to main holder view and getting labels on it.
initWithFrame:(v,v,v,v) number: (value) is the init method I had overloaded init with.
//These two are declared as global variable
NSMutableArray *tiles;
MyView *tile;
#implementation ViewController
#synthesize myHolderView;
- (void)viewDidLoad
{
[super viewDidLoad];
tiles=[[NSMutableArray alloc]initWithCapacity:9];
for (int xIndex=0; xIndex<3; xIndex++)
{
for(int yIndex=0; yIndex<3; yIndex++)
{
static int label=0;
[tiles addObject:[[MyView alloc]initWithFrame:CGRectMake(value,value,value,value)
number:[[NSString alloc] initWithFormat:#"%d",label+1]]];
[self.myHolderView addSubview:tiles[label]];
//Now - when I want to print the labels onto Console as NSLog messages
//it is printing null
MyView *n=[[MyView alloc]init];
n=(MyView *)[tiles objectAtIndex:label++];
NSLog(#"%#---", n.myLabel.text);
//this also does not work. Definitely wrong dereferencing
// NSLog(#"%#---", (MyView*) [tiles ObjectAtIndex:1].mylabel.text);
//label++;
}
}
}
Please help
how to dereference NSMuableArray objects to the type what we want (the
type we know it exactly)
MyView:
- (id)initWithFrame:(CGRect)frame number:(NSString *)num;
{
self = [super initWithFrame:frame];
if (self) {
MyView *tile = [[[NSBundle mainBundle] loadNibNamed:#"XView" owner:self options:nil] objectAtIndex:0];
tile.myLabel.text=num;
[self addSubview:tile];
}
return self;
}
According to your comment, you want to add some view to your holderview. Later, you want to change text in particular view. So I suggest some way. I think, It will help you..
1) create and Add subview to your holder view
for (int xIndex=0; xIndex<9; xIndex++)
{
MyView *subView = [[MyView alloc]initWithFrame:CGRectMake(value,value,value,value)
number:[[NSString alloc] initWithFormat:#"%d",somecount+1]]]; // doesn't know why do you try this..
subView.tag = xIndex;
[self.myHolderView addSubview: subView];
}
2) Get particular subview with tag value.(No need to maintain any other array in your view controller)
After some time, you want to change text in particular view. Just try it..
MyView *subView = [self.myHolderView viewWithTag:index]; // as like, you access array
subView.myLabel.text = yourUpdateText;
#import "ViewController.h"
#interface MyView : UIView
#property UILabel *myLabel;
- (id) initWithFrame: (CGRect) rect number: (NSString *) string;
#end
#implementation MyView
- (id) initWithFrame: (CGRect) frame number: (NSString *) number {
self = [super initWithFrame:frame];
if (!self) return nil;
// Any things here
// Are you initialized label?
_myLabel = [UILabel new];
[_myLabel setText:number];
[self addSubview:_myLabel];
return self;
}
#end
#interface ViewController ()
#property UIView *myHolderView;
#end
NSMutableArray *tiles;
MyView *tile;
#implementation ViewController
#synthesize myHolderView;
- (void)viewDidLoad {
[super viewDidLoad];
tiles=[[NSMutableArray alloc]initWithCapacity:9];
int value = 1, label = 0;
myHolderView = [UIView new];
for (int xIndex=0; xIndex<3; xIndex++) {
for(int yIndex=0; yIndex<3; yIndex++) {
NSString *number = [[NSString alloc] initWithFormat:#"%d",label+1];
MyView *myView = [[MyView alloc]initWithFrame:CGRectMake(value,value,value,value)
number:number];
[tiles addObject:myView];
[self.myHolderView addSubview:tiles[label]];
//Now - when I want to print the labels onto Console as NSLog messages
//it is printing number
MyView *n=(MyView *)[tiles objectAtIndex:label++];
NSLog(#"%#---", n.myLabel.text);
}
}
// And without tiles array
for (UIView *subView in [myHolderView subviews])
for (UIView *subSubView in [subView subviews])
if ([subSubView isKindOfClass:[UILabel class]])
NSLog(#"%#", [(UILabel *) subSubView text]);
}
#end
Your problem is nothing to do with arrays or dereferencing. It's a logical issue because you are creating multiple views and nesting them.
Change the implementation of MyView:
- (id)initWithFrame:(CGRect)frame number:(NSString *)num;
{
self = [[[NSBundle mainBundle] loadNibNamed:#"XView" owner:self options:nil] objectAtIndex:0];
self.frame = frame;
self.myLabel.text = num;
return self;
}
Try something like this (typed inline so watch for issues):
- (void)viewDidLoad
{
[super viewDidLoad];
tiles = [[NSMutableArray alloc] initWithCapacity:9];
NSUInteger counter = 0;
for (int xIndex=0; xIndex<3; xIndex++)
{
for(int yIndex=0; yIndex<3; yIndex++)
{
MyView *newView = [[MyView alloc] initWithFrame:CGRectMake(value,value,value,value) number:[[NSString alloc] initWithFormat:#"%d", counter+1]]];
[self.myHolderView addSubview:newView];
[tiles addObject:newView];
MyView *logView = (MyView *)[tiles objectAtIndex:counter];
NSLog(#"view %d is %#---", counter, logView.myLabel.text);
counter++;
}
}
}

Memory management in iOS - I can't find out how to release memory

I'm using iOS 4 and I have some memory management problems that I don't understand. I will try to simplify the code:
- (void)viewDidLoad
{
NSMutableArray *buttonArray = [[NSMutableArray alloc] init];
for (int i = 0; i < [othercollection count]; i++)
{
// Push objects to button array
}
self.buttonSliderView = [[ButtonSliderView alloc] initWithButtons:
buttonArray];
[buttonArray release];
[self.view addSubview:self.buttonSliderView];
[buttonSliderView release];
}
- (void) viewDidAppear
{
if ([buttonSliderView.menuButtons count] > 0)
{
// ...
}
}
In ButtonSliderView.m:
- (id)initWithButtons:(NSMutableArray *)buttonArray
{
self = [super init];
if (self)
{
menuButtons = buttonArray;
}
}
I have an error in the first line of viewDidAppear. menuButtons were released. How can I fix this? Which is the correct solution?
If I change button array declaration to this:
NSMutableArray* buttonArray = [[[NSMutableArray alloc] init] autorelease];
...and remove the release sentence, it crashes too. If I remove the release sentence and don't autorelease, it works, but there are memory leaks.
The problem is that you omit the setter and assign menuButtons property directly. Try this:
-(id) initWithButtons:(NSMutableArray*)buttonArray {
self = [super init];
if (self) {
[self setMenuButtons:buttonArray];
}
}
You didn't show how the menuButtons property is declared, but I assume it is:
#property (nonatomic, retain) NSArray* menuButtons;
This will retain menuButtons automatically for you whenever you set it with the setter. If you have your property declared like this:
#property (nonatomic, assign) NSArray* menuButtons;
then you need to retain the array manually:
-(id) initWithButtons:(NSMutableArray*)buttonArray {
self = [super init];
if (self) {
menuButtons = [buttonArray retain];
}
}
ButtonSliderView may be still using that object (as opposed to grabbing the contents and releasing it). Don't assume it's a memory leak just for that reason alone.

Resources