laying out images in UIScrollView automatically - iphone

i have a list of images retrieved from xml i want to populate them to a uiscrollview in an order such that it will look like this.
1 2 3
4 5 6
7 8 9
10
if there is only 10 images it will just stop here.
right now my current code is this
for (int i = 3; i<[appDelegate.ZensaiALLitems count]-1; i++) {
UIButton *zenbutton2 =[UIButton buttonWithType:UIButtonTypeCustom];
Items *ZensaiPLUitems = [appDelegate.ZensaiALLitems objectAtIndex:i];
NSURL *ZensaiimageSmallURL = [NSURL URLWithString:ZensaiPLUitems.ZensaiimageSmallURL];
NSLog(#"FVGFVEFV :%#", ZensaiPLUitems.ZensaiimageSmallURL);
NSData *simageData = [NSData dataWithContentsOfURL:ZensaiimageSmallURL];
UIImage *itemSmallimage = [UIImage imageWithData:simageData];
[zenbutton2 setImage:itemSmallimage forState:UIControlStateNormal];
zenbutton2.frame=CGRectMake( (i*110+i*110)-660 , 300, 200, 250);
[zenbutton2 addTarget:self action:#selector(ShowNextZensaiPage) forControlEvents:UIControlEventTouchUpInside];
[scrollView addSubview:zenbutton2];
}
notice the CGRectMake , i have to manually assign fixed values to position them.
Is there any way to populate them out without manually assigning.
for e.g the images will automatically go down a position once the first row has 3 images and subsequently for the rest.

If I understand what you are saying, you should be able to write a simple block of code that assigns a position based on the image number.
Something like this (where i is the image number, starting from 0):
- (CGPoint)getImageOrigin:(NSInteger)imageNumber {
CGFloat leftInset = 30;
CGFloat xOffsetBetweenOrigins = 80;
CGFloat topInset = 20;
CGFloat yOffsetBetweenOrigins = 80;
int numPerRow = 3;
CGFloat x = leftInset + (xOffsetBetweenOrigins * (imageNumber % numPerRow));
CGFloat y = topInset + (yOffsetBetweenOrigins * floorf(imageNumber / numPerRow));
CGPoint imageOrigin = CGPointMake(x, y);
return imageOrigin;
}
The origin being calculated here is the upper left corner of each image.
To calculate the x value, I start with the minimum distance from the left side of the screen (leftInset). Then, I add the distance from the left side of one image to the next image, multiplied by the column (imageNumber % numPerRow).
Y is calculated in a similar fashion, but to calculate the row, I use the imageNumber / numPerRow rounded down.
Edit:
You asked me to explain further, so I'll see what I can do.
OK, so I want to be able to input the image number (starting at 0) into my function, and I want the origin (upper left corner point) back.
leftInset is the distance between the left edge of the view, and the left edge of the first image.
xOffsetBetweenOrigins is the distance from the left edge of one image to the left edge of the next image on the same row. So, if I set it to 80 and my image is 50px wide, there will be a 30px gap between two images in the same row.
topInset is like left inset. It is the distance from the top edge of the view to the top edge of the images in the top row.
yOffsetBetweenOrigins is the distance from the top edge of an image to the top edge of the image below it. If I set this to 80, and my image is 50px tall, then there will be a 30px vertical gap between rows.
numPerRow is straightforward. It is just the number of images per row.
To calculate the x value of the upper left corner of the image, I always start with the leftInset, because it is constant. If I am on the first image of a row, that will be the entire x value. If I am on the second image of the row, I need to add xOffsetBetweenOrigins once, and if I am on the third, I need to add it twice.
To do this, I use the modulus (%) operator. It gives me the remainder of a division operation, so when I say imageNumber % numPerRow, I am asking for the remainder of imageNumber/numPerRow.
If I am on the first image (imageNumber = 0), then 3 goes into 0 no times, and the remainder is 0. If I am on the second image (imageNumber = 1), then I have 1/3. 3 goes into 1 0 times, but the remainder is 1, so I get xOffsetBetweenOrigins*1.
For the y value, I do something similar, but instead of taking the modulus, I simply divide imageNumber/numPerRow and round down. Doing this, I will get 0 for 0, 1, and 2. I will get 1 for 3, 4, and 5.
Edit:
It occurred to me that you might actually have been asking how to use this method. In your code, you would say something like
CGRect newFrame = zenbutton2.frame;
newFrame.origin = [self getImageOrigin:i];
zenbutton2.frame = newFrame;
Another Edit:
Maybe you could try this?
CGPoint origin = [self getImageOrigin:i];
zenbutton2.frame = CGRectMake(origin.x, origin.y, width, height);
If that doesn't work, throw in
NSLog("Origin Values: %f,%f", origin.x, origin.y);
to make sure that you are actually getting something back from getImageOrigin.

I think you probably want to wrap your loop in another loop, to get what I'm going to call a 2D loop:
for (int row = 0; row < num_rows; row++) {
for (int col = 0; col < num_cols; col++) {
// code before
zenButton2.frame = CGRectMake(origin x dependent on col,
origin y dependent on row,
width,
height);
// code after
}
}
Where the x and y of the CGRectMake() are multiples of the width and height of your image times the row and column respectively. Hope that makes sense.

Related

EaselJS shape x,y properties confusion

I generate a 4x4 grid of squares with below code. They all draw in correct position, rows and columns, on canvas on stage.update(). But the x,y coordinates for all sixteen of them on inspection are 0,0. Why? Does each shape has it's own x,y coordinate system? If so, if I get a handle to a shape, how do I determine where it was drawn originally onto the canvas?
The EaselJS documentation is silent on the topic ;-). Maybe you had to know Flash.
var stage = new createjs.Stage("demoCanvas");
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
var square = new createjs.Shape();
square.graphics.drawRect(i*100, j*100, 100, 100);
console.log("Created square + square.x + "," + square.y);
stage.addChild(square);
}
}
You are drawing the graphics at the coordinates you want, instead of drawing them at 0,0, and moving them using x/y coordinates. If you don't set the x/y yourself, it will be 0. EaselJS does not infer the x/y or width/height based on the graphics content (more info).
Here is an updated fiddle where the graphics are all drawn at [0,0], and then positioned using x/y instead: http://jsfiddle.net/0o63ty96/
Relevant code:
square.graphics.beginStroke("red").drawRect(0,0,100,100);
square.x = i * 100;
square.y = j * 100;

