UIScrollView height to adjust when width is adjusted - iphone

I have a UIScrollView and I wanted the frame height to adjust proportionally when I adjust the width, is this possible? Basically I am talking about auto adjusting the frame height of the UIScrollView when I adjust the width of the UIScrollView? I have tried setting the autoResizingMask to UIViewAutoresizingFlexibleWidth and height, but this doesn't work

I don't think there's an automatic way to do it. but you can added a UIView+SCaleAddition to do this:
#interface UIView(RespectScale)
- (void)setWidthRespect:(float)w;
- (void)setHeightRespect:(float)h;
#end
#implement UIView(RespectScale)
- (void)setWidthRespect:(float)w
{
float scale = self.bounds.size.width / self.bounds.size.height;
float h = w/scale;
CGRect bounds = self.bounds
bounds.size.width = w;
bounds.size.height = h;
self.bounds = bounds;
}
- (void)setHeightRespect:(float)h
{
// write it your own
}
#end

You can do:
//get old proportions
CGFloat proportion = scrollView.frame.size.width / scrollView.frame.size.height;
CGRect frame = scrollView.frame;
frame.size.width = new_width;
frame.size.height = new_width * proportion;
scrollView.frame = frame;

Related

How to modify the position of UIImageViews subviews inside a UIScrollView?

I'm trying to create gaps between the UIImageViews, which are subviews added into a UIScrollView.
And I thought I could do the following by modifying the CGRect of the UIImageView.
CGRect frame = imageView.frame;
frame.origin.x = <some values>
frame.origin.y = 0
imageView.frame = frame;
By modifying the x values, I assumed it will help me with the position of the UIImageViews inside the UIScrollView.
However, I realized whichever values I set for the frame.origin.x does not really matter. As each UIImageView inside the UIScrollView will be positioned side by side without any empty space.
I think you're doing it in the wrong way. You can't edit view's position by
view.frame.origin.x = something;
You should assign a new frame, like this:
view.frame = CGRectMake(x, y, w, h);
In your case it will look like this:
CGRect frame = imageView.frame;
CGFloat frameX = frame.origin.x;
CGFloat frameY = frame.origin.y;
CGFloat frameW = frame.size.width;
CGFloat frameH = frame.size.height;
frameX = <some values>
frameY = 0;
imageView.frame = CGRectMake(frameX, frameY, frameW, frameH);

Restrirct Boundary 320 x 480, while scaling UIImage

I am working on Interior Decoration Application, we can add Sofa, Table, Chair Table Lamp on Camera screen then can scale UIImage with touch to zoom picture. But by zooming we don't want to increase UIImage Size greater then 320 x 480, I mean want to restrict in iphone Boundary.
any suggestion, I had implemented and tried, but couldn't get exact solution. I use to check on base of center but this approach is not working, want some thing like Edge detection may be that would be exact solution,
Already Thanks, looking forwards
here is some code that I am using to Resize my UIImageView
-(BOOL)isValidSizeForView:(UIView *)myView forSize:(CGSize)size
{
BOOL Decision = NO;
CGRect rect = myView.frame;
CGRect BoundRect = CGRectMake(0, 0, 320, 480);
float MinX = (BoundRect.origin.x+(myView.frame.size.width/2));
float MaxX = ((BoundRect.origin.x+BoundRect.size.width)-(myView.frame.size.width/2));
float MinY = (BoundRect.origin.y+(myView.frame.size.height/2));
float MaxY = ((BoundRect.origin.y+BoundRect.size.height)-(myView.frame.size.height/2));
if(rect.origin.x > MinX && rect.origin.x< MaxX && rect.origin.y> MinY && rect.origin.y<MaxY)
{
Decision = YES;
}
else
{
printf(":( no sorry \n");
}
return Decision;
}
You could get the maximum zoomed size for your image by doing something like this:
- (CGSize)zoomedSizeForImage:(UIImage *)image constrainedTo:(CGSize)maxSize {
CGFloat width = image.size.width;
CGFloat height = image.size.height;
if (width > maxSize.width) {
width = maxSize.width;
height = height * maxSize.width / width;
}
if (height > maxSize.height) {
width = width * maxSize.height / height;
height = maxSize.height;
}
return CGSizeMake(width, height);
}

How to center image in a uiimageview/uiscrollview (pre and post zooming)

