Calculating Visible CGRect to crop a UIImageView [duplicate] - ios

This question already has answers here:
How to crop a UIImageView to a new UIImage in 'aspect fill' mode?
(2 answers)
Closed 4 years ago.
I have a UIView. There is a UIImageView inside that UIView. UIImageView doesn't comply to autolayout constraints. Parent UIView's bounds are clipped.
Now content mode of UIImageView is Aspect Fill, but the size of UIImageView is always calculated and set to size of UIImage that is being rendered. So UIImageView's size will always be equal to size of UIImage.
Now UIImageView can be positioned. It has a pan gesture applied to it. UIImageView is being moved within the parent UIView based on requirement of user.
My task is to crop the UIImage of UIImageView to the visible viewport of parent UIVIew only. Core Cropping the image is not issue but finding the visible rect is.
My solution which is not working was to convert frame of parent UIView and child UIImageView to window screen. Then find the intersection of new converted frames. But this is not working. There should be some simple mathematics behind this which I am not able to come up successfully.
This is my code so far.
CGRect imgFrame = [_pageImageView convertRect:_pageImageView.frame toView:nil];
CGRect viewportFrame = [_viewportView convertRect: _viewportView.frame toView:nil];
CGRect visibleRect = CGRectIntersection(imgFrame, viewportFrame);
CGImageRef imageRef = CGImageCreateWithImageInRect([_pageImageView.image CGImage], visibleRect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
My objective is to find the visible rect that is visible through clipped parent UIVIew, then crop the UIImageView based on that visible rect. Important note: UIImageView can be positioned and moved within parent UIView.
EDIT: I can't use UIScrollView for cropping as used in many other discussions for my internal reasons.

You can place the image view inside the UIScrollView instead of UIView which make the pan gesture work easier.
I have the scrollview have the UIImageView
- (CGRect)imageCropRect
{
CGSize imageSize = self.image.size;
CGSize contentSize = self.contentSize;
CGRect cropBoxFrame = self.frame;
CGPoint contentOffset = self.contentOffset;
UIEdgeInsets edgeInsets = self.contentInset;
CGRect frame = CGRectZero;
frame.origin.x = floor((contentOffset.x + edgeInsets.left) * (imageSize.width / contentSize.width));
frame.origin.x = MAX(0, frame.origin.x);
frame.origin.y = floor((contentOffset.y + edgeInsets.top) * (imageSize.height / contentSize.height));
frame.origin.y = MAX(0, frame.origin.y);
frame.size.width = ceil(cropBoxFrame.size.width * (imageSize.width / contentSize.width));
frame.size.width = MIN(imageSize.width, frame.size.width);
frame.size.height = ceil(cropBoxFrame.size.height * (imageSize.height / contentSize.height));
frame.size.height = MIN(imageSize.height, frame.size.height);
return frame;
}

Related

How to align an image inside a imageView

I wanted to know how to align an image to the right while keeping the aspect fill. So this is how my image view looks right now.
I would like to move the image to the left, so that the image looks like this.
Now I tried aligning it to the right, but the image is so big that it only shows her gun. So I was wondering how would you be able to do this. Would I have to use a ScrollView? Would appreciate the help, Thanks.
I am not sure if this works for you or not:
Try the different contentModes:
Use it like:
imgView.contentMode = .scaleAspectFit //Or any from below options
You can Use StoryBoard also:
Try different types which suites your useCase
Hope this helps.
Set Content Mode of image Which is best for you Image View.
you could resize imageview as per the image size, below code is not tested:
CGSize kMaxImageViewSize = {.width = 100, .height = 100};
CGSize imageSize = image.size;
CGFloat aspectRatio = imageSize.width / imageSize.height;
CGRect frame = imageView.frame;
if (kMaxImageViewSize.width / aspectRatio <= kMaxImageViewSize.height)
{
frame.size.width = kMaxImageViewSize.width;
frame.size.height = frame.size.width / aspectRatio;
}
else
{
frame.size.height = kMaxImageViewSize.height;
frame.size.width = frame.size.height * aspectRatio;
}
imageView.frame = frame;

Autolayout with dynamic height

I am trying to create a subclass of UIView that will show a list of fixed sized UIImages similar to how a UILabel displays letters.If all the images won't fit on one line, the images are arranged on multiple lines.
How can I achieve this using autolayouts so that if I put this view in a UIStackView the images will be listed correctly?
Here is a sample if I did it using fixed position :
- (void) layoutSubviews{
[super layoutSubviews];
CGRect bounds = self.bounds;
CGRect imageRect = CGRectMake(0, 0, 30, 30);
for (UIImageView* imageView in self.imageViews){
imageView.frame = imageRect;
imageRect.origin.x = CGRectGetMaxX(imageRect);
if (CGRectGetMaxX(imageRect) > CGRectGetMaxX(bounds)){
imageRect.origin.x = 0.0;
imageRect.origin.y = CGRectGetMaxY(imageRect);
}
}
}
Update:
Here is a sample project to show the issue.
https://github.com/datinc/DATDemoImageListView
Here the link for ImageListView
https://github.com/datinc/DATDemoImageListView/blob/master/DATDemoImageListView/DATDemoImageListView.m
You should overwrite intrinsicContentSize returning the preferred content size of your view.
The problem is that in intrinsicContentSize the view has not it's final bounds. You can use an internal height constraint, and overwrite updateConstraints:
- (void)updateConstraints {
CGFloat theWidth = CGRectGetWidth(self.bounds);
NSUInteger theCount = [self.subviews count];
CGFloat theRows = ceilf(theCount / floorf(theWidth / 30.0));
self.heightConstraint.constant = 30.0 * theRows;
[super updateConstraints];
}
In viewDidAppear: and on layout changes (e. g. rotation) you have to call setNeedsUpdateConstraints to get a proper initial layout of the image views.

changing just the height of the UIImageView

how can i set the height of UIImageView and leaving other attributes as it is from the .xib? i have UIImageView in .xib file but i only want to set height of it programmatically. can it be done?
Yes It can be done. For eg: If you have UIImageView as imageView then do the below to change the specific frame value.
Way - 1
CGRect rect = imageView.frame;
rect.size.height = /* YOUR_HEIGHT */;
imageView.frame = rect;
Way - 2
imageView.frame = CGRectMake(imageView.frame.origin.x, imageView.frame.origin.y, imageView.frame.size.width, /* YOUR_HEIGHT */);
I highly recommend you to add https://github.com/AlexDenisov/FrameAccessor to your project. It allows to work with frame directly, for example:
view.x = 15.;
view.width = 167.;

UIScrollView and UIImageView - move horizontally

I have UIScrollView with UIImageView on it. I set image in code and it can have different width (height is always the same). I want to do it scrollable (eg. see only part of it and drag it to left / right to see the rest).
I have this code performed after image change:
float x = self.imageView.frame.origin.x;
float y = self.imageView.frame.origin.y;
float w = scaleImg.size.width;
float h = scaleImg.size.height;
self.imageView.frame = CGRectMake(x, y, w, h);
self.imageView.bounds = CGRectMake(0, 0, w, h);
w = self.frame.size.width;
h = self.scrollView.frame.size.height;
[self.scrollView setContentSize: CGSizeMake(w + 20, h)];
Problem is, that it didn't scroll correctly (I think it didn't scroll at all). And after I scroll, my image is resized, not respecting the frame I have set above.
I am using iOS7, storyboard, autolayout.
If you want the image to fit the scrollview and be scrollable left and right only you should create the image view with height same as the scrollview and width scaled accordingly to the image. Try something like this:
UIImage *image;
UIScrollView *scrollView;
UIImageView *imageView;
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(.0f,
.0f,
scrollView.frame.size.height*image.size.width/image.size.height,
scrollView.frame.size.height)];//fixed height
imageView.image = image;
imageView.contentMode = UIViewContentModeScaleToFill;
[scrollView addSubview:imageView];
scrollView.contentSize = imageView.frame.size;
scrollView.contentOffset = CGPointMake((scrollView.contentSize.width-scrollView.frame.size.width)*.5f, .0f);//scroll to center
Note I designed this only for images which have proportionally larger width then scrollview. If that is not always the case you should create a specific logic for other images (either background is visible or it is scrollable in Y coordinate).
I guess you should remove the following lines:
w = self.frame.size.width;
h = self.scrollView.frame.size.height;
scrollView's content size should reflect size of enclosed content, i.e. it should be scaleImg.size.width & scaleImg.size.height.
you given the w as the width for both scrollview and imageview....content size should be more than scroll view.Try giving imageview width more than scrollview.

