Randomly swap the positions of 4 UIButtons - ios

I have 4 UIButtons, positioned in my VC. I want to randomly swap the positions of each button. I've placed all of the UIButton positions into a NSMutableArray
self.frameArray = [[NSMutableArray alloc] init];
[self.frameArray addObject:[NSValue valueWithCGRect:CGRectMake(60,171,64,64)]];
[self.frameArray addObject:[NSValue valueWithCGRect:CGRectMake(198,171,64,64)]];
[self.frameArray addObject:[NSValue valueWithCGRect:CGRectMake(60,333,64,64)]];
[self.frameArray addObject:[NSValue valueWithCGRect:CGRectMake(198,333,64,64)]];
`
and all of my buttons are in a NSArray
self.buttons = #[self.button1, self.button2, self.button3, self.button4];
but i'm really not sure where to go from here, to make the positions of the UIButtons to randomly swap. Any help would be appreciated

Basically you want to create the frameArray then randomize the order of the frames and then apply them to the buttons.
Step One: Randomize the frameArray (place code after creation of frameArray)
NSUInteger count = [self.frameArray count];
for (NSUInteger i = 0; i < count; ++i) {
NSInteger remainingCount = count - i;
NSInteger exchangeIndex = i + arc4random_uniform((u_int32_t )remainingCount);
[self.frameArray exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex];
}
Step Two: Set Button Frames
for (int i = 0; i < [self.buttons count]; i++) {
[[self.buttons objectAtIndex:i] setFrame:[self.frameArray objectAtIndex:i]];
}

You can use arc4random_uniform() to get a random button and a random Rect from the arrays, and then assign the random Rect to the random button's frame:
NSInteger randomButton = arc4random_uniform((u_int32_t)self.buttons.count);
NSInteger randomRect = arc4random_uniform((u_int32_t)self.frameArray.count);
UIButton *button = self.buttons[randomButton];
NSValue *theRectValue = self.frameArray[randomRect];
CGRect rect = [theRectValue CGRectValue];
button.frame = rect;
I think this will work.

Related

Objective-C: Setting an alpha value with an array of elements

I'm quite new at objective-c and I'm trying to set an array of labels at 0.0 Alpha with a for loop. Can someone give me a help?
NSArray *fadeLabels = #[#"_lonLabel", #"_firstLat",#"_firstLon",#"_firstReal",#"_firstMagnetic",#"_firstSpeed",#"_speedLabel",#"_realNorthLabel",#"_magneticNorthLabel"];
for (int i=0; i<[fadeLabels count]; i++) {
[fadeLabels objectAtIndex:i];
//setAlpha:0.0f?
}
Use an array of views instead of array of strings. insert you views in the array.
NSArray *fadeLabels = #[ _lonLabel, _firstLat, _firstLon, _firstReal, _firstMagnetic, _firstSpeed, _speedLabel, _realNorthLabel, _magneticNorthLabel];
for (int i=0; i<[fadeLabels count]; i++) {
UIView *view = (UIView *)[fadeLabels objectAtIndex:i];
[view setAlpha:0.0f];
}
Your are assigning array with the String with name is equalTo your UILabel object so you need to
replace your array elements like this
#"_lonLabel" to _lonLabel
NSArray *fadeLabels = #[ _lonLabel, _firstLat, _firstLon, _firstReal, _firstMagnetic, _firstSpeed, _speedLabel, _realNorthLabel, _magneticNorthLabel];
for (int i=0; i<[fadeLabels count]; i++) {
UILabel *label = (UILabel *)fadeLabels[i];
label.alpha = 0.0;
}
You also should better use for...in instead of the "old" for.
NSArray *fadeLabels = #[ _lonLabel, _firstLat, _firstLon, _firstReal, _firstMagnetic, _firstSpeed, _speedLabel, _realNorthLabel, _magneticNorthLabel];
for (UILabel *label in fadeLabels) {
label.alpha = 0.0;
}

Is there a way to shuffle/rearrange buttons on an iOS application?

I am developing an iOS application. I have 35 buttons on the storyboard. Is there anyway I can use a random generator to randomly rearrange or shuffle the buttons and still maintain the button's functionality?
You can add all the button objects to an NSMutableArray called buttonsArray and then use the following loop to exchange the buttons:
for (int i = 0; i < buttonsArray.count; i++) {
int random1 = arc4random() % [buttonsArray count];
int random2 = arc4random() % [buttonsArray count];
[buttonsArray exchangeObjectAtIndex:random1 withObjectAtIndex:random2];
}
If each of the button is belongs to a class like below then its really easy to shuffle them,
MyRandomButton {
NSInteger uniqueId;
NSString *title;
....
....
}
Now, what you can do is, at initial, prepare number of objets of MyRandomButton (equals to count of buttons on your StoryBoard). And add it into a mutable array (NSMutableArray).
Like this,
for(NSInteger index = 1; index <= 35; index++) {
MyRandomButton *btnInfo = ...;
btnInfo.uniqueId = index;
btnInfo.title = #sometitle;
[myButtonsArray addObject:btnInfo];
}
In next step, if you're done with something (and you want to shuffle all buttons) then get help from this question, What's the Best Way to Shuffle an NSMutableArray? to shuffle your array.
Loop through all buttons on your StoryBoard (in your case: 1 to 35).
for(NSInteger index = 1; index <= 35; index++) {
UIButton *button = (UIButton *)[yourView viewWithTag:index];
MyRandomButton *btnInfo = [myButtonsArray objectAtIndex:(index-1)];
button.tag = btnInfo.uniqueId;
[button setTitle:btnInfo.title forState:UIControlStateNormal];
}
Inside above loop, we've updated buttons title and tag.
Done!
That is possible, it will require some coding:
Create NSMutableArray with all buttons of the view in it.
Randomize its order.
Run over the buttons array and on each iteration keep last
button view and switch its rect with current button and
vice versa.
For better understanding, here is the code (ready for use, just copy paste):
- (void)viewDidLoad {
[super viewDidLoad];
// Getting buttons into array
NSMutableArray * buttonsArr = [self getButtonsArray];
// Shuffling buttons location
if(buttonsArr && [buttonsArr count]) {
[self randomizeButtonsLocation:buttonsArr];
} else {
NSLog(#"No buttons found");
}
}
- (void)randomizeButtonsLocation:(NSMutableArray *)buttonsArr {
// Randomizing order in button array
buttonsArr = [self randomizeArr:buttonsArr];
UIButton * lastBtn = nil;
for (UIButton * btn in buttonsArr) {
if(!lastBtn) {
lastBtn = btn;
} else {
// Put a side frames of last and current buttons
CGRect lastBtnRect = [lastBtn frame];
CGRect currentBtnRect = [btn frame];
// Switch frames
[btn setFrame:lastBtnRect];
[lastBtn setFrame:currentBtnRect];
// Keeping last btn for next iteration
lastBtn = btn;
}
}
}
- (NSMutableArray *)getButtonsArray {
// Getting all buttons in self.view and inserting them into a mutable array
NSMutableArray * resultArr = [[NSMutableArray alloc] init];
for (UIButton * btn in [self.view subviews]) {
if([btn isKindOfClass:[UIButton class]]) {
[resultArr addObject:btn];
}
}
return resultArr;
}
- (NSMutableArray *)randomizeArr:(NSMutableArray *)arr {
NSUInteger count = [arr count];
for (NSUInteger i = 0; i < count; ++i) {
unsigned long nElements = count - i;
unsigned long n = (arc4random() % nElements) + i;
[arr exchangeObjectAtIndex:i withObjectAtIndex:n];
}
return arr;
}
For anyone who needs to do this in SWIFT, here's how:
func randomlyPositionAnswerButtons() {
var posX = CGFloat()
var posY_button1 = CGFloat()
var posY_button2 = CGFloat()
var posY_button3 = CGFloat()
// if self.view.frame.size.height == 667 { /* ======== IPHONE 7, 7S, 8 ======== */
posX = 24 // Set all buttons to start from 37
posY_button1 = 310
posY_button2 = 405
posY_button3 = 500
// }
let tab = [
[1,2], [0,2], [0,1]
]
let indexArray = [
NSValue(cgRect:CGRect(x: posX, y: posY_button1, width: 350, height: 74)),
NSValue(cgRect:CGRect(x: posX, y: posY_button2, width: 350, height: 74)),
NSValue(cgRect:CGRect(x: posX, y: posY_button3, width: 350, height: 74))
] as NSMutableArray
// Shuffle the array
var p = UInt32()
p = arc4random() % 3
indexArray.exchangeObject(at: 0, withObjectAt: Int(p))
indexArray.exchangeObject(at: tab[Int(p)][0], withObjectAt: tab[Int(p)][1])
// Assign the buttons their new position
optionButtonA.frame = (indexArray[0] as AnyObject).cgRectValue
optionButtonB.frame = (indexArray[1] as AnyObject).cgRectValue
optionButtonC.frame = (indexArray[2] as AnyObject).cgRectValue
}
Rearranging the fixed buttons sounds like a headache, if only in terms of autolayout constraints.
Why don't you:
Create and position 35 "generic" buttons on the storyboard, each with their own outlet (properties named button01, button02, etc.), or better: use an outlet collection?
Assign a unique tag to each button (0, 1, 2, 3...) on the storyboard.
Assign the same #IBAction method to all your buttons on the storyboard.
At runtime, determine and assign a specific "role" to each button at random (and set each button's label accordingly). But keep track of which button is which, using -for example- a dictionary.
The action linked to all buttons will use the tag of the sender and the information contained in the dictionary mentioned above to determine which "role" button was tapped, and act accordingly.
Does it make sense?

Displaying UILabel dynamcially from the NSMutableArray

I have a mutable array as below
labelArrays = [NSMutableArray arrayWithObjects:#"One”, #“Two”, #“Three", nil];
I want to display all the list array values as individual label. This is like displaying the UILabel dynamically using the array values. What i did is as below,
int count=20;
for(int i = 0; i < [labelArrays count]; i++){
label1 = [[UILabel alloc]init];
label1.text = [labelArrays componentsJoinedByString:#" "];
label1.textColor = [UIColor colorWithRed:0.643 green:0.643 blue:0.639 alpha:1];
label1.layer.borderWidth = 1.0;
label1.layer.cornerRadius = 8;
count+=20;
[self addSubview:label1];
}
Here I can only display all the array values inside the single label. How can i display the array values in multiple labels dynamically.
//viewcontroller
myView.label1.frame =CGRectMake(588,200,200,28);
your coding is fine , but you are not added the frame , try this
label1 = [[UILabel alloc] initWithFrame: CGRectMake(0,count,aslikeyourwidth,aslikeyourheight)];
Define a public method for view in which you are willing to add the UILabel, pass a CGRect to that method and then according to your design, with each iteration update the x coordinate or y coordinate. Follow the below steps:
// code in your viewcontroller
[myView designYourMultipleLabelsWithStartingFrame:CGRectMake(588,200,200,28)]; // pass the frame of the first label.
Now add the multiple UILabel in your view class, let's assume you have set labelArrays.
// code in view class
-(void) designYourMultipleLabelsWithStartingFrame:(CGRect) frame{
float xCoordinate = frame.origin.x;
float yCoordinate = frame.origin.y;
for(int counter = 0; counter < [labelArrays count]; counter++){
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(xCoordinate,yCoordinate,labelWidth,labelHeight)];
label.text = [labelArrays objectAtIndex:counter];
[self addSubview:label];
// update the x coordinate or y coordinate according to your design. Let's say we need the labels in vertical order, so update the y Coordinate by labelHeight and gap between two labels.
yCoordinate = yCoordinate + labelHeight + gapBetweenTwoLabels.
}
}
Hope this will help.
Just a thought - would you not be better using a tableview to show the array?
It looks like you may need to just handle their position on the view as the code looks like it is adding the labels but it is placing them over each other.

CCScrollView scroll and touch events never firing

I can't find any helpful tutorials or explanation on how to use a CCScrollView. I have a grid-layout of sprites and labels (listing achievements for an iOS game). There are more than can fit on the screen so I want the user to be able to scroll.
To scroll, the user would swipe/pan upwards, to reveal the sprites etc which are lower.
I've found a few code samples and they seem to indicate you just need to add your content node to the scroll node and it will take care of the rest.
It doesn't seem to work. There's no scroll, and the pan/touch events on the scroll layer never seem to fire. The close button I have at the same child (sibling to the scroll view) no longer works as well.
I'm not using SpriteBuilder.
// Node to hold all sprites/labels
scrollContents = [CCNode node];
// I add a bunch of sprites/labels in a grid view
for( NSString *key in badgeKeys ){
// logic to load the sprite would be here
CCSprite *badge = [CCSprite spriteWithSpriteFrame:frame];
badge.positionType = CCPositionTypeNormalized;
badge.position = ccp(xPos,yPos);
[scrollContents addChild:badge];
// some logic to increment x/y position logic, for grid layout
}
// Scroll view
scrollView = [[CCScrollView alloc] initWithContentNode:scrollContents];
scrollView.horizontalScrollEnabled = NO;
scrollView.verticalScrollEnabled = YES;
[scrollView setBounces:NO];
// My sprites never even show unless I manually set this
scrollContents.contentSize = CGSizeMake(self.contentSize.width,960);
NSLog(#"scrollContents contentsize: %f %f", scrollContents.contentSize.width,scrollContents.contentSize.height);
[self addChild:scrollView];
ok, here is a working example (i deconstructed part of my code to give you a fully working code sample) of a scrolling menu with 'live' buttons inside. I just tested this 'deconstruction' , it works
- (void) scrollingMenuWithCharmsTest {
// setup something to scroll
GameInventory *gi = [GameInventory sharedGameInventory];
while (gi.armorCharms.count < 20) {
[gi addArmorCharm:[ArmorCharm createRandomArmorCharm]];
}
CCNode *contentNode = [self charmsContentNodeFor:gi.armorCharms
showEquiped:NO
spacingBetweenMenuItems:8
target:self
selector:#selector(onArmorCharmSelected:)];
// setup a clipping node to crop out the CCScrollingMenu
CCNodeColor *ccn = [CCNodeColor nodeWithColor:[CCColor blackColor] width:180 height:200];
ccn.anchorPoint = ccp(0, 0);
CCClippingNode *cn = [CCClippingNode clippingNodeWithStencil:ccn];
cn.alphaThreshold = 0.05f;
[self addChild:cn];
cn.inverted = NO;
cn.positionInPointsV = ccp(50, 50);
cn.anchorPoint = ccp(0, 0);
cn.contentSizeInPoints = CGSizeMake(180, 200);
// setup scrolling menu
CCScrollView * bsm = [[CCScrollView alloc] initWithContentNode:contentNode];
bsm.contentSize=CGSizeMake(180,200);
[cn addChild:bsm];
bsm.position = ccp(0, 0);
bsm.bounces = YES;
bsm.pagingEnabled = NO;
bsm.verticalScrollEnabled = YES;
bsm.horizontalScrollEnabled = NO;
bsm.contentSizeInPoints = CGSizeMake(180, 200); // inPoints only after the node has a parent
for (CharmAbstractBoxMenuItem *lmi in bsm.contentNode.children) {
TRACE(#"item %# is at %#", lmi.item.description, NSStringFromCGPoint(lmi.positionInPointsV));
}
TRACE(#"number of pages : %i", bsm.numVerticalPages);
}
- (CCNode *)charmsContentNodeFor:(NSDictionary *)keyedItems
showEquiped:(BOOL)isShowEquiped
spacingBetweenMenuItems:(float)inSpacing
target:(id)inTarget
selector:(SEL)inSelector {
NSSortDescriptor *sortOrder = [NSSortDescriptor sortDescriptorWithKey:#"self" ascending:YES];
NSArray *sortedKeys = [[keyedItems allKeys] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortOrder]];
float initialY = 0;
float currentY = initialY;
NSUInteger itemNumber = 0;
CGFloat width = 0;
CGFloat height = 0;
CCNode *contentNode = [CCNode node];
for (NSUInteger loopi = 0; loopi < [sortedKeys count]; loopi++) {
NSString *key = [sortedKeys objectAtIndex:loopi];
CharmAbstract *ci = [keyedItems objectForKey:key];
if (ci) {
CharmAbstractBoxMenuItem *cmi = [CharmAbstractBoxMenuItem itemBoxFor:ci
target:inTarget
selector:inSelector
];
cmi.toolTip = ci.toolTip;
cmi.position = ccp(deviceOffset(0), currentY);
cmi.key = key;
[contentNode addChild:cmi z:0 name:[NSString stringWithFormat:#"%li", (long) itemNumber]];
currentY += cmi.contentSizeInPoints.height + inSpacing;
itemNumber++;
if (cmi.contentSize.width > width) width = cmi.contentSize.width;
height += cmi.contentSize.height;
if (loopi < sortedKeys.count - 1) height += inSpacing;
}
else {
MPLOG(#"*** Key [%#] yielded no items.", key);
}
}
contentNode.contentSizeType = CCSizeTypePoints;
contentNode.contentSize = CGSizeMake(width, height);
return contentNode;
}
some notes :
i gave you my 'build content node' routine so you know the ins and outs of positions and sizes.
my charmBoxMenuItemss derive from 'CCButton' and are hot ... In the full version of this code snippet, i extended CCScrollView to prevent the buttons from being 'hot' outside the crop area (although they are cropped out from view, they are still 'visible' by default, and could respond when a random tap occurs above or below the crop area).
For clipping node with stencil, you need to add this in your setupCocos2dWithOptions line:
CCSetupDepthFormat : [NSNumber numberWithUnsignedInt:GL_DEPTH24_STENCIL8_OES]

UIButton cannot be displayed

in my app i'm using a scroll View (paging). each view in the scroll view (UIImageView) contains an image and each image has tags (buttons) scattered all over the pic. so here is my code:
for (int i = 0; i < self.pictures.count; i++)
{
//The first loop is to loop to every picture
int nbOfTags = classObject.tagsImages.count;
for (int j = 0; j < nbOfTags; j++)
{
//Second loop is to loop to each tag corresponding to the picture
ListTags *listTags = [[ListTags alloc]init];
NSMutableArray *tagInfo = [[NSMutableArray alloc]init];
listTags = [tagInfo objectAtIndex:j];
float tagX = [listTags.tag_x floatValue];
float tagY = [listTags.tag_y floatValue];
float x = 1024*i+ tagX;
float y = tagY;
CGRect rect = CGRectMake(x, tagY, 20, 20);
UIButton *button = [[UIButton alloc] initWithFrame:rect];
UIImage * buttonImage = [UIImage imageNamed:#"blueCircle.png"];
[button setBackgroundImage:buttonImage forState:UIControlStateNormal];
[self.scrollView addSubview:button];
}
}
problem: when i run the code i can't see the buttons (tags) on the picture. if i replace "i" with "j" in
float x = 1024*i+ tagX;
the buttons can be seen, however it's not the desired coordinates. so why "i" can't work? am i doing anything wrong or missing anything?
In interface builder (or storyboard - IB) uncheck autolayout (if it's checked) then your button will be displayed where you want.
See my comments in the code below:
for (int i = 0; i < self.pictures.count; i++)
{
//The first loop is to loop to every picture
/*
* This will give you the same number of tags for every image.
* Is that what you want?
*/
int nbOfTags = classObject.tagsImages.count;
for (int j = 0; j < nbOfTags; j++)
{
//Second loop is to loop to each tag corresponding to the picture
/* This allocates a brand new ListTags object EVERY time through the loop. */
ListTags *listTags = [[ListTags alloc]init];
/* This allocates a brand new EMPTY array EVERY time through the loop. */
NSMutableArray *tagInfo = [[NSMutableArray alloc]init];
/*
* This discards the new ListTags object that you just created two lines ago
* and replaces it with an object from tagInfo. Which doesn't exist because
* you just created a new array in the previous line. This really should
* crash, which leads me to believe that this isn't the actual code that you
* are using. Post the actual code that you are using so that we can help you
* better.
*/
listTags = [tagInfo objectAtIndex:j];
/* None of this is valid since listTags is nil/we never should have gotten here. */
float tagX = [listTags.tag_x floatValue];
float tagY = [listTags.tag_y floatValue];
float x = 1024*i+ tagX;
float y = tagY;
CGRect rect = CGRectMake(x, tagY, 20, 20);
UIButton *button = [[UIButton alloc] initWithFrame:rect];
UIImage * buttonImage = [UIImage imageNamed:#"blueCircle.png"];
[button setBackgroundImage:buttonImage forState:UIControlStateNormal];
[self.scrollView addSubview:button];
}
}

Resources