I am trying to center an image (with respect to the iphone screen) such that when the view is loaded the image is center width/height... In my case my view is loaded at pixel points 0,0 of the image itself and if I zoom out the image appears in the top left corner as opposed to mid-center of the screen/view. Here is what I have so far:
UIImage *image = [UIImage imageNamed: #"t.bmp"];
self.view.backgroundColor = [UIColor blackColor];
self.mi.frame = CGRectMake(0, 0, image.size.width, image.size.height);
self.mi.contentMode = (UIViewContentModeCenter);
self.mi.backgroundColor = [UIColor blackColor];
[self.mi setImage:image];
/* Draw a line here!!
UIGraphicsBeginImageContext(self.mi.frame.size);
[self.mi.image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineWidth(context, 20.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0,0);
CGContextAddLineToPoint(context, 200, 200);
CGContextMoveToPoint(context, 200,0);
CGContextAddLineToPoint(context, 0, 200);
CGContextStrokePath(context);
self.mi.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
*/
self.expimg.maximumZoomScale = 2.0;
self.expimg.minimumZoomScale = 0.1;
self.expimg.clipsToBounds = YES;
self.expimg.delegate = self;
[self.expimg setContentSize:self.mi.frame.size];
[self.expimg addSubview: self.mi];
[self.view addSubview: self.expimg];
Thanks!
Edit: mi is a UIImageView and expimg is a UIScrollView (used interface builder). Also, viewForZoomingInScrollView is defined.
You should be able to do this by adjusting the position and size of the image and scroll view on initial load, and as zoom levels change. Make a method that is called on viewDidLoad, and again on the UIScrollViewDelegate method scrollViewDidZoom:.
Something like this should work (where imageView and scrollView are class attributes):
- (void)centerImage {
CGSize screenSize = CGSizeMake(320, 480);
CGSize imageSize = imageView.frame.size;
imageSize.width = imageSize.width * scrollView.zoomScale;
imageSize.height = imageSize.height * scrollView.zoomScale;
CGSize scrollSize = scrollView.frame.size;
CGFloat centerX;
CGFloat centerY;
CGFloat left;
CGFloat top;
if (imageSize.width > screenSize.width) {
scrollSize.width = imageSize.width;
centerX = imageSize.width/2;
left = 0;
} else {
scrollSize.width = screenSize.width;
centerX = screenSize.width/2;
left = centerX - imageSize.width/2;
}
if (imageSize.height > screenSize.height) {
scrollSize.height = imageSize.height;
centerY = imageSize.height/2;
top = 0;
} else {
scrollSize.height = screenSize.height;
centerY = screenSize.width/2;
top = centerY - imageSize.height/2;
}
CGRect scrollFrame = scrollView.frame;
scrollFrame.size = scrollSize;
scrollView.frame = scrollFrame;
CGRect imageFrame = imageView.frame;
imageFrame.origin = CGPointMake(left, top);
scrollView.contentOffset = CGPointMake(centerX-(imageSize.width/2), centerY-(imageSize.height/2));
}
I have not tested this, so some of my math could be wrong. Either way, it will hopefully give you a good idea of what to do.
Step 1: You create an image view property then init it at viewDidLoad and add to scrollView:
self.pictureImage = [[UIImageView alloc]initWithImage:self.picture];
[_scrollView addSubview:_pictureImage];
self.pictureImage.alpha = 0;
_scrollView.delegate = self;
Step 2: Create adjustZoomScale function to get minimum scale
- (CGFloat)adjustZoomScale{
CGFloat scale = self.scrollView.bounds.size.width / self.pictureImage.bounds.size.width;
CGFloat h = scale * self.pictureImage.bounds.size.height;
if (h > self.scrollView.bounds.size.height) {
scale = self.scrollView.bounds.size.height / self.pictureImage.bounds.size.height;
}
return scale;
}
Step 3: Because frame of scroll view will be updated at viewDidAppear so we need to adjust zoom scale here. And set alpha to make image appear smoother:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear: animated];
_scrollView.maximumZoomScale = 1.0;
_scrollView.minimumZoomScale = [self adjustZoomScale];
[_scrollView setZoomScale:_scrollView.minimumZoomScale];
self.pictureImage.alpha = 1;
}
Step 4: Implement viewForZoomingInScrollView (scroll delegate method) and return image view
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
return self.pictureImage;
}
Step 5: Implement scrollViewDidZoom (scroll delegate method) and adjust point for image view
- (void)scrollViewDidZoom: (UIScrollView*) scrollView {
CGSize boundsSize = scrollView.bounds.size;
CGRect contentsFrame = _pictureImage.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0;
} else {
contentsFrame.origin.x = 0.0;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0;
} else {
contentsFrame.origin.y = 0.0;
}
_pictureImage.frame = contentsFrame;
}
Thank shawhu at this post for point adjusting for image view while zooming
[scrollView zoomToRect:CGRectMake(x, y, width, height) animated:YES]; will work for you...
Where rect should have those center coordinates as origin.