SpriteKit : calculate distance between two texture masks

I have two irregular shapes in SpriteKit, and I want to calculate the vertical distance from the base of a space ship and the (irregular) terrain right below.
Is there a way to do it ?
Thanks !
Place an SKPhysicsBody that is in a shape of a line at the center of your ship with a width of 1 and the height of your scene, then in the didBeginContact method, grab the 2 contact points. You now know 2 points, just use the distance formula (in this case it is just y2-y1) and you have your answer
I found a different way to solve my problem, but I think that KnightOfDragon's one is conceptually better (although I did not manage to make it work).
The terrain's texture is essentially a bitmap with opaque and transparent pixels. So I decided to parse these pixels, storing the highest opaque pixel for each column, building a "radar altitude map". So I just have to calculate the difference between the bottom of the ship and the altitude of the column right beneath its center:
CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(terrain.texture.CGImage));
const UInt32 *pixels = (const UInt32*)CFDataGetBytePtr(imageData);
NSMutableArray *radar = [NSMutableArray new];
for (long col = 0; col < terrain.size.width; col++)
[radar addObject:#(0)];
for (long ind = 0; ind < (terrain.size.height * terrain.size.width); ind++)
{
if (pixels[ind] & 0xff000000) // non-transparent pixel
{
long line = ind/terrain.size.width;
long col = ind - (line*terrain.size.width);
if ([radar[col]integerValue] <terrain.size.height-line) radar[col] = #(terrain.size.height-line);
}
}
This solution could be optimized, of course. It's just the basic idea.
I've added an image to show the original texture, its representation as opaque/transparent pixels, and a test by putting little white nodes to check where the "surface" was.

UIScrollView Subview Scaling

I'm attempting to scale a UIScrollView's subviews, based off of how far away they are from the center of the container. I think I'm close, but it isn't quite right. The views to the left of the center line are scaled a bit less than those to the right.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
for( unsigned int i=0; i<[scrollView.subviews count]; i++ )
{
UIView *v = [scrollView.subviews objectAtIndex:i];
// not quite right here
float scale = 1. - ( abs(scrollView.center.x - ( v.center.x - scrollView.contentOffset.x ) ) / scrollView.contentSize.width/2 );
v.alpha = scale;
v.transform = CGAffineTransformIdentity;
v.transform = CGAffineTransformScale(v.transform, scale, scale);
}
}
If anyone has any thoughts, I'd greatly appreciate it, as it has been a long day fighting with this.
Testing your scale calculation you can see there's something wrong in the first part of it. Imagine the following values:
scrollview center x = 10
scrollView contentOffset x = 30
v1 center x = 5
v2 center x = 15
v1 and v2 are mirrored (5 pixels to the left of the center and 5 to the right of the center), so they should return the same scale value.
If you do the calculation with your code, it returns 35 and 25, which means the calculation isn't right.
I'm not sure how do you want your scale to behave exactly but a suggestion would be:
float scale = 1. - ( (scrollView.contentOffset.x - abs( v.center.x - scrollView.center.x ) ) / scrollView.contentSize.width/2 );
Edit:
try this one:
float scale = 1. - (abs( v.center.x - scrollView.center.x ) ) / scrollView.contentSize.width/2 );
There are many ways of doing your calculation. My point is that you should make sure that your operations are giving you the expected results with fake values, and yours is not. Your dividend is giving wrong results.

