Public Properties Loses Value When Accessed From Functions - ios

I am very new to iPhone programming and I have a very basic problem which just confuses me. I declare a public UIScrollView in my header file like this.
#property UIScrollView *scroller;
Then in my implementation file I synthesize it;
#synthesize scroller;
And in the
- (id)initWithFrame:(CGRect)frame
method I allocate my object.
if (self) {
scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(23, 99, 280, 300)];
}
( The class is extended from UIView )
But when I try to access this property in a method I always get nil value which just does not make sense.
- (void)DeselectAll{
scroller.hidden = YES;
}
( The scroller is always returning nil here. )
I also try accessing it with self.scroller but the value is still nil. I am sure that I am missing a very simple point but just couldn't figure it out.
( By the way this problem is happening for all my public properties )
Any help is very much appreciated.
Thank you.

First of all, declare the property like below
#property(strong,nonatomic) UIScrollView *scroller;
Now you initialize as
if (self) {
self.scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(23, 99, 280, 300)];
}

Create the UIScrollView in your header file like so :
#interface MyViewController : UIViewController {
UIScrollView * scrollView;
}
#property (nonatomic, retain) IBOutlet UIScrollView * scrollView;
#end
Then initialize it in viewDidLoad:
#synthesize scrollView;
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView = [[UIScrollView alloc]init(WithFrame:)];
[self.scrollView addSubview:...];
self.scrollView.contentSize = ...;
}

If you are using ARC, I think the properties are getting released.
Just use strong property at the time of declaration.
#property(strong, nonatomic)UIScrollView *scroller;

Ya you have correct. But you had missed memory management to property. Take a look at this.
For example, if you need to hold your created object. you should do this.
For ARC,
#property(strong,nonatomic) UIScrollView *scroller;
withour ARc
#property(retain,nonatomic) UIScrollView *scroller;
Update:
You must assign it using self. Otherwise it will not set via setter method. like as below
if (self) {
self.scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(23, 99, 280, 300)];
}

Ok, here is how i solved this problem. I created these controls in my nib file and assigned the variables as outlets to these objects. In that case the objects are retained and I can access them from my methods. (I tried defining the variables as IBOutlets without assigning to the controls in the nib file and in that case values were returned as nil again. Only after I link them to controls, the objects are retained. ) Although I solved this problem I have difficulty understanding this behaviour. I tried everything recommended in the answers but weirdly I kept getting nil for the object in my method.
Thanks to everyone that has taken time to write a reply to my question. Maybe someone can explain and help me understand this weird behaviour?
Cheers.

Have you checked that initWithFrame: is being called?
If you are creating your UIView subclass in a NIB file then that method won't be called, but initWithCoder: is called instead.

Related

UIView: Loading from custom XIB causing a crash

Hello StackOverflow.
I am trying to setup a UIView such that it loads itself from XIB
file in Xcode.
To do this, I went through the following initial steps:
Created empty UIView subclass.
Added a blank XIB file.
Then, I added all the subviews I wanted into the XIB file, and made corresponding IBOutlet Properties inside the UIView Subclass.
I watched this video to show me how to properly connect the outlets and set the files owner. The video instructs me to do the following things:
Do not set the UIView class to your subclass in XIB. Time link
Set the File's Owner to your UIView subclass in XIB:Time Link
Insert a IBOutlet into your UIView class of type UIView so your XIB file can load into that.Time link
Override initWithCoder like (image) if you intend to initialize the custom UIView within another XIB file.
Override initWithFrame like (image) if you intend to initialize the custom UIView programatically within another class file.
Cool, I have done all of these things, and am choosing to initialize my UIView programatically.
See my UIView subclass implementation file:
#import "CXHostsTableViewCellContentView.h"
#implementation CXHostsTableViewCellContentView
#pragma mark Custom Initializers
-(instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[[NSBundle mainBundle]loadNibNamed:#"CXHostsTableViewCellContentView" owner:self options:nil];
[self setBounds:self.view.bounds];
[self addSubview:self.view];
}
return self;
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[[NSBundle mainBundle]loadNibNamed:#"CXHostsTableViewCellContentView" owner:self options:nil];
[self addSubview:self.view];
}
return self;
}
And of course, my header file:
#import <UIKit/UIKit.h>
#import "CXStyleView.h"
#interface CXHostsTableViewCellContentView : UIView
#pragma mark UIView Properties
#property (strong, nonatomic) IBOutlet UIView *view;
#property (nonatomic,weak)IBOutlet UIView *standardView;
#end
I also have an image here of the XIB file's owner and another of the IBOutlet link from the base UIView to the outlet on file's owner.
Right, so everything's looking pretty good, should be no problem running this right?
Nope, whenever I initialize this subview and present it, I get a crash:
CXHostsTableViewCellContentView *scrollContentView = [[CXHostsTableViewCellContentView alloc]init];
I've really got no idea how to solve this, as I'm sure I'm following all of these steps right. I've googled and come across this question which has the same symptoms, but the answer has identical code to what I'm using, and this question with a contradictory reply.
I'm not sure what to do at this point, or what is causing the crash. I know that if I have NO outlets linked at all, it works. But then again, nothing displays either.
I think that You will face Problem When You Allocate Memory to Your scrollContentView object.
so,Try To allocate Memory With Frame.
i.e
Write this in .m file
- (void)myAllocation {
//do your stuff
}
- (id)initWithFrame:(CGRect)aRect {
self = [super initWithFrame:aRect];
if (self) {
[self myAllocation];
}
return self;
}
- (id)initWithCoder:(NSCoder*)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self myAllocation];
}
return self;
}
...
CXHostsTableViewCellContentView *scrollContentView = [[CXHostsTableViewCellContentView alloc] initWithFrame:CGRectMake(0, 10, 0, 20)];

