How do I save a UILabel's properties in a NSMutableArray? - objective-c

For example,
-(void)drawHypnoticMessage:(NSString *)message{
....
UILabel messageLabel = [[UILabel alloc]init];
messageLabel.backgroundColor = [UIColor clearColor];
messageLabel.textColor = [UIColor whiteColor];
messageLabel.text = message;
[messageLabel sizeToFit];
int width = (int)(self.view.bounds.size.width - messageLabel.bounds.size.width);
int x = arc4random() % width;
int height = (int)(self.view.bounds.size.height - messageLabel.bounds.size.height);
int y = arc4random() % height;
CGRect frame = messageLabel.frame;
frame.origin = CGPointMake(x,y);
messageLabel.frame = frame;
[self.view addSubview:messageLabel];
...}
I want to be able to add this label "messageLabel" to a NSMutable array like this:
NSMutableArray *thisArray;
[thisArray addObject:messageLabel];
and then access it later after STATE RESTORATION like this:
UILabel *thisLabel = [thisArray objectAtIndex:0];
[self.view addSubview: thisLabel];
For some reason, when I try to access the UILabel stored inside the NSMutableArray, the text in ---> thisLabel.text <--- shows up empty. In Addition, all the properties in the --->thisLabel<--- also shows up empty when I try to access it again after STATE RESTORATION.
such as:
--->thisLabel.textColor<---- shows up empty.
--->thisLabel.frame <---- shows up empty.
Can anybody help me solve this quirk? It's really frustrating.

I want to be able to add this label "messageLabel" to a NSMutable
array like this:
NSMutableArray *thisArray;
[thisArray addObject:messageLabel];
Did you remember to first say
thisArray = [[NSMutableArray alloc]init];

Related

Objective C - How to create a set of sub views to update

Perhaps I am thinking about this incorrectly, so please point me in the right direction..
I want to make many UIViews as a property so I can update them with different methods. Something like this:
#property UIView *view1
#property UIView *view2
...
#property UIView *view200
-(void)updateViews{
for (i=0; i <200; i++){
//Update view here
}
So my questions are:
How can I easily make multiple views?
How can I easily pass a message to each individual view?
So many great responses, I think I need to add a little more info.
Have tried putting my UIViews into an array to index them but I struggle to access them to update their positions within the UIViewController. I thought making them a property is the correct way to since I need to update them with the device heading. Looks like using a tag might be best. Can someone further comment on this approach:
for (i = 0 ; i < 144; i++) {
UIView *sunView =[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"sun.png"]];
sunView.tag = i;
}
//then later, in a separate method do the following:
for (i = 0 ; i < 144; i++) {
CGFloat CGx = self.acceleration.X;
CGFloat CGy = self.acceleration.Y
[sunView.tag.i setFrame:CGRectMake(CGx, CGy, 20, 20)]
}
Im not sure how to access the UView with the corresponding indexed tag. Can someone help me there too?
When they are really subviews then you could iterate through the superview's subviews array.
You could do that recursievely in order to apply it on all subivews of your subviews, if you have multiple hierarchy levels.
If you don't want to apply this to each of your superview's subviews, then tag your subviews with something like view.tag = 6502 and use that to identify those views to which you want to apply it.
Or maintain your own array or set (iterable collection) of views and iterate through them.
Create your views in a loop and store them in an NSMutableArray. That way you will have access to all of them in your view controller.
Create a property.
#property (monatomic) NSMutableArray *arrayOfViews;
self.arrayOfViews = [[NSMutableArray alloc] init];
UIView *view;
for (int i = 0; i < 10; i++)
{
view = [[UIView alloc] init];
[self.arrayOfViews addObject:view];
}
Run a loop over this array and access the views one by one and call the method you need to call.
On a side note: Why are you creating 200 UIViews? That's a lot :)
I think you need something like that:
-(void)updateViews
{
UIView *views;
UILabel *label;
for(int i = 0 ; i < numberOfViews ; i++)
{
views = [[UIView alloc] initWithFrame:CGRectMake(...)];
label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,views.frame.size.width,views.frame.size.height)];
[label setText:[NSString stringWithFormat:#"%d",i]];
[views addSubview:label];
[self.view addSubview:views];
}
}
How can I easily make multiple views?
Do you want to create it in IB?
1) You can use tags in Xcode
UILabel * label = (UILabel*)[self.view viewWithTag:19];
UIView * view = (UIView*)[self.view viewWithTag:20];
2) You can use property that you also do
How can I easily pass a message to each individual view?
if your views inside another view(currentView) you can use that
for (UIView *yourView in self.currentView.subviews) {
//Update view here
}
Or if you want to create it in the code
for (int i = 0; i < 10; i++)
{
UIView * myView = [[UIView alloc]initWithFrame:CGRectMake(10*i, 10*i, 25, 25)];
int r = arc4random() % 255;
int g = arc4random() % 255;
int b = arc4random() % 255;
myView.backgroundColor = [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1];
[self.view addSubview: myView];
}
That's what you can see
And to call all these views you can use
for (UIView *yourView in self.view.subviews) {
[yourView removeFromSuperview];
}
And that's what you'll see
If you want to call own view you can use tags like this
for (int i = 0; i < 10; i++)
{
UIView * myView = [[UIView alloc]initWithFrame:CGRectMake(10*i, 10*i, 25, 25)];
myView.tag = i + 21;
int r = arc4random() % 255;
int g = arc4random() % 255;
int b = arc4random() % 255;
myView.backgroundColor = [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1];
[self.view addSubview: myView];
}
for (UIView *yourView in self.view.subviews) {
if (yourView.tag == 21) {
[yourView removeFromSuperview];
}
}