UIScrollView zooms subviews when increasing subview dimensions

I have a UIScrollView that contains a view derived from UIView that uses CATiledLayer. Essentially, in my ViewController viewDidLoad:
_tiledView = [[TiledView alloc] initWithFrame:rect tileSize:_tileSize];
_scrollView = [[ScrollingView alloc] initWithFrame:rect];
_scrollView.contentSize = _tiledView.frame.size;
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_scrollView.decelerationRate = UIScrollViewDecelerationRateFast;
_scrollView.scrollEnabled = YES;
_scrollView.delegate = self;
[_scrollView addSubview:_tiledView];
Initially, _tiledView is a 4x4 grid of 256x256 tiles. I'm trying increase the dimensions of _tiledView at runtime. When constructing _tiledView, I simply compute the size of the view by multiplying the number of tiles by its size. Then I set the size of _tiledView.frame and _tiledView.bounds, e.g.:
CGRect frame = self.frame;
frame.origin = CGPointZero;
frame.size = CGSizeMake(tileSize.width*4, tileSize.height*4);
self.frame = frame;
self.bounds = frame;
Now, when I hit a button in the interface, all I want to accomplish as a first step is increasing the dimensions of _tiledView by one 256x256 tile to both right and bottom. This is what I attempted:
- (void)addTiles:(id)sender
{
CGRect rect = _tiledView.frame;
rect.size.width += _tileSize.width;
rect.size.height += _tileSize.height;
_tiledView.frame = rect;
_tiledView.bounds = rect;
CGSize size = _scrollView.contentSize;
size.width += _tileSize.width;
size.height += _tileSize.height;
_scrollView.contentSize = size;
[_scrollView setNeedsLayout];
}
However, this doesn't work as expected. What happens is that _tiledView gets bigger as if it had been zoomed in - same number of tiles as the beginning though, i.e. 4x4. I checked the _scrollView.contentsScaleFactor property and it says 1.0. I assume _scrollView did not technically zoom the contents in.
I was expecting _tileView to stay put in its current place in the interface but add 9 new tiles, i.e. 4 to the right, 4 at the bottom and 1 at the bottom-right corner. Instead, the initial 16 tiles got bigger to fill in the space that could have been filled by 25 tiles.
What am I missing? What am I doing wrong? Any help would be appreciated.
In case anyone finds it useful. After digging further, I realized that I the contentMode defaults to ScaleToFill. So I set it to:
_tiledView.contentMode = UIViewContentModeRedraw;
upon initialization. And adjusted addTiles like this:
CGRect rect = _tiledView.frame;
rect.size.width += _tileSize.width;
rect.size.height += _tileSize.height;
_tiledView.frame = rect;
_tiledView.bounds = rect;
_scrollView.contentSize = rect.size;
[_tiledView setNeedsDisplay];
And, that had the effect I was looking for.

Resources