iOS Subclassing a Custom Class

I'm having trouble wrapping my thoughts about class inheritance. I'm suppsed to create a dashboard like interface in a app, and I'll have maybe 10 widgets/dashlets on that dashboard view. All those dashlets/widgets will have basically same look, with a title on the top, borders, row of buttons on the top and a graph.
Let's say I create a subclass of UI View called 'Dashlet' with properties and outlets, and create XIB file with proper layout and connected outlets etc.
Now I want to create several subclasses of that 'Dashlet' view that will only process data differently, and draw different graphs. My current code looks something like this:
Dashlet.h
#interface Dashlet : UIView{
#private
UILabel *title;
UIView *controls;
UIView *graph;
}
#property (weak, nonatomic) IBOutlet UILabel *title;
#property (weak, nonatomic) IBOutlet UIView *controls;
#property (weak, nonatomic) IBOutlet UIView *graph;
-(Dashlet*)initWithParams:(NSMutableDictionary *)params;
-(void)someDummyMethod;
#end
And in Dashlet.m
- (id) init {
self = [super init];
//Basic empty init...
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(id)initWithParams:(NSMutableDictionary *)params
{
self = [super init];
if (self) {
self = [[[NSBundle mainBundle] loadNibNamed:#"Dashlet" owner:nil options:nil] lastObject];
//some init code
}
return self;
}
Now let's say that I create a subclass called CustomDashlet.h:
#interface CustomDashlet : Dashlet
#property (nonatomic, strong) NSString* test;
-(void)testMethod;
-(void)someDummyMethod;
#end
and CustomDashlet.m
-(id)init{
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(id)initWithParams:(NSMutableDictionary *)parameters
{
self = [super initWithParams:parameters];
if (self) {
//do some stuff
}
return self;
}
This, kind of works, but I need to override some of the methods declared in the superclass or even add some of my own. Whenever i try to do something like this in CustomDashlet.m
[self someDummyMethod] or even [self testMethod] I get an exception error like this:
NSInvalidArgumentException', reason: '-[Dashlet testMethod]: unrecognized selector sent to instance
Am I even doing this right? Did I miss something? Am I supposed to make this work in some other way? If anyone has suggestions, please feel free to share your thoughts, thank you for all the help.
The problem is that
SalesDashlet *sales = [[SalesDashlet alloc] initWithParams:nil];
does not return a SalesDashlet instance, as expected, but a Dashlet instance.
Here is what happens:
[SalesDashlet alloc] allocates an instance of SalesDashlet.
The subclass implementation of initWithParams: is called with this instance,
and calls self = [super initWithParams:parameters].
The superclass implementation of initWithParams discards self and
overwrites it with a new instance loaded from the Nib file. This is an instance
of Dashlet.
This new instance is returned.
Therefore SalesDashlet *sales is "only" a Dashlet, and calling any subclass
method on it throws an "unknown selector" exception.
You cannot change the type of objects loaded in the Nib file. You could create a second
Nib file containing a SalesDashlet object. If the main purpose of the subclass is
to add additional methods, then the easiest solution would be to add these methods
in a Category of the Dashlet class.
If the problem is with the
- (Dashlet *)initWithParams:
method it is because the base class declares it with a Dashlet return value, whereas the subclass is redeclaring it with a SalesDashlet return instance.
Always use instancetype as the return type for any init method.
I believe you simply need to change following line in your Dashlet.h file:
-(Dashlet*)initWithParams:(NSMutableDictionary *)params;
to following:
-(id)initWithParams:(NSMutableDictionary *)params;
or better:
-(instancetype)initWithParams:(NSMutableDictionary *)params;
You need to change your init methods.
-(Dashlet*)initWithParams:(NSMutableDictionary *)params
-(SalesDashlet*)initWithParams:(NSMutableDictionary *)parameters
The return type on both of these should be id.
The problem you're running into is similar to trying to do this:
NSMutableArray *someArray = [[NSArray alloc] init];
Despite declaring someArray as an NSMutableArray, you've initialized it as an NSArray, and as such, someArray will actually be an immutable NSArray.
So because your SalesDashlet init method calls its super init method and the super explicitly returns an object of type Dashlet, then the SalesDashlet will also return an object of type Dashlet, so you're trying to call testMethod (a method that only exists in SalesDashlet) on an object of type Dashlet (which doesn't know about the testMethod method).
Changing your return type to id will make the methods return an object of the right type.
As a note, you've done your init, and initWithFrame methods correctly.
SalesDashlet *mySalesDashlet = [[SalesDashlet alloc] initWithFrame:someFrame];
Creating a SalesDashlet in this way will allow you to call [mySalesDashlet testMethod].
Your initWithFrame has return type of id in both super and sub classes.

UILabel is not updating it's text, IOS

I am stuck at a simple task, I have uilabel on my uiviewcontroller's interface file. I want to update that label via some methods. It doesn't update the label.
.h
UIViewController
{
UILabel *pictureNameLabel;
}
#property (nonatomic, strong) IBOutlet UILabel *pictureNameLabel;
.m
#synthesize pictureNameLabel=_pictureNameLabel;
- (void)viewDidLoad
{
_pictureNameLabel = [[UILabel alloc] init];
_pictureNameLabel.textColor=[UIColor blackColor];
_pictureNameLabel.text=#"Try";
}
How can I fix that issue?
You don't need to alloc the label. It's already alive and get's awaken from the nib.
.h
UIViewController
{
//UILabel *pictureNameLabel;
}
#property (nonatomic, strong) IBOutlet UILabel *pictureNameLabel;
.m
#synthesize pictureNameLabel=_pictureNameLabel;
- (void)viewDidLoad
{
//_pictureNameLabel = [[UILabel alloc] init];
_pictureNameLabel.textColor=[UIColor blackColor];
_pictureNameLabel.text=#"Try";
}
Your direct problem is the line:
_pictureNameLabel = [[UILabel alloc] init];
In -ViewDidLoad. It's creating a new variable, and having the pictureNameLabel property point to it, causing you to lose your reference to the one created in Interface Builder. Remove that line, and everything should work fine.
If you've created an element via Interface Builder, you do not need to alloc & init it yourself, along with adding it to the view in the appropriate spot, as it's automatically done for you, and the property is already set to point to it. If you're manually creating a new view, you do need to do that... but you'd also need to add it somewhere in the view hierarchy as a subview.
Not related to your problem, but you have also created a variable named UILabel *pictureNameLabel;. I'd assume you created this variable to be the backing variable for the synthesized property pictureNameLabel... but, you synthesized that to _pictureNameLabel, which means you now have two variables, _pictureNameLabel and pictureNameLabel. This is almost certainly a mistake. You should either remove the manual definition of pictureNameLabel, or rename it to something distinct if you actually intend to use it separately from the property with the same name. Having it is likely to just lead to confusion & bugs down the road.
your label has already exists on your xib file, and you can set the textcolor, text on interface bulider.

iOS5 adding and manipulting a UIView class

I have create a UIVeiw class and a .xib. Within this .xib view I have its set to freeform with the dimensions of 400x200 and I have assigned it to my custom class with the same name:
Storyboard: blogView
Class Files: blogView.h & blogView.m
Within in the .xib i have added a label and a text field and linked them up to variable within the .h files etc (See code below).
blogCont.h
#import <UIKit/UIKit.h>
#interface blogCont : UIView
#property (strong, nonatomic) IBOutlet UILabel *lbBlogDate;
#property (strong, nonatomic) IBOutlet UITextView *txtBlogTitle;
#end
blogCont.m
#import "newsStoryView.h"
#implementation blogCont
#synthesize lbBlogDate;
#synthesize txtBlogTitle;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code place a log to see if it loads
NSLog(#"View Loaded");
}
return self;
}
#end
Now with in my main viewController.m file i have added the following code to init this view class, and I have added a background colour to see if this loads in.
viewController.m
UIView *blogCont = [[blogView alloc] init];
blogCont.backgroundColor = [UIColor purpleColor];
[subview addSubview:blogCont];
Now when I run this it all works well but as I do not see the purple background it looks as if the view does not load, but within the log I do see the NSLog message I have within this view NSLog(#"View Loaded"); so it seems it initiating this, but I cannot for the life of me get this to display?
Now if I change the code slightly to my main View Controller.m fiel to:
CGRect blogFrame;
blogFrame.origin.x = 20;
blogFrame.origin.y = 20;
blogFrame.size = CGRectMake(400,200);;
newsStoryView *blogCont = [[blogView alloc] blogFrame];
blogCont.backgroundColor = [UIColor purpleColor];
[subview addSubview:blogCont];
Then I get my view display a nice purple box, so this shows up when I set a frame size and the init the view with it 'blogFrame', bu tI thought that all this would be set within the .xib settings so no need to do this?
SO how can I create this external view class and assign it into another view and then manipulate its data, as accessing the label in the .xib using blogCont.lbBlogDate.text does not seem to work that is it probably does but as I cannot view it i cannot confirm it.
What am i doing wrong?
Thanks
Seems I nearly answered my own question then did:
I was not setting the size within my separate class view I was asking for a size when init it:
- (id)initWithFrame:(CGRect)frame
this is asking for a size
so I could do the following to the above:
- (id)init
{
self = [super initWithFrame:CGRectMake(0, 0, 478, 220)];
.... rest of code
Setting the size within the view load.
But I could also set it when I init it in my main view controller as below:
newsStoryView *blogCont = [[blogView alloc] initWithFrame:CGRectMake(0, 0, 400, 200)];
This is better as I can control the position of each one. Hope this helps anyone

Passing Array and String to Custom UIView Class (Subclass) (iOS)

Hope you can help.
I have created my own Custom UIView and I'm trying to pass in a NSMutableArray which I have setup from data from a sqlite DB. I have NSLog'd the Array from the ViewController and everything is showing correctly.
However I want to pass this NSMutableArray into my Custom UIView (it's actually a UIScrollView) so that I can do some magic. However when I do this, my NSLog show's the output as (null).
Here is my code (I've also passed a test string to help to see if it's Array specific, but it isn't):
viewcontroller.m (just shown the Custom class call - NSLog outputs the Array contents (see end of examples)
- (void)viewDidLoad
{
...
NSString *teststring = #"Testing";
NSLog(#"Subitems: %#", subitems);
SubItemView* subitemview = [[SubItemView alloc] initWithFrame:CGRectMake(150,150,0,0)];
subitemview.cvSubitems = subitems;
subitemview.teststring = teststring;
[self.view addSubview:subitemview];
}
customview.h
#import <UIKit/UIKit.h>
#class SubItemView;
#interface SubItemView : UIScrollView {
}
#property (nonatomic, retain) NSMutableArray *cvSubitems;
#property (nonatomic, retain) NSString *teststring;
#end
customview.m
#import "SubItemView.h"
#implementation SubItemView
#synthesize cvSubitems;
#synthesize teststring;
- (id)initWithFrame:(CGRect)frame
{
CGRect rect = CGRectMake(0, 0, 400, 400);
NSLog(#"Subclass Properties: %#", self.cvSubitems);
self = [super initWithFrame:rect];
if (self) {
// Initialization code
}
return self;
}
The first NSLog in the viewcontroller.m outputs:
Subitems: (
"<SubItems: 0x6894400>",
"<SubItems: 0x6894560>"
)
The second NSLog from the Custom UIScrollView outputs:
Subclass Properties: (null)
I am a bit of a newbie, so I'm obviously missing something (possibly obvious) here. I am just really struggling to pass an Array and even a simple string into a Custom class and just output it's contents via NSLog.
Any help is gratefully appreciated.
Well when your initWithFrame method is called, your cvSubitems property isn't set yet, as it is set only after your call to initWithFrame.
Try again to log your arrays value in a method that is called after your view is initialized, or provide a custom init method (e.g. initWithMyData: andFrame:) to solve this issue.
So to clarify what has already been said, you are calling out of order.
1| SubItemView* subitemview = [[SubItemView alloc] initWithFrame:CGRectMake(150,150,0,0)];
2| subitemview.cvSubitems = subitems;
3| subitemview.teststring = teststring;
On line 1 you are calling the initWithFrame: method on SubItemView
On lines 2 and 3 you are setting the ivars
The point being that you are setting the ivars (lines 2+3) after the initWithFrame: method has returned.
But you are trying to print the ivars in the initWithFrame: method
You are also trying to log the ivars before you have even assigned self which is not a good idea either
NSLog(#"Subclass Properties: %#", self.cvSubitems);
self = [super initWithFrame:rect];
To prove that they are being set you can just print from where you instantiate:
SubItemView *subitemview = [[SubItemView alloc] initWithFrame:CGRectMake(150,150,0,0)];
subitemview.cvSubitems = subitems;
subitemview.teststring = teststring;
NSLog(#"Subclass Properties: %#", subitemview.cvSubitems);

Resources