Passing Array and String to Custom UIView Class (Subclass) (iOS) - 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);

Related

Initializing from an array of property pointers

I'm trying to write a shortcut for initializing my custom UIView's properties, rather than write out each one line by line, but unfortunately it's not working the way I expected.
// .h file
#property (nonatomic, strong) UIView *view1;
#property (nonatomic, strong) UIView *view2;
#property (nonatomic, strong) UIView *view3;
#property (nonatomic, strong) UIView *view4;
// .m file
UIView *views[] = { self.view1, self.view2, self.view3, self.view4 };
for ( int i = 0; i < sizeof(views) / sizeof(views[0]); i++ ) {
views[i] = [[UIView alloc] initWithFrame:self.frame];
}
NSLog( #"%#", self.view1 ); // prints null
Is this just not allowed with C-style arrays?
Your initialization of views[] looks fine, but as soon as you do
views[i] = ...
you are merely overwriting the contents of the array. You are, however, not initializing self.view1, self.view2, etc. Try
NSLog(#"%#", views[0]);
to see what I mean.
I would not do it like follows, but you could probably do (untested):
UIView **views[] = { &self.view1, &self.view2, &self.view3, &self.view4 };
for (int i = 0; i < (sizeof(views) / sizeof(views[0]); ++i)
{
*views[i] = [[UIView alloc] initWithFrame: self.frame];
}
NSLog(#"%#", self.view1);
I would rather initialize them one by one, without the array. I don't see any advantage in using an array and a loop.
You are just changing the pointer inside the array, the self.view1 pointer will still point to nil.
You could have an array of views as property:
#property(nonatomic,strong) UIView **views;
Or maybe you can use Key Value Coding like
for(int i=1;i<5;i++){
NSString* key = [NSString stringWithFormat:#"view%i",i];
UIView *view = //initialize view
[self setValue:view forKey:key];
}
Calling e.g. self.view1 at the beginning will give you nil before you have initialized it. So the line
UIView *views[] = { self.view1, self.view2, self.view3, self.view4 };
is effectively creating an array of 4 nils.
Inserting the new instances of UIView to the array afterwards has no effect on the properties. In fact, a property is only a pair of selectors (e.g. view1 and setView1:) and by itself cannot be used reliably with C-style pointers.
You could use the backing ivars with an array of UIView ** pointers, but you're most likely better off rethinking the whole approach.

Public Properties Loses Value When Accessed From Functions

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.

Variable not passed to UIView

I am trying to pass a variable from within my main UIViewController to a UIView.
CGRect frame = CGRectMake(0,0,347, 308);
XBMCPopUpView * xbmcpop = [[XBMCPopUpView alloc]initWithFrame:frame];
xbmcpop.DevIP = #"2222";
[self.view addSubview:xbmcpop];
the Object xbmcpop gets created and its string property "DevIP" gets assigned correctly. But when the UIVIEW is actually viewed it seems like the instance that is viewed is different from the one I created and the variable #"2222" is not passed to the string property "DevIP"
Note: My Main UIViewController is in a storyboard with a navigation controller
If you are interested to see the "XBMCPopUpView class, here it is:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#class XBMCPopUpView;
#interface XBMCPopUpView : UIView
{
NSString * DevIP;
}
#property (strong,nonatomic) NSString *DevIP;
- (id)initWithFrame:(CGRect)frame;
-(IBAction)sendPlay:(id)sender;
#end
Under
#interface XBMCPopUpView : UIView
remove
{
NSString * DevIP;
}
Since you're using a storyboard, you need to use your view controller's awakeFromNib method for this.
When you add the awakeFromNib method, don't forget to call [super awakeFromNib] there.
- (void)awakeFromNib
{
[super awakeFromNib];
CGRect frame = CGRectMake(0,0,347, 308);
XBMCPopUpView * xbmcpop = [[XBMCPopUpView alloc]initWithFrame:frame];
xbmcpop.DevIP = #"2222";
[self.view addSubview:xbmcpop];
}

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.

NSMutableArray from another view returns empty

I have two UIViews. One UIView, called SelectText has an ivar of NSMutableArray which is populated after performing a certain function.
Here is a snippet code:
- (void)fillDrawPoints
{
//the codes....
[self.drawnPoints addObject:[NSValue valueWithCGPoint:currPoint]];
}
NOTE: The drawnPoints array is initialized in the initWithFrame of SelectText. Also, I always check if the array is actually populated inside the view by putting a log in the function.
Now what I want to do is to access this array from another view. This is what I do:
TextView.h
#import "SelectText.h"
#interface TextView : UIView
{
SelectText *txtSel;
}
#property (nonatomic, retain) SelectText *txtSel;
TextView.m
#synthesize txtSel;
- (void)getDrawingPoints:(NSMutableArray *)pointArray
{
self.pointArray = pointArray;
NSLog(#"Array count: %d", [self.pointArray count]);
}
As you can see from the above code, I am trying to pass the data inside txtSel.drawnPoints to the textView.pointArray for later use. The problem is, the txtSel.drawnPoints always returns empty when I try to access it from another view. What am I doing wrong here?
ADDITIONAL:
This is how I instatiate SelectText
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
txtSel = [[SelectText alloc]init];
[self addSubView:txtSel];
//rest of code...
}
change TextView class init method as below
- (id)initWithClassSelectText:(SelectText *)selectText {
if ((self = [super init])) {
txtSel = selectText;
}
return self;
}
And when you make an instance of TextView you use this:
TextView textView = [[TextView alloc] initWithClassSelectText:self];
now you can access all properties of SelectText class using txtSel object

Resources