UIView category for setting origins and sizes directly

I am a beginner of iPhone app programing.
I really don't like the way we have to set origins and sizes like:
UIView *view;
CGRect frame = view.frame;
frame.origin.x = 100;
view.frame = frame;
or:
UIView *view;
view.frame = CGRectMake(100, view.frame.origin.y, view.frame.size.width, view.frame.size.height);
so I created a category for UIView like:
#interface UIView (Origin)
-(void) setOriginX:(CGFloat)x;
-(void) setOriginY:(CGFloat)y;
-(void) setOriginX:(CGFloat)x y:(CGFloat)y;
-(void) setWidth:(CGFloat)w;
-(void) setHeight:(CGFloat)h;
-(void) setWidth:(CGFloat)w height:(CGFloat)h;
#end
#implementation UIView(Origin)
-(void) setOriginX:(CGFloat)x {
self.frame = CGRectMake(x, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
}
...
#end
then I could write:
UIView *view;
[view setOriginX 100];
this is much convenient for me, but is there any concern that i shouldn't do such thing, or any easier way to set origins/sizes directly?
I find a few shortcuts for setting frame defined in three20 really convenient for positioning and resizing views, like:
// frame.origin.x
x
// frame.origin.y
y
//frame.origin
origin
// center
center
// frame.size
size
// center.x
centerX
// center.y
centerY
// frame.size.width
width
// frame.size.height
height
// x
left
// x + width
right
// y
top
// y + height
bottom
They all have setters and getters defined.

Alignment UIImageView with Aspect Fit

for my UIImageView I choose Aspect Fit (InterfaceBuilder) but how can I change the vertical alignment?
[EDIT - this code is a bit moldy being from 2011 and all but I incorporated #ArtOfWarefare's mods]
You can't do this w/ UIImageView. I created a simple UIView subclass MyImageView that contains a UIImageView. Code below.
// MyImageView.h
#import <UIKit/UIKit.h>
#interface MyImageView : UIView {
UIImageView *_imageView;
}
#property (nonatomic, assign) UIImage *image;
#end
and
// MyImageView.m
#import "MyImageView.h"
#implementation MyImageView
#dynamic image;
- (id)initWithCoder:(NSCoder*)coder
{
self = [super initWithCoder:coder];
if (self) {
self.clipsToBounds = YES;
_imageView = [[UIImageView alloc] initWithFrame:self.bounds];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
[self addSubview:_imageView];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.clipsToBounds = YES;
_imageView = [[UIImageView alloc] initWithFrame:self.bounds];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
[self addSubview:_imageView];
}
return self;
}
- (id)initWithImage:(UIImage *)anImage
{
self = [self initWithFrame:CGRectZero];
if (self) {
_imageView.image = anImage;
[_imageView sizeToFit];
// initialize frame to be same size as imageView
self.frame = _imageView.bounds;
}
return self;
}
// Delete this function if you're using ARC
- (void)dealloc
{
[_imageView release];
[super dealloc];
}
- (UIImage *)image
{
return _imageView.image;
}
- (void)setImage:(UIImage *)anImage
{
_imageView.image = anImage;
[self setNeedsLayout];
}
- (void)layoutSubviews
{
if (!self.image) return;
// compute scale factor for imageView
CGFloat widthScaleFactor = CGRectGetWidth(self.bounds) / self.image.size.width;
CGFloat heightScaleFactor = CGRectGetHeight(self.bounds) / self.image.size.height;
CGFloat imageViewXOrigin = 0;
CGFloat imageViewYOrigin = 0;
CGFloat imageViewWidth;
CGFloat imageViewHeight;
// if image is narrow and tall, scale to width and align vertically to the top
if (widthScaleFactor > heightScaleFactor) {
imageViewWidth = self.image.size.width * widthScaleFactor;
imageViewHeight = self.image.size.height * widthScaleFactor;
}
// else if image is wide and short, scale to height and align horizontally centered
else {
imageViewWidth = self.image.size.width * heightScaleFactor;
imageViewHeight = self.image.size.height * heightScaleFactor;
imageViewXOrigin = - (imageViewWidth - CGRectGetWidth(self.bounds))/2;
}
_imageView.frame = CGRectMake(imageViewXOrigin,
imageViewYOrigin,
imageViewWidth,
imageViewHeight);
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
[self setNeedsLayout];
}
#end
If using Storyboards this can be achieved with constraints...
Firstly a UIView with the desired final frame / constraints. Add a UIImageView to the UIView. Set the contentMode to Aspect Fill. Make the UIImageView frame be the same ratio as the image (this avoids any Storyboard warnings later). Pin the sides to the UIView using standard constraints. Pin the top OR bottom (depending where you want it aligned) to the UIView using standard constraints. Finally add an aspect ratio constraint to the UIImageView (making sure ratio as the image).
This is a bit tricky one since there is no option to set further alignment rules if you already selected a content mode (.scaleAspectFit).
But here's a workaround to this:
First need to resize your source image explicitly by calculating dimensions (if it'd be in a UIImageView with contentMode = .scaleAspectFit).
extension UIImage {
func aspectFitImage(inRect rect: CGRect) -> UIImage? {
let width = self.size.width
let height = self.size.height
let aspectWidth = rect.width / width
let aspectHeight = rect.height / height
let scaleFactor = aspectWidth > aspectHeight ? rect.size.height / height : rect.size.width / width
UIGraphicsBeginImageContextWithOptions(CGSize(width: width * scaleFactor, height: height * scaleFactor), false, 0.0)
self.draw(in: CGRect(x: 0.0, y: 0.0, width: width * scaleFactor, height: height * scaleFactor))
defer {
UIGraphicsEndImageContext()
}
return UIGraphicsGetImageFromCurrentImageContext()
}
}
Then you simply need to call this function on your original image by passing your imageView's frame and assign the result to your UIImageView.image property. Also, make sure you set your imageView's desired contentMode here (or even in the Interface Builder)!
let image = UIImage(named: "MySourceImage")
imageView.image = image?.aspectFitImage(inRect: imageView.frame)
imageView.contentMode = .left
Try setting:
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.clipsToBounds = YES;
This worked for me.
I used UIImageViewAligned for changing the alignment of image thanks to the developer
UIImageViewAligned
I know this is an old thread, but I thought I'd share what I did to easily change the clipped region from the top to the bottom of the image view in Interface Builder, in case anyone had the same problem I did. I had a UIImageView that filled the View of my ViewController, and was trying to make the top stay the same, independent of the size of the device's screen.
I applied the retina 4 form factor (Editor->Apply Retina 4 Form Factor).
I pinned the height and width.
Now, when the screen changes size, the UIImageView is actually the same size, and the view controller just clips what is off the screen. The frame origin stays at 0,0, so the bottom and right of the image are clipped, not the top.
Hope this helps.
You can do it by first scaling and then resizing.
The thing to mention here is that I was conditioned by height. I mean , I had to have the image of 34px high and no matter how width.
So , get the ratio between the actual content height and the height of the view ( 34 px ) and then scale the width too.
Here's how I did it:
CGSize size = [imageView sizeThatFits:imageView.frame.size];
CGSize actualSize;
actualSize.height = imageView.frame.size.height;
actualSize.width = size.width / (1.0 * (size.height / imageView.frame.size.height));
CGRect frame = imageView.frame;
frame.size = actualSize;
[imageView setFrame:frame];
Hope this helps.
I came up to the following solution:
Set UIImageView content mode to top: imageView.contentMode = .top
Resize image to fit UIImageView bounds
To load and resize image I use Kingfisher:
let size = imageView.bounds.size
let processor = ResizingImageProcessor(referenceSize: size, mode: .aspectFit)
imageView.kf.setImage(with: URL(string: imageUrl), options: [.processor(processor), .scaleFactor(UIScreen.main.scale)])
If you need to achieve aspectFit and get rid empty spaces
Dont forget to remove width constraint of your imageview from storyboard and enjoy
class SelfSizedImageView :UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
guard let imageSize = image?.size else {
return
}
let viewBounds = bounds
let imageFactor = imageSize.width / imageSize.height
let newWidth = viewBounds.height * imageFactor
let myWidthConstraint = self.constraints.first(where: { $0.firstAttribute == .width })
myWidthConstraint?.constant = min(newWidth, UIScreen.main.bounds.width / 3)
layoutIfNeeded()
}}
I solved this by subclassing UIImageView and overriding the setImage: method. The subclass would first store it's original values for origin and size so it could use the original set size as a bounding box.
I set the content mode to UIViewContentModeAspectFit. Inside of setImage: I grabbed the image width to height ratio and then resized the image view to fit the same ratio as the image. After the resize, I adjusted my frame properties to set the image view on the same spot it was before, and then I called the super setImage:.
This results in an image view who's frame is adjusted to fit the image exactly, so aspect fit works and the image view frame properties are doing the heavy lifting in putting the image view where it should be to get the same effect.
Here's some code that I used:
First up, and I find it pretty useful in general, is a category on UIView that makes it easy to set frame properties on a view via properties like left, right, top, bottom, width, height, etc.
UIImageView+FrameAdditions
#interface UIView (FrameAdditions)
#property CGFloat left, right, top, bottom, width, height;
#property CGPoint origin;
#end
#implementation UIView (FrameAdditions)
- (CGFloat)left {
return self.frame.origin.x;
}
- (void)setLeft:(CGFloat)left {
self.frame = CGRectMake(left, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
}
- (CGFloat)right {
return self.frame.origin.x + self.frame.size.width;
}
- (void)setRight:(CGFloat)right {
self.frame = CGRectMake(right - self.frame.size.width, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
}
- (CGFloat)top {
return self.frame.origin.y;
}
- (void)setTop:(CGFloat)top {
self.frame = CGRectMake(self.frame.origin.x, top, self.frame.size.width, self.frame.size.height);
}
- (CGFloat)bottom {
return self.frame.origin.y + self.frame.size.height;
}
- (void)setBottom:(CGFloat)bottom {
self.frame = CGRectMake(self.frame.origin.x, bottom - self.frame.size.height, self.frame.size.width, self.frame.size.height);
}
- (CGFloat)width {
return self.frame.size.width;
}
- (void)setWidth:(CGFloat)width {
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, width, self.frame.size.height);
}
- (CGFloat)height {
return self.frame.size.height;
}
- (void)setHeight:(CGFloat)height {
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, height);
}
- (CGPoint)origin {
return self.frame.origin;
}
- (void)setOrigin:(CGPoint)origin {
self.frame = CGRectMake(origin.x, origin.y, self.frame.size.width, self.frame.size.height);
}
#end
This is the subclass of UIImageView. It is not fully tested, but should get the idea across. This could be expanded to set your own new modes for alignment.
BottomCenteredImageView
#interface BottomCenteredImageView : UIImageView
#end
#interface BottomCenteredImageView() {
CGFloat originalLeft;
CGFloat originalBottom;
CGFloat originalHeight;
CGFloat originalWidth;
}
#end
#implementation BottomCenteredImageView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if(self) {
[self initialize];
}
return self;
}
- (void)awakeFromNib {
[self initialize];
}
- (void)initialize {
originalLeft = self.frame.origin.x;
originalHeight = CGRectGetHeight(self.frame);
originalWidth = CGRectGetWidth(self.frame);
originalBottom = self.frame.origin.y + originalHeight;
}
- (void)setImage:(UIImage *)image {
if(image) {
self.width = originalWidth;
self.height = originalHeight;
self.left = originalLeft;
self.bottom = originalBottom;
float myWidthToHeightRatio = originalWidth/originalHeight;
float imageWidthToHeightRatio = image.size.width/image.size.height;
if(myWidthToHeightRatio >= imageWidthToHeightRatio) {
// Calculate my new width
CGFloat newWidth = self.height * imageWidthToHeightRatio;
self.width = newWidth;
self.left = originalLeft + (originalWidth - self.width)/2;
self.bottom = originalBottom;
} else {
// Calculate my new height
CGFloat newHeight = self.width / imageWidthToHeightRatio;
self.height = newHeight;
self.bottom = originalBottom;
}
self.contentMode = UIViewContentModeScaleAspectFit;
[super setImage:image];
} else {
[super setImage:image];
}
}
#end
Credit to #michael-platt
Key Points
imageView.contentMode = .scaleAspectFit
let width = Layout.height * image.size.width / image.size.height
imageView.widthAnchor.constraint(equalToConstant: width).isActive = true
Code:
func setupImageView(for image: UIImage) {
let imageView = UIImageView()
imageView.backgroundColor = .orange
//Content mode
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
//Constraints
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
//Feel free to set the height to any value
imageView.heightAnchor.constraint(equalToConstant: 150).isActive = true
//ImageView width calculation
let width = Layout.height * image.size.width / image.size.height
imageView.widthAnchor.constraint(equalToConstant: width).isActive = true
}
To align a scale-to-fit image use auto layout. Example right aligned image after scale-to-fit in UIImageView:
Create UIImageView that holds the image
Add auto constraints for right, top, bottom, width
Set the image:
myUIImageView.contentMode = .ScaleAspectFit
myUIImageView.translatesAutoresizingMaskIntoConstraints = false
myUIImageView.image = UIImage(named:"pizza.png")
Your aspect fit scaled image is now right aligned in the UIImageView
+----------------------------------+
| [IMAGE]|
+----------------------------------+
Change the constraints to align differently within the imageview.