I'm trying to find a rational solution for a layout problem in iOS.
I need to load n images in a view with this layout:
-----------
| x x |
| x x |
| x x |
| x x |
| x x |
| x x |
-----------
Each image may have a different width, but the height has a maximum and each image is to be centered in it's column.
What is the suggested approach?
I'm thinking of putting views with 50% width side by side and then center the images in them.
Thanks!
This should get you started. It does not, however, take into consideration the ratio of the images.
CGRect bounds = [[UIScreen mainScreen] bounds];
CGFloat maxCol = [imageViewArray count] + ([imageViewArray count] % 2);
CGFloat width = (bounds.size.width/2) - 15;
CGFloat height = (bounds.size.height/maxCol) - 10;
// loop through imageViews
for(pos = 0; pos < [imageViewArray count]; pos++)
{
// calculate position within grid
CGFloat row = (pos/2);
CGFloat col = (pos%2);
// retrieves UIImageView and UIImage
imageView = [imageViewArray objectAtIndex:pos];
image = imageView.image;
// calculates image size
CGFloat scaledWidth = (height * image.size.width) / image.size.height;
// adjust size of image view
imageView.frame = CGRectMake(0, // temp value for x
0, // temp value for y
height, // width of image
scaledWidth); // height of image
// adjust position of image view
imageView.center = CGPointMake((bounds.size.width/3) + ((bounds.size.width/3) * col),
((height+10) * row) + (height+10)/2));
// add image view to super view
[rootView addSubView:imageView];
};
The above scaling assumes that the height of the grid location is always smaller than the width of the grid location.
If you will load a lot of images it will cause performance problems. Better way to do this - use UITableView with custom cells.
Looks like the same problem a text engine has when rendering glyphs. What you want to do is to first calculate the width for a row before you do anything else. This is done by looping from left to right, adding the sizes of the views until you get to the edge and can't add more. Then you know the width and the maximum height for that row, and simply center the views by adding an offset to the left equal to the margin that was left to the right divided by two. And then add an offset to the y coordinate by the maximum height for that row.
Then simply keep doing that for all views until they are all have a position.
Related
I have an animation that I'm trying to position in the center of the view. Here's my current code set at static values:
imageViewAnimatedFrame.origin.x = 10
imageViewAnimatedFrame.origin.y = 200
imageViewAnimatedFrame.size.height = 298
imageViewAnimatedFrame.size.width = 391
I'm pretty new to Swift. Is there a way to set the origin x and y to the center subtracted by half the width and height so it lays in the center?
Thanks!
You can do something like this,
let imageViewAnimatedFrame: UIImageView = UIImageView()
imageViewAnimatedFrame.frame = CGRectMake((self.view.frame.size.width / 2) - 159.5, (self.view.frame.size.height / 2) - 149, 391, 298)
Divide view's height and width by 2 and divide your imageview's height and width by 2. then subtract half width of imageview from half width of view as position x and same for position y consider height here.
this way your imageview always be in center.
Hope this will help :)
If you have an ImageView of these dimensions:
imageViewAnimatedFrame.size.height = 298
imageViewAnimatedFrame.size.width = 391
imageViewAnimatedFrame.frame = CGRectMake((self.view.frame.size.width -
imageViewAnimatedFrame.frame.size.width) / 2,
(self.view.frame.size.height -
imageViewAnimatedFrame.frame.size.height) / 2,
imageViewAnimatedFrame.frame.size.width, imageViewAnimatedFrame.frame.size.height);
I have a UIView that needs to be placed over a UIImage inside of a UIImageView at specific coordinates. The coordinates for the frame are referenced from the top left corner and have a specified width and height refrenced from the original image.
So, to make the frame, I am first getting the CGRect of the image using a category from the following post: UIImage size in UIImageView
I then get a scale factor to shrink the size of the frame by taking the original height, dividing it by the scaled height, and then dividing all of my values by that.
Lastly, I take the image CGRect and add the scaled position values of the frame to get my final CGRect for the view. However, the frame is always up and to the right of the desired location. Can anyone see what I'm doing wrong?
Here's the code (new is just a custom object with the correct frame parameters):
CGRect imageBounds = [self.imageView displayedImageBounds];
float scaleFactor = AppDelegate.usedImage.size.height / imageBounds.size.height;
new.height /= scaleFactor;
new.width /= scaleFactor;
new.positionX /= scaleFactor;
new.positionY /= scaleFactor;
UIView *faceRectView = [[UIView alloc] init];
faceRectView.tag = idx;
faceRectView.backgroundColor = [UIColor whiteColor];
faceRectView.frame = CGRectMake((imageBounds.origin.x + new.positionX), (imageBounds.origin.y + new.positionY), new.width, new.height);
[self.view addSubview:faceRectView];
CGPoint is a C structure that defines a point in a coordinate system. The origin of this coordinate system is at the top left on iOS and at the bottom left on OS X. In other words, the orientation of its vertical axis differs on iOS and OS X.
CGSize is another simple C structure that defines a width and a height value, and CGRect has an origin field, a CGPoint, and a size field, a CGSize. Together the origin and size fields define the position and size of a rectangle.
On iOS and OS X, an application has multiple coordinate systems. On iOS, for example, the application's window is positioned in the screen's coordinate system and every subview of the window is positioned in the window's coordinate system. In other words, the subviews of a view are always positioned in the view's coordinate system.
Take this example of a frame
and notice how it differs from the concept of bounds
CGGeometry Reference is a collection of structures, constants, and functions that make it easier to work with coordinates and rectangles. You may have run into code snippets similar to this:
CGPoint point = CGPointMake(self.view.frame.origin.x + self.view.frame.size.width, self.view.frame.origin.y + self.view.frame.size.height);
Not only is this snippet hard to read, it's also quite verbose. We can rewrite this code snippet using two convenient functions defined in the CGGeometry Reference.
CGRect frame = self.view.frame;
CGPoint point = CGPointMake(CGRectGetMaxX(frame), CGRectGetMaxY(frame));
To simplify the above code snippet, we store the view's frame in a variable named frame and use CGRectGetMaxX and CGRectGetMaxY. The names of the functions are self-explanatory.
The CGGeometry Reference defines functions to return the smallest and largest values for the x- and y-coordinates of a rectangle as well as the x- and y-coordinates that lie at the rectangle's center. Two other convenient getter functions are CGRectGetWidth and CGRectGetHeight.
Finally to conclude, check out the implementation of CGRectMake.
CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
{
CGRect rect;
rect.origin.x = x; rect.origin.y = y;
rect.size.width = width; rect.size.height = height;
return rect;
}
Can you add Like that
faceRectView.frame = CGRectMake((0.0), (0.0), new.width, new.height);
I'm making a game that doesn't use Autolayout, and have used constraints to scale everything and it worked perfectly. However I cant seem to figure this out, I'm using arc4random to randomly position X and Y coordinates of a UIButton on the 4.7 inch screen. When I try running it on the smaller screen it plots the UIButton off the screen at times. How can I scale arc4random up and down depending on screen size.
-(void)position{
Randompositionx = arc4random() %270;
Randompositionx = Randompositionx + 51;
Randompositiony = arc4random() %411;
Randompositiony = Randompositiony +163;
UIButton.center = CGPointMake(Randompositionx, Randompositiony);
}
You should be using arc4random_uniform rather than arc4random and the modulo operator. It gives numbers without "modulo bias".
You need to adapt the upper value of your random number based on screen size. T_77's answer was a step in the right direction, but still not right.
The code below assumes the button you want to move is called myButton.
It uses margin values of 20 all around. Change the values as desired.
EDIT: I updated it to use the height and width of the button in the position calculation. With the updated code the button should never be closer to any edge than the margin value, even if the button size changes. No magic numbers, either. It should adapt to the size of it's superview.
-(void)position
{
CGRect frame = myButton.superview.bounds;
CGFloat leftMargin = 20; //use whatever values you want for magins
CGFloat rightMargin = 20;
CGFloat topMargin = 20;
CGFloat bottomMargin = 20;
CGFloat randomX, randomY;
CGFloat xMax = frame.size.width-leftMargin-rightMargin-
button.bounds.size.width/2;
randomX = arc4random_uniform(xMax) + leftMargin;
CGFloat yMax = frame.size.height-topMargin-bottomMargin-
button.bounds.size.height/2;
randomY = arc4random_uniform(yMax) + topMargin;
myButton.center = CGPointMake(randomX, randomY);
}
Also note that if you're using auto layout you shouldn't move the button's position directly, and instead should have position constraints and modify their constants, then call layoutIfNeeded. Otherwise the first thing that causes your layout to change will cause your button to revert to it's previous position.
Try this
-(void)position{
CGRect frame = [self.view frame];
Randompositionx = arc4random() %270;
Randompositionx = Randompositionx + CGRectGetMinX(frame);
Randompositiony = arc4random() %411;
Randompositiony = Randompositiony +CGRectGetMinY(frame);
UIButton.center = CGPointMake(Randompositionx, Randompositiony);
}
Now you can position your UIButton within your view.
My StoryBoard is configured for iPhone4 resolution, and when running on iPhone 5 I'd like a UIView to get bigger (both height&width).
The problem right now is that the view is only getting higher and not wider. What should be the auto-resize configuration in order to achieve this?
You will probably need to use a subclass of UIView with the setFrame: method overridden to catch frame changes.
- (void)setFrame:(CGRect)frame
{
frame.size.width = frame.size.height; // Make the *width* always equal to the *height*. Logic could go here, etc.
[super setFrame:frame]
}
Reference the height of the screen and use some padding values to set your view frame. Below is code to set the frame of a UIView called yourShapeView:
// Get the frame of the screen
CGRect screenFrame = [[UIScreen mainScreen] bounds];
// Set padding from the top and bottom of the shape
CGFloat verticalPadding = 40.0f;
CGFloat horizontalPadding = 20.0f;
// Get the height/width values
CGFloat height = screenFrame.size.height - (verticalPadding * 2);
CGFloat width = screenFrame.size.width - (horizontalPadding * 2);
// Set the size of your shape based on the height and padding
yourShapeView.frame = CGRectMake(horizontalPadding, verticalPadding, width, height);
I am creating a UIImageView and adding it in a loop to my view, I set the initial frame to 0,0,1,47 and each passage of the loop I change the center of the image view to space them out.
I am always using 0 as the origin.y
The problem is the origin reference is in the centre of the image view, assuming we was in interface builder, this is equivalent to the image below.
How can I change the reference point in code ?
After reading these answers and your comments I'm not really sure what is your point.
With UIView you can set position by 2 ways:
center – It definitely says it is the center.
frame.origin – Top left corner, can't be set directly.
If you want the bottom left corner to be at x=300, y=300 you can just do this:
UIView *view = ...
CGRect frame = view.frame;
frame.origin.x = 300 - frame.size.width;
frame.origin.y = 300 - frame.size.height;
view.frame = frame;
But if you go one level deeper to magical world of CALayers (don' forget to import QuartzCore), you are more powerful.
CALayer has these:
position – You see, it don't explicitely says 'center', so it may not be center!
anchorPoint – CGPoint with values in range 0..1 (including) that specifies point inside the view. Default is x=0.5, y=0.5 which means 'center' (and -[UIView center] assumes this value). You may set it to any other value and the position property will be applied to that point.
Example time:
You have a view with size 100x100
view.layer.anchorPoint = CGPointMake(1, 1);
view.layer.position = CGPointMake(300, 300);
Top left corner of the view is at x=200, y=200 and its bottom right corner is at x=300, y=300.
Note: When you rotate the layer/view it will be rotated around the anchorPoint, that is the center by default.
Bu since you just ask HOW to do specific thing and not WHAT you want to achieve, I can't help you any further now.
The object's frame includes its position in its superview. You can change it with something like:
CGRect frame = self.imageView.frame;
frame.origin.y = 0.0f;
self.imageView.frame = frame;
If I am understanding you correctly, you need to set the frame of the image view you are interested in moving. This can be done in the simple case like this:
_theImageView.frame = CGRectMake(x, y, width, height);
Obviously you need to set x, y, width, and height yourself. Please also be aware that a view's frame is in reference to its parent view. So, if you have a view that is in the top left corner (x = 0, y = 0), and is 320 points wide and 400 points tall, and you set the frame of the image view to be (10, 50, 100, 50) and then add it as a subview of the previous view, it will sit at x = 10, y = 50 of the parent view's coordinate space, even though the bounds of the image view are x = 0, y = 0. Bounds are in reference to the view itself, frame is in reference to the parent.
So, in your scenario, your code might look something like the following:
CGRect currentFrame = _theImageView.frame;
currentFrame.origin.x = 0;
currentFrame.origin.y = 0;
_theImageView.frame = currentFrame;
[_parentView addSubview:_theImageView];
Alternatively, you can say:
CGRect currentFrame = _theImageView.frame;
_theImageView.frame = CGRectMake(0, 0, currentFrame.size.width, currentFrame.size.height);
[_parentView addSubview:_theImageView];
Either approach will set the image view to the top left of the parent you add it to.
I thought I would take a cut at this in Swift.
If one would like to set a views position on the screen by specifying the coordinates to an origin point in X and Y for that view, with a little math, we can figure out where the center of the view needs to be in order for the origin of the frame to be located as desired.
This extension uses the frame of the view to get the width and height.
The equation to calculate the new center is almost trivial. See the below extension :
extension CGRect {
// Created 12/16/2020 by Michael Kucinski for anyone to reuse as desired
func getCenterWhichPlacesFrameOriginAtSpecified_X_and_Y_Coordinates(x_Position: CGFloat, y_Position: CGFloat) -> CGPoint
{
// self is the CGRect
let widthDividedBy2 = self.width / 2
let heightDividedBy2 = self.height / 2
// Calculate where the center needs to be to place the origin at the specified x and y position
let desiredCenter_X = x_Position + widthDividedBy2
let desiredCenter_Y = y_Position + heightDividedBy2
let calculatedCenter : CGPoint = CGPoint(x: desiredCenter_X, y: desiredCenter_Y)
return calculatedCenter // Using this point as the center will place the origin at the specified X and Y coordinates
}
}
Usage as shown below to place the origin in the upper left corner area, 25 pixels in :
// Set the origin for this object at the values specified
maskChoosingSlider.center = maskChoosingSlider.frame.getCenterWhichPlacesFrameOriginAtSpecified_X_and_Y_Coordinates(x_Position: 25, y_Position: 25)
If you want to pass a CGPoint into the extension instead of X and Y coordinates, that's an easy change you can make on your own.