Keeping track of objects on the screen - ios

Even though I’m writing in Objective C, most of my code is still written in a procedural style. However, now I want to do something where that approach will not work. So I need some advice on how to deal with an indeterminate number of objects on the screen at the same time. I’m sure that this problem has been solved, I just haven’t been able to find out how.
I have a bunch of games where I put two or four pictures on the screen and then the user interacts with the picture. When they are done with a page they swipe to the next one and I use a transition to slide the pictures off the screen. I can control the movement of the pictures because when they were created I name them self.picture_1 and self.picture_2. The movement method knows about them even though that method didn’t create them.
Now suppose I want to have an indeterminate number of pictures on the screen. I can’t call them self.picture_1. through self.picture_n because ObjectiveC won’t let you dynamically create variable names. But I still need to move them in a method where they weren’t created.
I can make it work with two techniques, neither of which seem ideal. First, I look at all the objects on the screen and then do something with the ones that I want to target. Note: pictures are in buttons.
for ( id subview in self.parentView.subviews ) {
if ( [subview isKindOfClass:[UIButton class]] ) {
UIButton *pictureButton = subview;
for (NSUInteger i=0; i<self.totalItems; i++) {
NSUInteger row = (i % 2) + 1;
NSUInteger column = (i/2) + 1;
NSString *pictureTitle = [NSString stringWithFormat:#"pictureR%iC%i", row, column];
if ( [pictureButton.titleLabel.text isEqualToString:pictureTitle] ) [pictureButton removeFromSuperview];
}
}
}
This works for removing them from the view, but gets cumbersome when I try to make the pictures slide off the screen.
The second way is to make an array that holds the picture objects when they are created. I’ve been playing with something like this.
self.gridImages = [NSMutableArray arrayWithCapacity:4];
for (NSUInteger i = 0; i < itemsOnScreen; i++) {
Word *word = [wordListArray objectAtIndex:i];
self.gridImages[i] = word.image;
}
And then to do things with the pictures I loop through the array.
for (NSUInteger i = 0; i < itemsOnScreen; i++) {
Picture *picture = self.gridImages[i];
// do something with picture
}
Neither of these methods seems ‘right’ so I’m wondering if there is a preferred method for manipulating an indeterminate number of objects on the screen?

#Hot Licks, that works, so I put you answer into an answer.
I have it working for checkBoxes. They are created by the main view controller and it passes in a tag. I'm using tags starting at 1000 for checkboxes, 2000 for pictures, etc. Just before I put the checkBox on the screen, I assign it the tag.
[self.checkBox setTag:checkBoxTag];
You could also use: self.checkbox.tag = checkBoxTag;
When it is time to remove the checkBoxes, I loop through all of the tags starting at 1000 up to the total number of items on the screen. I have warnings turned up to 11 so I need to cast the counter to an NSInteger.
- (void)removeCheckboxes {
for (NSUInteger i = 0; i < self.totalItems; i++) {
NSInteger tagNumber = 1000 + (NSInteger)i;
[ [self.parentView viewWithTag:tagNumber] removeFromSuperview];
}
}

Related

How can I achieve a 30 button grid using autolayout storyboards?

So I had this working for a 12 button (4x3) grid of buttons.
I'd like all of the buttons to be equal size, common distances above and to the side of each other, and the entire grid to be centered on the device, like so:
The problem is, it looks like a jumbled mess when I build the project.
I have no problem getting the segmented control, score, or reset buttons positioned correctly, but the grid just messes everything up.
I've been using the middle tool to set up the constraints on the grid, which worked fine for the 12 button grid:
However, using this only creates infinite conflicting constraints that cannot be resolved by xcode.
I am very new to iOS and could be missing something simple, but I've been trying my best to match up to the blue auto suggested lines as much as possible here.
Thanks for any advice.
It would be a lot simpler just to use a UICollectionView with a UICollectionViewFlowLayout and let the flow layout create the grid for you.
But even if you're not going to do that, then still, my advice is: don't set this up in Xcode / Interface Builder; make the entire grid (and constraints if you want them) in code. It's much simpler (and more fun and less boring and, of course, less error-prone).
1.) Instead of setting each button up in the interface builder just create the container (a UIView) that the whole grid should fit inside. Add constraints to that container for how you would want that to expand and contract with screen size.
2.) Link that container UIView to your .h view controller class and name it gridContainer or whatever.
3.) Create a property in your .h class:
#property (strong, nonatomic) NSMutableArray *twoDimensionalArrayContainingRowsOfCardButtons;
4.) Then:
- (void)viewDidLoad {
// other stuff you're doing to set up your app
self.twoDimensionalArrayContainingRowsOfCardButtons = [NSMutableArray new];
//Do this inside the main thread to make sure all your other views are laid out before this starts
//Sometimes when you do layout stuff before the rest of the view is set up from Interface Builder you will get weird results.
dispatch_async(dispatch_get_main_queue(), ^{
[self createTwoMentionalArrayHoldingCardButtons];
[self displayCardGrid];
});
}
- (void)createTwoMentionalArrayHoldingCardButtons {
NSMutableArray *arrayWithRowsOfButtons= [NSMutableArray new];
for (int x = 0; x < 6; x++) {
NSMutableArray *arrayOfButtonsAtRowX = [NSMutableArray new];
for (int i = 0; i < 6; i++) {
CGRect rect = self.gridContainer.bounds;
CGSize cellSize = CGSizeMake(rect.size.width / 6, rect.size.height / 6);
UIButton *buttonInColumnI = [UIButton alloc] initWithFrame:CGRectMake(cellSize.width * i, cellSize.height * x, cellSize.width, cellSize.height);
[buttonInColumnI setImage:[UIImage imageNamed:#"yourCardImage"] forState:UIControlStateNormal];
[buttonInColumnI addTarget:self action:#selector(yourButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[arrayOfButtonsAtRowX addObject:buttonInColumnI];
}
[arrayOfRowsOfButtons addObject:arrayOfButtonsAtRowX];
}
self.twoDimensionalArrayContainingRowsOfCardButtons = arrayWithRowsOfButtons;
}
- (void)displayCardGrid {
for (int x = 0; x < self.twoDimensionalArrayContainingRowsOfCardButtons.count; x++) {
NSMutableArray *arrayOfButtonsAtColumnsAtRowX = self.twoDimensionalArrayContainingRowsOfCardButtons[x];
for (int i = 0; i < arrayOfButtonsAtColumnsAtRowX.count; i++) {
UIButton *buttonAtColumnI = arrayOfButtonsAtColumnsAtRowX[i];
[self.gridContainer addSubview:buttonAtColumnI];
}
}
}
- (void)yourButtonAction:(UIButton *)tappedCard {
//To swap the card image on your tapped button
for (int x = 0; x < self.twoDimensionalArrayContainingRowsOfCardButtons.count; x++) {
NSMutableArray *arrayOfButtonsAtColumnsAtRowX = self.twoDimensionalArrayContainingRowsOfCardButtons[x];
for (int i = 0; i < arrayOfButtonsAtColumnsAtRowX.count; i++) {
UIButton *buttonAtColumnI = arrayOfButtonsAtColumnsAtRowX[i];
if (tappedCard == buttonAtColumnI) {
int row = x;
int column = i;
//Now you can save that the user has tapped something at this row and column.
//While you're here, you can update the card image.
[tappedCard setImage:[UIImage imageNamed:#"CardExposedImage"];
}
}
}
}
I'm writing this all in the box here without running it, so hopefully that works for you. Ended up being a few more lines than expected.
Edit: forgot to add that I separated the building of the card buttons and the displaying of them so that you could call the display method separately. With the property, you also have a retained source of all the cards so you can just fetch them out of the array and change what you need, as needed.

iPhone - how to select from a collection of UILabels?

I have a few UILabels, any one of which will update according to the index of an NSArray index they represent. I thought of selecting them by their tag
self.displayLabel.tag = myArray[index];
but that changes the tag value to whatever my array is holding at the moment
Using a dictionary for whatever tricks it offers instead of an NSArray doesn't help because i still have to select the correct matching label. This is the effect i want to achieve.
self.|mySelectedLabel|.text = myArray[index];
what should i put in |mySelectedLabel| to get the one i'm looking for?
I'm almost ashamed to ask at my reputation level, but this is stymie-ing me
every search only turns up how to set Labels and change, not the process of selecting
Assuming you have set the tags to the appropriate index to match your
array indices you can use [self.view viewWithTag:index];
Why are you not setting the tag with:
self.displayLabel.tag = index;
Also, you could just loop though an array of labels and find the right one:
for (UILabel *label : labelArray) {
if (label.tag == index) {
label.text = #"I found you!";
}
}
Rather than using tags you can refer to your specific textfields by reference:
// Create an array to hold your textfields
NSMutableArray *textFields = [NSMutableArray array]
// Create your textfields and add them to the array
UITextField *textField;
for (NSUInteger idx = 0: idx++; idx < numberOfTextFieldsYouWant) {
textField = [UITextField alloc] initWithFrame:<whateverYouWant>];
[textFields addObject:textField];
}
Since you are adding the objects to an array, rather than using the tag value 0, 1, 2... you can just access it by it's index in the array
So, for what you want to do you can just do:
textfields[index].text = myArray[index];
It's a lot cleaner, doesn't rely on magic tags, and you have an array of all your dynamic textfields that you can remove, or change in one place.
I think tags are vastly overused, and they aren't necessary in most cases.
Just letting you know I reframed the problem and this eventually worked for me without having to use an array
( with endless experimenting, I sort of bumped into it so I don't know if it constitutes good technique )
the desired label corresponding to the bag weight ( one of a number possible ) displays the right update
- (IBAction)acceptWeight:(UIButton *)sender {
int tempValue = (int) currentWeight;
// current weight comes from a UISegementedController
for (UILabel *labels in self.view.subviews)
{
if (labels.tag == currentWeight)
{
bags[tempValue]++;
labels.text = [NSString stringWithFormat:#"%i",bags[tempValue]];
}
}
totalKilo = totalKilo + (int)currentWeight;
self.totalKilo.text = [NSString stringWithFormat:#"%d",totalKilo];
}

iOS: Moving a uibutton by its tag outside of where it was generated

Is it possible? I have an array of buttons created in an void and I want to move one when a neighbouring one is called in the buttonTapped void. I have no trouble moving the button thats pressed because it is the sender, but I also need to move the one next to it and can't seem to get it to move. Each button in the array has a tag value so they're unique.
Thanks
Reasonable intuitive assumption: you generated the buttons so that they are ordered in the array...
- (void)moveButton:(UIButton *)sender // whatever
{
NSUInteger idx = [buttonArray indexOfObject:sender] + 1;
UIButton *nextButton = idx < buttonArray.count ? [buttonArray objectAtIndex:idx] : nil;
// do something with `nextButton`
}

Managed array of UIView using some method

Hello I am noob in Xcode and I want to ask
I have array of UIView's with one image
NSInteger i;
for (i=0; i<CARD_AMOUNT; i++) {
[cardArray addObject:[UIImage imageNamed:#"Back.png"]];
}
and I want to draw it and then to do different things on each (move...etc)
or I must create 10 variables of UIImageView?
Could use a bit more explanation of the app you're trying to build, but essentially if you're wanting to display many different cards on one view (let's say, a game of solitaire) - you'll want to create many UIImageViews. Well, from an pure design perspective, you'd probably want a different class called CardView which knew how to render itself
So, if you wanted many different views all with one image, something like this:
for(int a=0; a<CARD_AMOUNT; a++) {
UIImageView *view = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Back.png"]];
[cardArray addObject:view];
// and maybe you'd want to set the frame locations, or add them to your current view as subviews, or ..
}
To address the comments, your custom CardView might have something like this (there's ways to optimize, but the general concept isn't too bad):
- (void)setRank: (int)rank andSuit:(int)suit {
self.image = [UIImage imageNamed:[NSString stringWithFormat:#"card-%d-%d.png",rank,suit];
self.rank = rank; self.suit = suit;
}
or, better yet (since a card's rank won't change) - some initWithRank: andSuit: method.. dunno.
There's probably 10 other ways to design this.

hittin UIImageViews with another UIImageView

I have 10 UIImageViews on the screen. They are all in an array called posArray. I also have another UIImageView that is dragged by user and can hit those other 10. What is the easies way of displaying a simple NSlog message if the one object hits any of the other 10?
Right now i'm using touchesBegin, touchesMoved to move my one object and this array below to test if the one objects hits any of the other ten.
I'm just thinking that there is an easier, less memory spending way, way of doing this for some reason.
for (int i = 0; i < [posArray count]; i++) {
UIImageView *tempPos;
tempPos = [posArray objectAtIndex:i];
if (CGRectIntersectsRect(red1.frame, tempPos.frame)) {
red1.center = CGPointMake(tempPos.center.x, tempPos.center.y);
NSLog(#"position piece touched");
}
}
You can also use fast enumeration to get some more speed. Also you can add a break statement after you found one match (if you just need one match):
for (UIImageView * tempPos in posArray){
if (CGRectIntersectsRect(red1.frame, tempPos.frame)) {
red1.center = CGPointMake(tempPos.center.x, tempPos.center.y);
NSLog(#"position piece touched");
break;
}
}

Resources