I have a UIScrollView and five UIViewControllers A,B,C,D,E.
The order of the UIViewController views to be shown in the scroll view depends on some pre-requisite (e.g. one day it might be B,A,E,D,C and the next E,A,D,B,C and so on).
How can I keep track of which UIViewController is to be displayed (as in something like tags for each UIViewController which can be stored in an array and updated)?
Is it possible to assign views to the scroll view in the dynamic fashion shown above?
you want to make out a way to permutate the 5 view controllers ?
maybe it helps to use the property of prime numbers.
it depends on the count of permutations you want.
but since there's only 5 view controllers, there can't be more than 5! = 120 permutations
what's good about 5 is that it's a prime number
so if you calculate (n*a) % 5,where a is a constant number from 0 to 4, and n is a variable from 1 to 5 you get a table like this :
a==0, 0,0,0,0,0
a==1, 1,2,3,4,0
a==2, 2,4,1,3,0
a==3, 3,1,4,2,0
a==4, 4,3,2,1,0
as you've seen, that's 5 completely different permutations, in which each number appears exactly once.
so, if you want to have 120 permutations
just apply a const number b (from 0 to 4), and calculate (n*a + b) % 5
then you'll get 5 times the previous result, which is 125 , but there are some repetitions, then the final result will be 120 permutations.
so, the only thing you have to do is to determine a and b with your prerequisite
and apply the following code:
int result = (n*a + b)%5;
static UIView *addedView = nil;
if(addedView != nil)
[addedView removeFromSuperview];
switch(result)
{
case 0:
[self.scrollView addSubview:VCA.view];
addedView = VCA.view;
break;
case 1:
[self.scrollView addSubview:VCB.view];
addedView = VCB.view;
break;
//and so on...
}
hope it helps
Related
Say ...
you have about 20 Thing
very often, you do a complex calculation running through a loop of say 1000 items. The end result is a varying number around 20 each time
you don't know how many there will be until you run through the whole loop
you then want to quickly (and of course elegantly!) access the result set in many places
for performance reasons you don't want to just make a new array each time. note that unfortunately there's a differing amount so you can't just reuse the same array trivially.
What about ...
var thingsBacking = [Thing](repeating: Thing(), count: 100) // hard limit!
var things: ArraySlice<Thing> = []
func fatCalculation() {
var pin: Int = 0
// happily, no need to clean-out thingsBacking
for c in .. some huge loop {
... only some of the items (roughly 20 say) become the result
x = .. one of the result items
thingsBacking[pin] = Thing(... x, y, z )
pin += 1
}
// and then, magic of slices ...
things = thingsBacking[0..<pin]
(Then, you can do this anywhere... for t in things { .. } )
What I am wondering, is there a way you can call to an ArraySlice<Thing> to do that in one step - to "append to" an ArraySlice and avoid having to bother setting the length at the end?
So, something like this ..
things = ... set it to zero length
things.quasiAppend(x)
things.quasiAppend(x2)
things.quasiAppend(x3)
With no further effort, things now has a length of three and indeed the three items are already in the backing array.
I'm particularly interested in performance here (unusually!)
Another approach,
var thingsBacking = [Thing?](repeating: Thing(), count: 100) // hard limit!
and just set the first one after your data to nil as an end-marker. Again, you don't have to waste time zeroing. But the end marker is a nuisance.
Is there a more better way to solve this particular type of array-performance problem?
Based on MartinR's comments, it would seem that for the problem
the data points are incoming and
you don't know how many there will be until the last one (always less than a limit) and
you're having to redo the whole thing at high Hz
It would seem to be best to just:
(1) set up the array
var ra = [Thing](repeating: Thing(), count: 100) // hard limit!
(2) at the start of each run,
.removeAll(keepingCapacity: true)
(3) just go ahead and .append each one.
(4) you don't have to especially mark the end or set a length once finished.
It seems it will indeed then use the same array backing. And it of course "increases the length" as it were each time you append - and you can iterate happily at any time.
Slices - get lost!
I am pretty new to Swift and I cant find the answer to my issue with Swift.
Creating an lotto number generator and I can do it using a label but I'm having difficulties mapping these numbers to images (e.g. lotto ball images) so whenever numbers are populated, it will convert these numbers to images and shows in a custom Cell.
Currently using a tableView with customer Cell - i have six UIImageView in a cell.
Through out my practice, i found myself repeating the same codes. e.g. ball1.image = arc4Random_uniform(45) and ball2 ball3 and so on.. want to learn the harder way. i mean not repeating the codes. please masters how could i populate 6 numbers from 1-45 without duplicate and shows 6 lotto ball images in a cell?
To reduce code usage, make methods on the view controller and call it with the array of image views.
Here is the method:
func setImages(imageViews: [UIImageView]) {
var numSet = Set<Int>()
while numSet.count < imageViews.count
{
numSet.insert(Int(arc4random_uniform(45)) + 1) // Assuming you want numbers from 1 to 45 inclusive of both.
}
for (i, elem) in numSet.sort(<).enumerate() // Don't use sort if not needed.
{
imageViews[i].image = UIImage(named: "lotto\(elem).png") // Or whatever naming convention you used for the images.
}
}
Just call the setImages with the array of imageViews. It uses the count of the array to generate enough numbers. Don't forget to change the naming scheme used for the images. I assumed you used a naming scheme of lotto1.png, lotto2.png ... lotto44.png
I have NSMutableArray named *arrWords containing list of 50 words. e.g. Bond/Bind/King/Sing/Ring etc.
I use following function to display random words on screen.
-(void)displayRandomWord //This is called from timer so it randomly shows words on screen
{
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(150,150,200,30)];
[lbl setText:[arrWords objectAtIndex:round([self randomXValueBetween:0 andValue:49])];
[self.view addSubView:[lbl]; //this label will be removed after 1 second I have written code for that.
}
- (float)randomXValueBetween:(float)low andValue:(float)high {
return (((float) arc4random() / 0xFFFFFFFFu) * (high - low)) + low;
}
This works fine. Now my problem is let say after certain amount of time I want to increase frequency of the particular word to be appear.
I want to setup logic like initially "Sing" is to be appeared 3 times in first 10 seconds. After 10 seconds I want it to be appear atleast 2 times more than 3 times (i.e. 5 times).
Because it is random picking of words "Sing" may appear 3 times in first 10 seconds or may not.
I am stuck here to setup logic. Any suggestions ?
My first thought was to increase the amount of word "Sing" after certain time.
What do I mean is this:
If I have 50 words. A chance to get a "sing" word is 1/50.
Now after 10 seconds I insert additional word "sing" to the array. Now I have 51 words in it.
My chance of getting word "sing" just increased to" 2/51.
That is the simplest way I could think of.
Hope that helps.
EDIT: I just thought that if you want to make sure that you have at least some amount.
You could do it like this.
If you know exactly how many words you will display in first 10 seconds? Example. 10 which is 1 per second. Then you could do this:
Pseudocode
word = get me a random word from array;
displayCount = 0; number of words already displayed;
max = 10; Displaying 10 words per 10 second
targetWord = "Sing"
targetWordCount = 0; number of words targeted for this time frame;
requiredTargetWordCount = 2; number of times a word should be displayed during the time frame
for(word; displayCount < max; word.next){
if word = targetWord
targetWordCount ++
display word
else if displayCount < max - requiredTargetWordCount && targetWordCount != requiredTargetWordCount
display targetWord
targetWordCount ++
displayCount ++
}
You can add words weights. Declare another NSMutableArray (let's say arrWordsWeights) of the same size as arrWords. Fill it with all 1's initially. Then you can increase or decrease frequency of any word just by increasing or decreasing it's frequency in weights array. To get the random index which respects the weighting you can use the following code:
int maxVal = 0;
for (int i in arrWordsWeights)
maxVal += i;
int r = arc4random() % maxVal + 1;
int index = 0;
while (r > 0 && index < [arrWordsWeights size])
{
r -= [arrWordsWeights objectAtIndex:index];
index++;
}
return index - 1;
At the moment I can't check if it will compile, but at least you should get an idea.
I have one UIViewController containing a UITableView. Along with this I have 3 buttons and 4 Mutablable arrays.
Array "prime" is the primary datasource for the table view.
When I hit button a , b or c I write
prime= a
[table reloadData];
or
prime= b
[table reloadData];
The problem I have is that after say 2 button pushes prime is not equal to either a or b. It contents now contain the contents of a "and" b and it's contents will continue to accumulate after every assign.
How can I make 1 nsmutuablearray switch its contents to be that of another?
I know I can just remove all objects and then add more. I more wanted to know if its possible to make prime be a pointer to a b or c?
Try instead:
prime = [a copy];
I am a newbie of xcode and objective-c and I have a few questions regarding to the code sample of a game attached below. It is written in objective C, Xcode for iphone4 simulator. It is part of the code of 'ball bounce against brick" game. Instead of creating the image by IB, the code supposes to create (programmatically) 5 X 4 bricks using 4 different kinds of bricks pictures (bricktype1.png...). I have the bricks defined in .h file properly and method written in .m.
My questions are for the following code:
- (void)initializeBricks
{
brickTypes[0] = #"bricktype1.png";
brickTypes[1] = #"bricktype2.png";
brickTypes[2] = #"bricktype3.png";
brickTypes[3] = #"bricktype4.png";
int count = 0;
for (int y = 0; y < BRICKS_HEIGHT; y++)
{
for (int x = 0; x < BRICKS_WIDTH; x++)
{
UIImage *image = [ImageCache loadImage:brickTypes[count++ % 4]]; - Line1
bricks[x][y] = [[[UIImageView alloc] initWithImage:image] autorelease];
CGRect newFrame = bricks[x][y].frame;
newFrame.origin = CGPointMake(x * 64, (y * 40) + 50);
bricks[x][y].frame = newFrame;
[self.view addSubview:bricks[x][y]];
}
}
}
1) When it is compiled, error "ImageCache undeclared" in Line 1. But I have already added the png to the project. What is the problem and how to fix it? (If possible, please suggest code and explain what it does and where to put it.)
2) How does the following in Line 1 work? Does it assign the element (name of .png) of brickType to image?
brickTypes[count ++ % 4]
For instance, returns one of the file name bricktype1.png to the image object? If true, what is the max value of "count", ends at 5? (as X increments 5 times for each Y). But then "count" will exceed the max 'index value' of brickTypes which is 3!
3) In Line2, does the image object which is being allocated has a name and linked with the .png already at this line before it is assigned to brick[x][y]?
4) What do Line3 and Line5 do? Why newFrame on left in line3 but appears on right in Line5?
5) What does Line 4 do?
When it is compiled, error "ImageCache undeclared" in Line 1. But I have already added the png to the project. What is the problem and how to fix it?
ImageCache is the name of an object you're supposed to create. Since you haven't created one, it's undefined.
How does the following in Line 1 work? Does it assign the element (name of .png) of brickType to image?
It uses the count modulo 4 (% is the modulus operator) as the index to the array and then increments count. It will not exceed the array size - that's what the modulus operation is preventing. Suggest you study: Modulo Operation
In Line2, does the image object which is being allocated has a name and linked with the .png already at this line before it is assigned to brick[x][y]?
Not sure I understand the question, but yes, the image has been loaded.
What do Line3 and Line5 do? Why newFrame on left in line3 but appears on right in Line5?
They set newFrame to the same frame as an existing image and then create a CGPoint with which they set a new origin for newFrame. Lines 3, 4, and 5 get the frame of an image, set it's origin to a new value, and then replace the image with the newFrame.
1) ImageCache is not a standard class, so the class must be defined somewhere in the game's project. You are missing an import "ImageCache.h" (or whichever header file ImageCache is defined in).
2) First, the count modulo 4 is taken. That is: divide count by 4 and take the rest. That rest is used as an index to brickTypes. So you will always get a value between 0 and 3 (including). Then, count is increased by 1 (the postfix ++ operator first returns the variable value and afterwards increases the variable by one). Since brickType seems to of type NSString *brickType[4] (you haven't showed us the declaration) this code will always return a string #"bricktype1.png" ... #"bricktype4.png".
3) I don't understand that question, sorry. Please try to explain.
4) First, the position and size of the brick are queried (line 3). Then, the position is changed, while leaving the size unmodified (line 4). Lastly, the changed position and size are assigned to the brick. In effect, this just moves the brick to a new position. It must be done this way because frame is property of type CGRect (that is: it's a method called setFrame:(CGRect)rect but the compiler provides a more convenient way to access it) which is a struct containing other structs, so one can't just do brick[x][y].frame.origin.x = x * 64.
5) It assigns a new position to the brick (or rather, to the struct queried from the brick). The CGPointMake(x,y) method returns a struct of type CGPoint. The result is assigned to the frame's member origin. One could also write:
newFrame.origin.x = x * 64;
newFrame.origin.y = (y * 40) + 50;
(here you can directly do the assigns because newFrame is a struct on your stack, not a method like brick[x][y].frame)