UIScrollView zooms subviews when increasing subview dimensions - ios

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.

Related

Changing a views bounds affect the frame

I'm trying to understand how a view responds to its bounds being changed. If I change the bounds origin of a view, will it change the frame origin accordingly?
E.g.
UIView *greenView = [[UIView alloc] initWithFrame:CGRectMake(150, 150, 150, 200)];
greenView.backgroundColor = [UIColor colorWithRed:0.494 green:0.827
blue:0.129 alpha:1];
[self.view addSubview:greenView];
greenView.bounds = CGRectMake(0, 150, greenView.bounds.size.width, greenView.bounds.size.height);
Would this not change the frame's origin to (150, 300)? Running the code above doesn't seem to change its frame. (I know you're not meant to change a views position using bounds, this is just a hypothetical).
Per Apple Documentation, here are the relationship between a view's frame, bounds and centre:
Although you can change the frame, bounds, and center properties
independent of the others, changes to one property affect the others
in the following ways:
When you set the frame property, the size value in the bounds property changes to match the new size of the frame rectangle. The
value in the center property similarly changes to match the new
center point of the frame rectangle.
When you set the center property, the origin value in the frame changes accordingly.
When you set the size of the bounds property, the size value in the frame property changes to match the new size of the bounds rectangle.
So, answer to your question, changing X,Y position on View's bounds should not affect frame. Most of the cases bounds starts with (0,0). Changing the height or width to negative values would allow origin of bounds to go negative.
EDIT: To answer OP question - No, changing the position of bounds won't affect frame in any way. Since bounds is with reference to view's own co-ordinate system, changing X,Y on self co-ordinate system would not change position in superview's co-ordinate system.
You can try by using two custom views like this:
UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(50.0f, 100.0f, 150.0f, 150.0f)];
view1.backgroundColor = [UIColor redColor];
NSLog(#"view1.bounds = %#", NSStringFromCGRect(view1.bounds));
NSLog(#"view1.frame = %#", NSStringFromCGRect(view1.frame));
UIView* view2 = [[UIView alloc] initWithFrame:CGRectInset(view1.bounds, 20.0f, 20.0f)];
view2.backgroundColor = [UIColor yellowColor];
NSLog(#"view2.bounds = %#", NSStringFromCGRect(view2.bounds));
NSLog(#"view2.frame = %#", NSStringFromCGRect(view2.frame));
NSLog(#"view1.bounds = %#", NSStringFromCGRect(view1.bounds));
NSLog(#"view1.frame = %#", NSStringFromCGRect(view1.frame));
NSLog(#"view2.bounds = %#", NSStringFromCGRect(view2.bounds));
NSLog(#"view2.frame = %#", NSStringFromCGRect(view2.frame));
[view1 addSubview:view2];
And then change the subview bound like this:
CGRect frame = view2.bounds;
frame.origin.x += 20.0f;
frame.origin.y += 20.0f;
view2.bounds = frame;
Changing the bounds would not impact frame at all. And both the views would look same on screen:
And finally, try by changing the bounds of parent view to see below screen:
CGRect frame = view1.bounds;
frame.origin.x += 20.0f;
frame.origin.y += 20.0f;
view1.bounds = frame;

How can I resize a UICollectionViewCell along one axis without distorting its contents?

I am trying to resize a UICollectionViewCell along only one axis without distorting its contents.
I have read answers for similar problems involving UICollectionViewLayoutAttributes layoutAttributesForItemAtIndexPath and layoutAttributesForElementsInRect. I have also tried using initWithFrame and awakeFromNib in a UICollectionViewCell subclass. I have attempted to set UIViewContentMode to UIViewContentModeScaleAspectFit or UIViewContentModeScaleAspectFill in those methods, but that has had no effect.
So far, I still get distorted text when I stretch along only one axis.
So...
I have a UICollectionView with its cells defined in a xib. Currently the cell only contains a UILabel. The collection view uses UICollectionViewFlowLayout to do some minor customizations:
self.flowLayout = [[UICollectionViewFlowLayout alloc] init];
[self.flowLayout setMinimumLineSpacing:1.0f];
[self.flowLayout setItemSize:CGSizeMake(self.cellSize.width, self.cellSize.height)];
[self.flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[self.collectionView setCollectionViewLayout:self.flowLayout];
The UICollectionView is embedded inside a UIScrollView, and fits exactly inside the scroll view's dimensions. The scroll view size is set to be much larger than the screen in both directions. Hard-coded numbers are just for testing; I will turn them into appropriate constants later.
self.cellSize = CGSizeMake(150.0f, 150.0f);
CGSize scrollViewSize = self.scrollView.contentSize;
scrollViewSize.width = 20 * self.cellSize.width + 20.0f;
scrollViewSize.height = 20 * self.cellSize.height + 20.0f;
self.scrollView.contentSize = scrollViewSize;
self.collectionViewHeightConstraint.constant = self.scrollView.contentSize.height;
self.collectionViewWidthConstraint.constant = self.scrollView.contentSize.width;
The result is a grid which is scrollable in any direction. That part is working.
The grid needs to be stretchable, either proportionally or along only one axis at a time. I am using the following pinch gesture recognizer to make one-axis zooming happen (again, the hard-coded 1.0 value for y is just for testing):
- (IBAction)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, 1.0);
recognizer.scale = 1;
}
When I resize the collection view with a pinch gesture, the label in each cell stretches and deforms. Here's a screen grab:
(source: afterburnerimages.com)
I will supply any other pertinent information gladly, and all assistance is much appreciated!
Oky u are using scrollview which contains collection view then u can use scrollview delegate for zoom, in your case it is stretches, better u can use like below
for example,
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_scrollView = [[UIScrollView alloc]initWithFrame:self.view.bounds];
_scrollView.delegate = self;
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setMinimumLineSpacing:1.0f];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[self.collectionVIew setCollectionViewLayout:flowLayout];
[self.collectionVIew registerClass:[CustomCollectionVIewCell class] forCellWithReuseIdentifier:#"CELL_ID"];
//setting up content size for scroll view
CGSize size = self.scrollView.contentSize;
size.width = 200 * 10;
size.height = 185 * 10;
self.scrollView.contentSize = size;
CGRect rect = CGRectZero;
rect.origin = self.scrollView.bounds.origin;
rect.size = size;
self.collectionVIew.frame = rect;
self.scrollView.backgroundColor = [UIColor greenColor];
[self.scrollView addSubview:self.collectionVIew];
[self.view addSubview:self.scrollView];
// CGRect scrollViewFrame = self.scrollView.frame;
// CGFloat scaleWidth = _collectionVIew.frame.size.width / self.scrollView.contentSize.width;
// CGFloat scaleHeight = _collectionVIew.frame.size.height / self.scrollView.contentSize.height;
// CGFloat minScale = MIN(scaleWidth, scaleHeight);
//set min scale must be less than max zoom scale so that it can be zoomable
self.scrollView.minimumZoomScale = 0.5;
self.scrollView.maximumZoomScale = 1.0f;
// self.scrollView.zoomScale = minScale; //default is 1.0f;
}
and use scroll view delegates to perform zoom
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.collectionVIew; //return collection view which is embedded inside scroll view
}
and result is something like gif below stretching proportionally without effecting the contents of collection view , hope this helps u .. :)

Set proper gap between text.view frame and button frame

I have a text view and buttons. For some reason i got unexpected result when trying to set proper gap between those 2 elements. I think there could be incorrect calculating of text.view frame. That is how i manage to set text.view frame:
//1 Setting properties for text, starting with dictionary containing font size and family
NSDictionary *stringAttributes = [NSDictionary dictionaryWithObject:[UIFont fontWithName:#"helvetica neue"
size:14]
forKey: NSFontAttributeName];
//Creating frame to hold textView
CGSize maximumLabelSize = CGSizeMake(self.myTextView.frame.size.width, FLT_MAX);
CGSize textViewSize = [self.descriptionStringShort boundingRectWithSize:maximumLabelSize
options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin
attributes:stringAttributes context:nil].size;
//Setting frame
CGRect frame = self.myTextView.frame;
frame.size.height = textViewSize.height;
NSLog(#"%f height", frame.size.height);
self.myTextView.frame = frame;
After that, when tried to set button frames it always show different gap between those to elements (my "rough" solutions is to check height of text.view and set "minimum" and "maximum" y depend on it, but it won't work in a real world app). For example, button frame may be draw on textView.frame or much below it. There is how i set map frame:
CGRect mapButtonFrame = self.mapButton.frame;
mapButtonFrame.origin.x = 30;
if (frame.size.height < 50){
mapButtonFrame.origin.y = (200+frame.size.height + 150);
} else if (frame.size.height >300){
mapButtonFrame.origin.y = (200+frame.size.height +50);
} else {
mapButtonFrame.origin.y = (200+frame.size.height +100);
}
self.mapButton.frame = mapButtonFrame;
Still, that approach is bad, please take a look at screenshots attached:
Here, as you can see, always different gap between 2 elements. How could i make it fixed?
By the way, auto layout is turned off.
Any advice would be appreciated, thanks1
This should work just fine:
[myTextView sizeToFit];//This will change the textView frame according to the text length
CGRect bFrame = self.mapButton.frame;
bFrame.origin.y = myTextView.frame.size.height + 20.0f;// The gap you want between the text and the button
self.mapButton.frame = bFrame;

custom uitableviewcell reset subview frame doesn't work as I wanted

I custom a uitableviewcell with nib, and want to custom the highlighted style. When the cell highlighted, I want to reduce the size of the subview of the cell and keep the position of the imageview in the cell not changing. So, it looks like the frame of a imageview zoomed out, but the image itself stay there not changing its position.
However, this code snippet doesn't work as I wanted.Could anybody help me to figure out where I am wrong. Any help will be appreciated!
tips: the imageview is a subview of self.frameView and frameView is a subview of frameBgView.
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
if (highlighted)
{
self.frameView.backgroundColor = UIColorFromRGBA(0, 0, 0, 0.1);
CGRect rect = self.frameBgView.frame;
rect.origin.x += 10;
rect.size.width -= 20;
self.frameBgView.frame = rect;
rect = self.frameView.frame;
rect.origin.x -= 10;
self.frameView.frame = rect;
}
else
{
....
}
}
EDIT: some screenshots to explain the question:
oky i tried your solution but i got like this i am posting the code as well as the output how it looks, if there is any problem just comment , i am deleted the frameBgView it is not required
code
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
if (highlighted)
{
self.frameView.backgroundColor = [UIColor grayColor];
CGRect rect = self.contentView.bounds;
rect.origin.x += 15;
rect.size.width -= 30;
//rect = self.frameView.frame;
// rect.origin.x -= 10;
self.frameView.frame = rect;
}
else
{
self.frameView.backgroundColor = [UIColor whiteColor];
CGRect rect = self.contentView.bounds;
rect.origin.x += 10;
rect.size.width -= 20;
self.frameView.frame = rect;
}
}
and i am not using auto layout .. the result what i got is, before
and after highlighted,
EDIT:
this is the cell structure
as u can see in the picture, in content view i hav added frameView which is blue in colour, and within frameView i hav added imageView and also don't forget t set content mode scale to fill and also auto resizing masks for both frameView and imageView for example
autoresizing masks for content view
autoresizing masks for frameView
autoresizing masks for imageView
END EDIT

UIScrollview imbeded within UIPageviewController resizes after page turned

So I have a UIPageviewController used that contains various news articles. The articles are presented on UIWebviews of varying lengths, and each UIWebview is contained within a UIScrollview. When the first article is loaded, everything works, and the user is able to scroll up and down the page and read the entire article. However, when the user swipes to the left or right to view the next article, the UIScrollview on that page is resized to only be the height of the screen and thus will only scroll enought to bounce back. I can tell that the UIWebview is longer than what is being shown because I have printed it's height, and when scrolling, you can see that the article continues. Also, when you swipe back to the initial article View, this UIScrollview is also resized to only be the screen size.
The UIWebView has it's content's loaded in the ArticleViewController's init method, and then is sized within the webViewDidLoad method like this:
CGRect frame = aWebView.frame;
frame.size.height = 1;
aWebView.frame = frame;
CGSize fittingSize = [aWebView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
aWebView.frame = frame;
This seems to work, although tell me if you think I should change it.
To get to each article, you click on it from a UITableview presenting each of the articles. Within the DidSelectRowAtIndexPath method for this, I set up the UIPageViewController like so:
UIPageViewController *articlesPageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
articlesPageViewController.dataSource = self;
articlesPageViewController.delegate = self;
NVM! The Solution was turning off auto layout for the UIScrollview! Thanks everyone!
(void)webViewDidFinishLoad:(UIWebView *)aWebView {
CGRect frame = aWebView.frame;
frame.size.height = 1;
aWebView.frame = frame;
CGSize fittingSize = [aWebView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
aWebView.frame = frame; NSLog(#"size: %f, %f", fittingSize.width, fittingSize.height);
}
Get height of the webview then set the content size of the scrollview
OR try this way:
CGFloat height = [[myWebView stringByEvaluatingJavaScriptFromString:#"document.height"] floatValue];
CGFloat width = [[myWebView stringByEvaluatingJavaScriptFromString:#"document.width"] floatValue];
CGRect frame = myWebView.frame;
frame.size.height = height + 125.0;
frame.size.width = width;
myWebView.frame = frame;
mainScrollView.contentSize = myWebView.bounds.size;

Resources