Duplicating UIView

I can't figure out how could I duplicate UIView with its contents. Or is it even possible?
I have made and UIView with a label and a button inside a .xib file. Now I wish to copy this view n times only with different label text.
I'm trying to do this like this, but such way I only get the last object shown. _view1 is IBOutlet just like _view1Label.
NSMutableArray *Info = [[NSMutableArray alloc]initWithCapacity:number.intValue];
for(int i = 0; i != number.intValue; i++)
{
NSString *number = [NSString stringWithFormat:#"Zona%i ",[[NSUserDefaults standardUserDefaults] stringForKey:#"ObjectNumber"].intValue];
NSString *zona = [NSString stringWithFormat:#"%#%i",number, i+1];
NSString *parinkta = [[NSUserDefaults standardUserDefaults] stringForKey:zona];
_view1Label.text = parinkta;
_view1.hidden = false;
CGRect newrect = CGRectMake(_view1.frame.origin.x, _view1.frame.origin.y + (80 * i), _view1.frame.size.width, _view1.frame.size.height);
_view1.frame = newrect;
[Info addObject:_view1];
}
for(int i = 0; i != number.intValue; i++)
{
UIView *add = [Info objectAtIndex:i];
[self.view addSubview:add];
}
I guess you'll get the idea what I am trying to do, maybe my idea of doing this is totaly wrong, so could anyone help me get on the path on this ?
Load the view multiple times from the nib, adjusting the label contents on each copy you load. This is much easier, shorter, less prone to error than trying to copy the UIView contents in-memory which you're trying to do.
Here's an example of using UINib to access the nib contents:
UINib *nib = [UINib nibWithNibName:#"nibName" bundle:nil];
NSArray *nibContents = [nib instantiateWithOwner:nil options:nil];
// Now nibContents contains the top level items from the nib.
// So a solitary top level UIView will be accessible
// as [nibContents objectAtIndex:0]
UIView *view = (UIView *)[nibContents objectAtIndex:0];
// assumes you've set the tag of your label to '1' in interface builder
UILabel *label = (UILabel *)[view viewWithTag:1];
label.text = #"My new text";
So just repeat the above code for each nib instance you want.
if you wish to show n views then you have to create the view n times, inside the first for loop, you can not place a single view in multiple places
If you are created the first view through code then you can use the following method.
Change your for loop like:
for(int i = 0; i != number.intValue; i++)
{
NSString *number = [NSString stringWithFormat:#"Zona%i ",[[NSUserDefaults standardUserDefaults] stringForKey:#"ObjectNumber"].intValue];
NSString *zona = [NSString stringWithFormat:#"%#%i",number, i+1];
NSString *parinkta = [[NSUserDefaults standardUserDefaults] stringForKey:zona];
UIView *tempView = [[UIView alloc] init];
UILabel *tempLabel = [[UILabel alloc] init];
tempLabel.frame = _view1Label.frame;
tempLabel.text = _view1Label.text;
[tempView addSubview: tempLabel];
tempView.frame = _view1.frame;
_view1Label.text = parinkta;
_view1.hidden = false;
CGRect newrect = CGRectMake(_view1.frame.origin.x, _view1.frame.origin.y + (80 * i), _view1.frame.size.width, _view1.frame.size.height);
_view1.frame = newrect;
[Info addObject:tempView];
}

How to place "x" number of objects on the view

If the user provides a number, such as 8, how exactly would I place eight labels onto the view?
For example:
int userGivenNumber = textfield.text;
for (int labelNumber=1; i<=userGivenNumber; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, *previousLabel*.frame.origin.y + 20)];
}
How would you get the location of previousLabel? Additionally, if the user wanted to edit a label, such as label 3, how would I edit this label?
The problem is that I don't know how many labels they want to place while coding, and I have no way of keeping track of the labels, since they are called label. Do you have any ideas? Do you understand what I am asking? Thanks for your help.
- (void)addLabels {
CGFloat offset = 0;
// labels is NSMutableArray property
labels = [[NSMutableArray alloc] initWithCapacity:8];
for (int i = 0; i < 8; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, offset, 320, 20)];
[self.view addSubview:label];
[labels addObject:label];
offset += label.frame.size.height;
}
}
Edit: Like jerrylroberts said, to access the label array from elsewhere in your code, you should declare it as a property.
So you could add the views like so:
int userGivenNumber = [textfield.text intValue];
for (int labelNumber=1; i<=userGivenNumber; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, *previousLabel*.frame.origin.y + 20)];
[self.view addSubview:label];
[label release];
}
If you wanted to keep track of them you could just create a mutable array as a property and then add each label to the array before you add to the subview;
Interface
#property (nonatomic, retain) NSMutableArray *addedLabels;
Implementation
#synthesize addedLabels=_addedLabels;
- (void)viewDidLoad
[super viewDidLoad];
// Create your array to hold labels
NSMutableArray *addedLabels = [[NSMutableArray alloc] initWithCapacity:0];
self.addedLabels = addedLabels;
[addedLabels release];
// NOW put your code
int userGivenNumber = [textfield.text intValue];
for (int labelNumber=1; i<=userGivenNumber; i++) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, *previousLabel*.frame.origin.y + 20)];
[self.view addSubview:label];
[addedLabels addObject:label];
[label release];
}
}
Now you can easily access any label you have added if you have the index. Hope this helps
Just keep a reference to the location (CGPoint) outside the loop, but update it inside the loop to the current label's location. When you assign the next frame do your operation depending on width and size of the label, and don't forget to update the reference of CGPoint.