3 interconnected sliders

I've been racking my brains over this problem for two days, I've tried different things but none of them work. I'm building an app which is a kind of quizz. There are three subjects which contain questions. I would like to use 3 sliders to define the percentage of questions they want on each subject.
ex : slider one = History
slider two = Maths
slider three = Grammar
If I choose to have more history, I slide the history slider up and the other sliders should decrease according to the values they have to reach 100% for the 3 sliders...
Any idea for an algorithm ? And what happens when one slider reach a zero value ?
Maths has never been my scene.
Any Help would be very much appreciated.
Thanks in advance.
Mike
Though Snowangelic's answer is good, I think it makes more sense to to constrain the ratio between the unchanged values as follows.
Let s1, s2, s3 be the current values of the sliders, so that s1+s2+s3=100. You want to solve for n1, n2, n3 the new values of the sliders, so that n1+n2+n3=100. Assume s1 is changed to n1 by the user. Then this adds the following constraint:
n2/n3 = s2/s3
So the solution to the problem, with n1+n2+n3=100, is
n2 = (100-n1)/(s3/s2 + 1) or 0 (if s2=0) and
n3 = (100-n1)/(s2/s3 + 1) or 0 (if s3=0)
Start all three sliders at 33.333...%
When the users moves a slider up say 10% : move the two other sliders down of 5%. But if one of two slider reaches 0 => only move the other one of ten percent. So it gives something like this :
User moved slider of x (my be positive or negative)
for first slider
if slider -x/2 > 0 and x/2 < 100
move this slider of -x/2
else
move the other slider of -x/2
for second slider
if slider -x/2 > 0 and x/2 < 100
move this slider of -x/2
else
move the other slider of -x/2
end
Another possibility would be to consider that the sum os the available ressources is 100, the ressources are separated into n buckets (in your case 3). When the user moves a slider, he fixes the number of ressources in the corresponding bucket. And so you may either take ressources from other bucket or put ressources in these other buckets.
You have something like :
state 1 ; modified bucket ; new number of ressources in that bucket
modification = new number of ressources in the bucket - number of rescources in the state 1
for (int i=0 ; modification > 0 ; i++){
i=i%nbr of buckets;
if(bucket i != modified bucket){
if(number of ressources in bucket i-- > 0 && number of ressources in bucket i-- < 100){
number of ressources in bucket i--;
modification --;
}
}
}
That is assuming the modification is positive (new number in the modified bucket is higher than before). This small algorithm would work with any number of buckets (sliders in your case).
The following algorithm should be reviewed and of course optimized. It is only something that I have put together and I've not tested it.
initialize each slider with a max and minimum value and set the inital value as desired, but respecting that x + y + z = 1.
[self.slider1 setMinimumValue:0.0];
[self.slider1 setMaximumValue:1.0];
[self.slider1 setValue:0.20];
[self.slider2 setMinimumValue:0.0];
[self.slider2 setMaximumValue:1.0];
[self.slider2 setValue:0.30];
[self.slider3 setMinimumValue:0.0];
[self.slider3 setMaximumValue:1.0];
[self.slider3 setValue:0.50];
Set the three slider to the same selector:
[self.slider1 addTarget:self action:#selector(valueChanged:) forControlEvents:UIControlEventValueChanged];
[self.slider2 addTarget:self action:#selector(valueChanged:) forControlEvents:UIControlEventValueChanged];
[self.slider3 addTarget:self action:#selector(valueChanged:) forControlEvents:UIControlEventValueChanged];
The selector should do something like that:
- (void)valueChanged:(UISlider *)slider {
UISlider *sliderX = nil;
UISlider *sliderY = nil;
UISlider *sliderZ = nil;
if (slider == self.slider1) {
sliderX = self.slider1;
sliderY = self.slider2;
sliderZ = self.slider3;
} else if (slider == self.slider2) {
sliderY = self.slider1;
sliderX = self.slider2;
sliderZ = self.slider3;
} else {
sliderY = self.slider1;
sliderZ = self.slider2;
sliderX = self.slider3;
}
float x = sliderX.value;
float y = sliderY.value;
float z = sliderZ.value;
// x + y + z = 1
// Get the amout x has changed
float oldX = 1 - y - z;
float difference = x - oldX;
float newY = y - difference / 2;
float newZ = z - difference / 2;
if (newY < 0) {
newZ += y + newY;
newY = 0;
}
if (newZ < 0) {
newY += z + newZ;
newZ = 0;
}
[sliderY setValue:newY animated:YES];
[sliderZ setValue:newZ animated:YES];
}
If there is something wrong with this code, please let me know, and I can fix it!

Matlab Adjusting coordinate

I have image which size was 600 * 600 and it was displayed on 800 * 800 pixel screen.
The x,y coordinate in which the user look on screen was recorded in an array:
x =[250,300,390,750,760];
y =[120,550,250,130,420];
In other program, I want to plot the x,y coordinate on the 600 * 600 image. The problem is that some of the x,y plot were out of the image (as shown on the picture below) since the coordinate was more that the maximum size of the image (600 * 600).
EDITED:
How to transform/adjust the coordinate of the bigger image (800*800) into the smaller image (600*600) so all x,y coordinate are inside the smaller image (600*600)?
Lets say for example, the coordinate of top left image of the 600*600 inside the image of the 800*800 image is e.g. x = -10, y = 3.
Thanks.
alt text http://img9.imageshack.us/img9/8836/e47184420f.jpg
To get the pixels in image coordinates, you need to know where the bottom left and top right corners of your image were placed on the screen. From that you can both calculate offset and zoom of the image.
%# define some parameters
imageSize = [600 600];
topLeftPixScreen = [200,100]; %# position of the top left image corner in screen pixels
bottomRightPixScreen = [800,750]; %# position of the bottom right image corner in screen pixels
%# transform coordinates
oldX =[250,300,390,750];
oldY =[120,550,250,130,420];
newX = (oldX - topLeftPixScreen(1))/(bottomRightPixScreen(1) - topLeftPixScreen(1) + 1);
newY = (oldY - topLeftPixScreen(2))/(bottomRightPixScreen(2) - topLeftPixScreen(2) + 1);
Having said that, I'd suggest using ginput to select the points with Matlab, since this function directly returns image pixels.
EDIT
If you only have the top left corner, you have to hope that there has not been any scaling - otherwise, there is no way you can transform the points.
With offset only, the above simplifies to
%# define some parameters
imageSize = [600 600];
topLeftPixScreen = [200,100]; %# position of the top left image corner in screen pixels
%# transform coordinates
oldX =[250,300,390,750];
oldY =[120,550,250,130,420];
newX = oldX - topLeftPixScreen(1);
newY = oldY - topLeftPixScreen(2);
It seems that just adjusting the coordinates by the ratio of the screen area and image size would do:
newX = x.*(600/800)
newY = y.*(600/800)