Create textfields based on user input

I want to create textfields based on user input. Let's say 5 textfield are needed
for (int x = 0; x < self.howMany; x++)
{
NSLog(#"%i", x_position);
self.textFieldRounded = [[UITextField alloc] initWithFrame:CGRectMake(10, x_position, 300, 25)];
self.textFieldRounded.borderStyle = UITextBorderStyleRoundedRect;
self.textFieldRounded.textColor = [UIColor blackColor]; //text color
self.textFieldRounded.font = [UIFont systemFontOfSize:17.0]; //font size
[self.view addSubview:self.textFieldRounded];
x_position += 30;
}
So far, so good. That code create five textfields. The problem is that these textfields are not "unique". Each of these textfield can contain different information. What to do so that I can gather information of each of these fields ?
Add a tag to them.
int i = 1;
for (int x = 0; x < self.howMany; x++)
{
NSLog(#"%i", x_position);
self.textFieldRounded = [[UITextField alloc] initWithFrame:CGRectMake(10, x_position, 300, 25)];
textFieldRounded.tag = i++;
...
And then, when you need to find textfield 3 for example, do:
UITextField *textField = (UITextField *)[self.view viewWithTag:3];
You could set a tag for each text field.
self.textFieldRounded.tag = x;
Then you can use the tag to identify your text fields. You can iterate through self.view to find all subviews (text fields) using this technique.
I think you should take different TextField for each information so that everything should be clear and if you still want to use one text field then you can set the tag for each info.
You could add a reference to each TextField in an NSMutableArray and then iterate through the array using something like
for (UITextField *tmp in myArray) {
//do stuff with the textfield
}

problem with UiImageView.image = nil

.h file
#interface AppViewController : UIViewController {
...
UIImageView *imageViewArray[3][5];// not using any #property for this
...
}
...
#define FOR(j,q) for (int j = 0; j < q; j++)
.m file
ViewDidLoad{
FOR(i,5){
FOR(j,3)
{
image_names[0] = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png",j]];
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_names[0]];
CGRect newFrame ;
newFrame = CGRectMake(60+(j*130), 110+(150*i),130,136);
imageViewArray[j][i].frame = newFrame;
[self.view addSubview:imageViewArray[j][i]];
}
}
}
//clear the previous images and call a method to add new
-(IBAction)Change{
FOR(i,5)
FOR(p,3)
imageViewArray[p][i].image = nil;
[self ImageSwitch];
}
-(void)ImageSwitch{
FOR(i,5){
FOR(j,3)
{
image_namesTmp[0] = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png",j+3]];
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_namesTmp[0]];
CGRect newFrame ;
newFrame = CGRectMake(60+(j*130), 110+(150*i),130,136);
imageViewArray[j][i].frame = newFrame;
[self.view addSubview:imageViewArray[j][i]];
}
}
}
when i press the button for the first time it works fine(old images get replaced with new ones),but if i do it second time
imageViewArray[p][i].image = nil;
this line wont work and all the previous images still remain there and new images are overlapping with the present ones
In the ImageSwitch method you are always creating new instances of UIImageView.
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_namesTmp[0]];
At this point, there already is a reference to UIImageView stored in the variable. You are losing your reference to it and since you alloc/inited it, you will most probably also leak its memory. Moreover, you are adding new UIImageViews to the view hierarchy, but you are not removing the old ones.
Try something like this:
[imageViewArray[j][i] removeFromSuperview];
[imageViewArray[j][i] release];
imageViewArray[j][i] = [[UIImageView alloc] initWithImage: image_namesTmp[0]];
Second time you set your images you don't need to recreate the UIImageView objects. Your ImageSwitch method is better declared as,
-(void)ImageSwitch{
FOR(i,5){
FOR(j,3)
{
imageViewArray[j][i].image = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png",j+3]];
}
}
}
Just changing the images will suffice and that's what you need to do.
You need an NSMutableArray of ImageViews. In your header declare the NSMutableArray:
NSMutableArray *_imageViewArray;
Then in your viewDidLoad method initialize the array and then populate it with your image views.
_imageViewArray = [[NSMutableArray alloc]init];
for (int i =0; i < imageNames; i++) {
UIImageView *tempImageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:[imageNames objectAtIndex:i];
[_imageViewArray addObject:tempImageView];
[tempImageView release];
}
So thats the image view array populated, you may do it a different way but thats up to you.
To switch the views over what I would do is like so...
[self.view removeAllSubviews]; // gets rid of all previous subviews.
[self.view addSubview:[_imageViewArray objectAtIndex:3]];// could be any number. generate a random number if you want.
Thats all you would need to do. If you wanted to know which image view was being displauyed the maybe declare an instance of UIImageView in your header called _currentImageView and then you can remove just that image view instead of all subviews.
Hope this helped.